Saiba como usar a API Instant Placement nos seus próprios apps.
Pré-requisitos
Entenda os conceitos básicos de RA e como configurar uma sessão do ARCore antes de continuar.
Configurar uma nova sessão com a posição instantânea
Em uma nova sessão do ARCore, ative o modo de posicionamento instantâneo.
Java
// Create the ARCore session.
public void createSession() {
session = new Session(applicationContext);
Config config = new Config(session);
// Set the Instant Placement mode.
config.setInstantPlacementMode(InstantPlacementMode.LOCAL_Y_UP);
session.configure(config);
}
Kotlin
// Create the ARCore session.
fun createSession() {
session = Session(applicationContext);
val config = Config(session)
// Set the Instant Placement mode.
config.instantPlacementMode = Config.InstantPlacementMode.LOCAL_Y_UP
session.configure(config)
}
Colocar um objeto
Usar Frame.hitTestInstantPlacement()
para criar um ponto de posicionamento instantâneo rastreável conforme uma posição de toque na tela.
Recupere a pose atual com o método getPose()
.
Java
private placementIsDone = false;
public void onDrawFrame(GL10 gl) {
Frame frame = session.update();
// Place an object on tap.
if (!placementIsDone && didUserTap()) {
// Use estimated distance from the user's device to the real world, based
// on expected user interaction and behavior.
float approximateDistanceMeters = 2.0f;
// Performs a ray cast given a screen tap position.
List<HitResult> results =
frame.hitTestInstantPlacement(tapX, tapY, approximateDistanceMeters);
if (!results.isEmpty()) {
InstantPlacementPoint point = (InstantPlacementPoint) results.get(0).getTrackable();
// Create an Anchor from the point's pose.
Anchor anchor = point.createAnchor(point.getPose());
placementIsDone = true;
disableInstantPlacement();
}
}
}
Kotlin
var placementIsDone = false;
fun onDrawFrame(gl: GL10) {
val frame = session.update();
// Place an object on tap.
if (!placementIsDone && didUserTap()) {
// Use estimated distance from the user's device to the real world, based
// on expected user interaction and behavior.
val approximateDistanceMeters = 2.0f;
// Performs a ray cast given a screen tap position.
val results = frame.hitTestInstantPlacement(tapX, tapY, approximateDistanceMeters)
if (results.isNotEmpty()) {
val point = results[0].trackable as InstantPlacementPoint
// Create an Anchor from the point's pose.
val anchor = point.createAnchor(point.pose)
placementIsDone = true
disableInstantPlacement()
}
}
}
A posição instantânea é compatível com
rastreamento do espaço da tela com distância aproximada,
automaticamente para o rastreamento completo quando o ponto de posicionamento instantâneo for
ancorados no mundo real. Recuperar a pose atual com
getPose()
Obtenha o método de acompanhamento atual com
getTrackingMethod()
Embora o ARCore possa realizar testes de hits de posicionamento instantâneo em superfícies de qualquer os resultados de hits sempre retornarão uma pose com +Y para cima, em relação direção da gravidade. Em superfícies horizontais, os testes de hit retornam precisão posições com muito mais rapidez.
Suavizar a transição do método de acompanhamento
Quando a profundidade real estiver disponível, o ARCore mudará o método de acompanhamento de um InstantPlacementPoint
de
SCREENSPACE_WITH_APPROXIMATE_DISTANCE
para FULL_TRACKING
.
A posição do ponto também será alterada para refletir a profundidade real.
Isso pode fazer com que o objeto pareça aumentar ou diminuir repentinamente.
Para evitar essa mudança repentina, adicione um wrapper InstantPlacementPoint
.
Java
// Wrapper class to track state to reduce sudden visual changes in object size
class WrappedInstantPlacement {
public InstantPlacementPoint point;
public InstantPlacementPoint.TrackingMethod previousTrackingMethod;
public float previousDistanceToCamera;
public float scaleFactor = 1.0f;
public WrappedInstantPlacement(
InstantPlacementPoint point,
InstantPlacementPoint.TrackingMethod previousTrackingMethod,
float previousDistanceToCamera) {
this.point = point;
this.previousTrackingMethod = previousTrackingMethod;
this.previousDistanceToCamera = previousDistanceToCamera;
}
}
Kotlin
// Wrapper class to track state to reduce sudden visual changes in object size
class WrappedInstantPlacement(
var point: InstantPlacementPoint,
var previousTrackingMethod: InstantPlacementPoint.TrackingMethod,
var previousDistanceToCamera: Float,
var scaleFactor: Float = 1.0f
)
Em seguida, adicione o seguinte à sua atividade.
Java
List<WrappedInstantPlacement> wrappedPoints = new ArrayList<>();
public void onDrawFrame(GL10 gl) {
Frame frame = session.update();
Camera camera = frame.getCamera();
// Place an object on tap.
if (didUserTap()) {
// Instant Placement should only be applied if no results are available through hitTest.
List<HitResult> results = frame.hitTest(tapX, tapY);
if (results.isEmpty()) {
// Use the estimated distance from the user's device to the closest
// available surface, based on expected user interaction and behavior.
float approximateDistanceMeters = 2.0f;
// Returns a single result if the hit test was successful.
results =
frame.hitTestInstantPlacement(tapX, tapY, approximateDistanceMeters);
if (!results.isEmpty()) {
// Instant placement was successful.
InstantPlacementPoint point = (InstantPlacementPoint) results.get(0).getTrackable();
wrappedPoints.add(new WrappedInstantPlacement(point, point.getTrackingMethod(),
distance(camera.getPose(), point.getPose())));
}
} else {
// results contain valid hit tests which can be used directly, so instant placement is not required.
}
}
for (WrappedInstantPlacement wrappedPoint : wrappedPoints) {
InstantPlacementPoint point = wrappedPoint.point;
if (point.getTrackingState() == TrackingState.STOPPED) {
wrappedPoints.remove(wrappedPoint);
continue;
}
if (point.getTrackingState() == TrackingState.PAUSED) {
continue;
}
if (point.getTrackingMethod() == TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE) {
// Continue to use the estimated depth and pose. Record the distance to
// the camera for use in the next frame if the transition to full
// tracking happens.
wrappedPoint.previousDistanceToCamera = distance(point.getPose(), camera.getPose());
}
else if (point.getTrackingMethod() == TrackingMethod.FULL_TRACKING) {
if (wrappedPoint.previousTrackingMethod == TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE) {
// Change from the estimated pose to the accurate pose. Adjust the
// object scale to counteract the apparent change due to pose jump.
wrappedPoint.scaleFactor = distance(point.getPose(), camera.getPose()) /
wrappedPoint.previousDistanceToCamera;
// Apply the scale factor to the model.
// ...
wrappedPoint.previousTrackingMethod = TrackingMethod.FULL_TRACKING;
}
}
}
}
float distance(Pose p, Pose q) {
return Math.sqrt(Math.pow(p.tx() - q.tx(), 2) + Math.pow(p.ty() - q.ty(), 2) + Math.pow(p.tz() - q.tz(), 2));
}
Kotlin
var wrappedPoints = mutableListOf<WrappedInstantPlacement>()
fun onDrawFrame(gl: GL10?) {
val frame = session.update()
val camera = frame.camera
// Place an object on tap.
if (didUserTap()) {
// Instant Placement should only be applied if no results are available through hitTest.
var results = frame.hitTest(tapX, tapY);
if (results.isEmpty()) {
// Use the estimated distance from the user's device to the closest
// available surface, based on expected user interaction and behavior.
val approximateDistanceMeters = 2.0f;
// Returns a single result if the hit test was successful.
results = frame.hitTestInstantPlacement(tapX, tapY, approximateDistanceMeters);
if (results.isNotEmpty()) {
// Instant placement was successful.
val point = results[0].trackable as InstantPlacementPoint
val wrapped = WrappedInstantPlacement(point, point.trackingMethod, point.pose.distance(camera.pose))
wrappedPoints.add(wrapped)
}
} else {
// Results contain valid hit tests which can be used directly, so Instant Placement
// is not required.
}
}
loop@ for (wrappedPoint in wrappedPoints) {
val point = wrappedPoint.point
when {
point.trackingState == TrackingState.STOPPED -> {
wrappedPoints.remove(wrappedPoint)
continue@loop
}
point.trackingState == TrackingState.PAUSED -> continue@loop
point.trackingMethod == TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE -> {
// Continue to use the estimated depth and pose. Record the distance to
// the camera for use in the next frame if the transition to full
// tracking happens.
wrappedPoint.previousDistanceToCamera = point.pose.distance(camera.pose)
}
wrappedPoint.previousTrackingMethod == TrackingMethod.SCREENSPACE_WITH_APPROXIMATE_DISTANCE &&
point.trackingMethod == TrackingMethod.FULL_TRACKING -> {
// Change from the estimated pose to the accurate pose. Adjust the
// object scale to counteract the apparent change due to pose jump.
wrappedPoint.scaleFactor =
point.pose.distance(camera.pose) / wrappedPoint.previousDistanceToCamera
// Apply the scale factor to the model.
// ...
wrappedPoint.previousTrackingMethod = TrackingMethod.FULL_TRACKING
}
}
}
}
fun Pose.distance(other: Pose) = sqrt(
(tx() - other.tx()).pow(2.0f) + (ty() - other.ty()).pow(2.0f) + (tz() - other.tz()).pow(2.0f)
)
Aumentar a eficiência após a colocação do objeto
Desativar a posição instantânea quando o objeto estiver posicionado corretamente para economizar ciclos de CPU e energia.
Java
void disableInstantPlacement() {
Config config = new Config(session);
config.setInstantPlacementMode(Config.InstantPlacementMode.DISABLED);
session.configure(config);
}
Kotlin
fun disableInstantPlacement() {
val config = Config(session)
// Set the Instant Placement mode.
config.instantPlacementMode = Config.InstantPlacementMode.DISABLED
session.configure(config)
}