Scopri come utilizzare l'API Instant Placement nelle tue app.
Prerequisiti
Prima di procedere, assicurati di conoscere i concetti fondamentali dell'AR e come configurare una sessione ARCore.
Configurare una nuova sessione con il posizionamento istantaneo
In una nuova sessione ARCore, attiva la modalità di posizionamento istantaneo.
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)
}
Posiziona un oggetto
Utilizza Frame.hitTestInstantPlacement()
per creare un punto di posizionamento istantaneo monitorabile in base alla posizione del tocco sullo schermo.
Recupera la posa attuale con il metodo 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()
}
}
}
Il posizionamento istantaneo supporta
monitoraggio dello spazio sullo schermo con distanza approssimativa,
passaggio automatico al monitoraggio completo una volta che il punto di posizionamento istantaneo
ancorate al mondo reale. Recupera la posa attuale con
getPose()
Ottieni il metodo di monitoraggio corrente con
getTrackingMethod()
Sebbene ARCore possa eseguire test di hit di posizionamento istantaneo su superfici di qualsiasi orientamento, i risultati degli hit restituiranno sempre una posa con +Y verso l'alto, contro la direzione della gravità. Sulle superfici orizzontali, i risultati dei test degli hit sono accurati posizioni molto più velocemente.
Semplifica la transizione del metodo di monitoraggio
Quando la profondità true diventa disponibile, ARCore cambia il metodo di monitoraggio di InstantPlacementPoint
da
Da SCREENSPACE_WITH_APPROXIMATE_DISTANCE
a FULL_TRACKING
.
Anche la posa del punto cambierà per riflettere la profondità reale.
Di conseguenza, l'oggetto potrebbe apparire o ridursi improvvisamente.
Per evitare questo cambiamento improvviso, aggiungi un 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
)
Quindi, aggiungi quanto segue alla tua attività.
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)
)
Aumenta l'efficienza dopo il posizionamento degli oggetti
Disattiva il posizionamento istantaneo quando l'oggetto è posizionato correttamente per salvare i cicli della CPU e potenza.
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)
}