Przewodnik dla programistów dotyczący błyskawicznego miejsca docelowego (Android)

Dowiedz się, jak korzystać z interfejsu Instant Places API. we własnych aplikacjach.

Wymagania wstępne

Upewnij się, że znasz podstawowe pojęcia związane z AR. i dowiedz się, jak skonfigurować sesję ARCore, zanim przejdziesz dalej.

Konfigurowanie nowej sesji z użyciem Szybkiego miejsca docelowego

W nowej sesji ARCore włącz tryb natychmiastowego miejsca docelowego.

JavaKotlin
// 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);
}
// 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)
}

Umieść obiekt

Użyj formatu Frame.hitTestInstantPlacement() aby utworzyć punkt Szybkiego miejsca docelowego z możliwością śledzenia w miejscu dotknięcia ekranu. Pobierz bieżącą pozycję za pomocą metody getPose().

JavaKotlin
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
();
   
}
 
}
}
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
()
   
}
 
}
}

Obsługa błyskawicznych miejsc docelowych śledzenie przestrzeni ekranu z przybliżoną odległością, automatyczne przełączenie na pełne śledzenie, gdy punkt Błyskawicznego miejsca docelowego w świecie rzeczywistym. Uzyskaj bieżącą pozycję dzięki getPose() Pobierz aktualną metodę śledzenia za pomocą getTrackingMethod()

Chociaż ARCore może przeprowadzać testy trafień błyskawicznego miejsca docelowego na platformach w orientacji strony, wyniki działań zawsze zwracają pozę ze znakiem +Y w górę, kierunku grawitacji. Na platformach poziomych testy trafień zwracają dokładne informacje może pojawiać się znacznie szybciej.

Płynniejsze przejście między metodami śledzenia

Gdy dostępna będzie rzeczywista głębia, ARCore zmieni metodę śledzenia InstantPlacementPoint z SCREENSPACE_WITH_APPROXIMATE_DISTANCE do FULL_TRACKING. Pozycja punktu zmieni się również, aby odzwierciedlić prawdziwą głębię. Może to spowodować, że obiekt nagle rośnie lub kurczy się. Aby uniknąć tej nagłej zmiany, dodaj kod InstantPlacementPoint.

JavaKotlin
// 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;
 
}
}
// 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
)

Następnie dodaj do aktywności poniższy element.

JavaKotlin
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));
}
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)
)

Zwiększenie wydajności po umieszczeniu obiektu

Wyłącz Szybkie umieszczanie, gdy obiekt jest prawidłowo umieszczony, by oszczędzać cykle pracy procesora i energii elektrycznej.

JavaKotlin
void disableInstantPlacement() {
 
Config config = new Config(session);
  config
.setInstantPlacementMode(Config.InstantPlacementMode.DISABLED);
  session
.configure(config);
}
fun disableInstantPlacement() {
 
val config = Config(session)
 
// Set the Instant Placement mode.
  config
.instantPlacementMode = Config.InstantPlacementMode.DISABLED
  session
.configure(config)
}