คู่มือนักพัฒนาซอฟต์แวร์เกี่ยวกับตำแหน่งแบบทันทีสำหรับ Android

ดูวิธีใช้ API ตำแหน่งโฆษณาทันที ในแอปของคุณเอง

ข้อกำหนดเบื้องต้น

ตรวจสอบว่าคุณเข้าใจแนวคิด AR พื้นฐาน และวิธีกําหนดค่าเซสชัน ARCore ก่อนดำเนินการต่อ

กำหนดค่าเซสชันใหม่ด้วยตำแหน่งโฆษณาทันใจ

ในเซสชัน ARCore ใหม่ ให้เปิดใช้โหมดการวางตำแหน่งทันที

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

วางวัตถุ

ใช้ Frame.hitTestInstantPlacement() เพื่อสร้างจุดตำแหน่งทันใจที่ติดตามได้ตามตำแหน่งการแตะหน้าจอ เรียกท่าทางปัจจุบันด้วยเมธอด 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
()
   
}
 
}
}

การวางโฆษณาทันทีรองรับการติดตามพื้นที่บนหน้าจอโดยประมาณ โดยระบบจะเปลี่ยนเป็นการติดตามแบบเต็มโดยอัตโนมัติเมื่อจุดการวางโฆษณาทันทียึดอยู่ในโลกแห่งความเป็นจริง เรียกท่าทางปัจจุบันด้วย getPose() รับวิธีการติดตามปัจจุบันด้วย getTrackingMethod()

แม้ว่า ARCore สามารถทำการทดสอบ Hit สำหรับตำแหน่งด่วนกับพื้นผิว การวางแนว ผลลัพธ์ของ Hit จะแสดงท่าทางที่มี +Y ขึ้นเสมอ ทิศทางของแรงโน้มถ่วง บนพื้นผิวแนวนอน การทดสอบ Hit จะให้ผลที่แม่นยำ ได้เร็วขึ้นมาก

ทำให้การเปลี่ยนไปใช้วิธีการติดตามราบรื่น

เมื่อมีความลึกที่แท้จริง ARCore จะเปลี่ยนวิธีการติดตามของ InstantPlacementPoint จาก SCREENSPACE_WITH_APPROXIMATE_DISTANCE ถึง FULL_TRACKING ท่าทางของจุดจะเปลี่ยนด้วยเพื่อแสดงถึงความลึกที่แท้จริง ซึ่งอาจทำให้วัตถุขยายหรือหดสั้นลงอย่างกะทันหัน เพิ่ม InstantPlacementPoint Wrapper เพื่อหลีกเลี่ยงการเปลี่ยนแปลงอย่างกะทันหันนี้

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
)

จากนั้นเพิ่มข้อมูลต่อไปนี้ลงในกิจกรรม

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)
)

เพิ่มประสิทธิภาพหลังจากการวางออบเจ็กต์

ปิดใช้ตำแหน่งทันทีเมื่อวางออบเจ็กต์อย่างถูกต้องเพื่อบันทึกรอบ CPU และประสิทธิภาพ

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