คู่มือนักพัฒนาซอฟต์แวร์การกำหนดตำแหน่งทันใจสำหรับ Android NDK

เรียนรู้วิธีใช้ API ตำแหน่งทันทีในแอปของคุณเอง

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

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

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

ในเซสชัน ARCore ใหม่ ให้เปิดใช้โหมดตำแหน่งโฆษณาทันใจ

// Create a session config.
ArConfig* ar_config = NULL;
ArConfig_create(ar_session, &ar_config);

// Enable Instant Placement mode.
ArConfig_setInstantPlacementMode(ar_session, ar_config,
                                 AR_INSTANT_PLACEMENT_MODE_LOCAL_Y_UP);
CHECK(ArSession_configure(ar_session, ar_config) == AR_SUCCESS);

// Release config resources.
ArConfig_destroy(ar_config);

วางวัตถุ

ในเซสชัน ARCore ใหม่ ให้ทำการทดสอบ Hit ของตำแหน่งทันใจด้วย ArFrame_hitTestInstantPlacement จากนั้นสร้าง ArAnchor ใหม่โดยใช้ท่าทาง ArInstantPlacementPoint จากผลลัพธ์ของ Hit ARTrackable

ArFrame* ar_frame = NULL;
if (ArSession_update(ar_session, ar_frame) != AR_SUCCESS) {
  // Get the latest frame.
  LOGE("ArSession_update error");
  return;
}

// Place an object on tap.
// Use the estimated distance from the user's device to the closest
// available surface, based on expected user interaction and behavior.
float approximate_distance_meters = 2.0f;

ArHitResultList* hit_result_list = NULL;
ArHitResultList_create(ar_session, &hit_result_list);
CHECK(hit_result_list);

// Returns a single result if the hit test was successful.
ArFrame_hitTestInstantPlacement(ar_session, ar_frame, x, y,
                                approximate_distance_meters, hit_result_list);

int32_t hit_result_list_size = 0;
ArHitResultList_getSize(ar_session, hit_result_list, &hit_result_list_size);
if (hit_result_list_size > 0) {
  ArHitResult* ar_hit_result = NULL;
  ArHitResult_create(ar_session, &ar_hit_result);
  CHECK(ar_hit_result);
  ArHitResultList_getItem(ar_session, hit_result_list, 0, ar_hit_result);
  if (ar_hit_result == NULL) {
    LOGE("ArHitResultList_getItem error");
    return;
  }

  ArTrackable* ar_trackable = NULL;
  ArHitResult_acquireTrackable(ar_session, ar_hit_result, &ar_trackable);
  if (ar_trackable == NULL) {
    LOGE("ArHitResultList_acquireTrackable error");
    return;
  }
  ArTrackableType ar_trackable_type = AR_TRACKABLE_NOT_VALID;
  ArTrackable_getType(ar_session, ar_trackable, &ar_trackable_type);

  if (ar_trackable_type == AR_TRACKABLE_INSTANT_PLACEMENT_POINT) {
    ArInstantPlacementPoint* point = (ArInstantPlacementPoint*)ar_trackable;

    // Gets the pose of the Instant Placement point.
    ArPose* ar_pose = NULL;
    ArPose_create(ar_session, NULL, &ar_pose);
    CHECK(ar_pose);
    ArInstantPlacementPoint_getPose(ar_session, point, ar_pose);

    // Attaches an anchor to the Instant Placement point.
    ArAnchor* anchor = NULL;
    ArStatus status = ArTrackable_acquireNewAnchor(ar_session, ar_trackable,
                                                   ar_pose, &anchor);
    ArPose_destroy(ar_pose);
    // Render content at the anchor.
    // ...
  }

  ArTrackable_release(ar_trackable);
}

ตำแหน่งทันใจรองรับการติดตามพื้นที่หน้าจอด้วยระยะทางโดยประมาณ ซึ่งจะสลับไปเป็นการติดตามแบบเต็มโดยอัตโนมัติเมื่อมีการยึดตำแหน่งตำแหน่งทันทีในโลกจริง ดึงท่าทางปัจจุบันด้วย ArInstantPlacementPoint_getPose() รับวิธีการติดตามปัจจุบันด้วย ArInstantPlacementPoint_getTrackingMethod()

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

ตรวจสอบวิธีการติดตามจุดของตำแหน่งทันใจ

หาก ARCore มีท่าทาง 3 มิติที่แม่นยำสำหรับ ArInstantPlacementPoint ที่ ArFrame_hitTestInstantPlacement ส่งคืน ระบบจะเริ่มต้นด้วยวิธีการติดตาม AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING มิเช่นนั้น จะเริ่มใช้วิธีการติดตาม AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE แล้วเปลี่ยนไปใช้ AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING เมื่อ ARCore แสดงท่าทาง 3 มิติที่ถูกต้อง เมื่อวิธีการติดตามคือ AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING จะไม่เปลี่ยนกลับไปเป็น AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE

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

เมื่อวิธีการติดตามเปลี่ยนจาก AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE ในเฟรมหนึ่งเป็น AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING ในเฟรมถัดไป ท่าทางจะกระโดดจากตำแหน่งแรกเริ่มโดยอิงตามระยะทางโดยประมาณที่ให้มาไปยังตำแหน่งใหม่ซึ่งมีระยะทางที่แม่นยำ การเปลี่ยนแปลงท่าทางนี้จะเปลี่ยนแปลงขนาดที่เห็นได้ชัดของวัตถุที่ยึดอยู่กับ ArInstantPositionPoint กล่าวคือ จู่ๆ วัตถุที่ปรากฏ ใหญ่ขึ้นหรือเล็กลงกว่าในเฟรมก่อนหน้า

ทำตามขั้นตอนต่อไปนี้เพื่อหลีกเลี่ยงการเพิ่มขึ้นของภาพเนื่องจากการเปลี่ยนแปลงขนาดออบเจ็กต์ที่ปรากฏอย่างกะทันหัน

  1. ติดตามท่าทางและวิธีติดตามของ ArInstantPlacementPoint ในแต่ละเฟรม
  2. รอให้วิธีการติดตามเปลี่ยนเป็น AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING
  3. ใช้ท่าทางจากเฟรมก่อนหน้าและท่าทางในเฟรมปัจจุบันเพื่อระบุระยะห่างของวัตถุไปยังอุปกรณ์ในทั้ง 2 เฟรม
  4. คำนวณการเปลี่ยนแปลงที่เห็นได้ชัดของขนาดเนื่องจากระยะห่างจากกล้องที่เปลี่ยนแปลงไป
  5. ปรับขนาดของวัตถุเพื่อขจัดการเปลี่ยนแปลงขนาดที่รับรู้ได้ เพื่อไม่ให้วัตถุดูเหมือนเปลี่ยนขนาดภาพ
  6. (ไม่บังคับ) ปรับขนาดออบเจ็กต์กลับไปเป็นค่าเดิมในหลายเฟรม
class WrappedInstantPlacement {
  ArInstantPlacementPoint* point;
  ArInstantPlacementPointTrackingMethod previous_tracking_method;
  float previous_distance_to_camera;
  float scale_factor = 1.0f;

 public:
  WrappedInstantPlacement(ArInstantPlacementPoint* point,
                          TrackingMethod previous_tracking_method,
                          float previous_distance_to_camera) {
    this.point = point;
    this.previous_tracking_method = previous_tracking_method;
    this.previous_distance_to_camera = previous_distance_to_camera;
  }
};

std::vector<WrappedInstantPlacement> wrapped_points_;

หลังจากสร้างจุดตำแหน่งทันใจแล้ว ให้แก้ไข OnTouched() เพื่อรวมจุดดังกล่าว

if (ar_trackable_type == AR_TRACKABLE_INSTANT_PLACEMENT_POINT) {
  ArInstantPlacementPoint* point = (ArInstantPlacementPoint*)ar_trackable;
  ArInstantPlacementPointTrackingMethod tracking_method;
  ArInstantPlacementPoint_getTrackingMethod(ar_session, point,
                                            &tracking_method);
  ArCamera* ar_camera = nullptr;
  ArFrame_acquireCamera(ar_session, ar_frame, &ar_camera);
  CHECK(ar_camera);
  wrapped_points_.push_back(WrappedInstantPlacement(
      point, tracking_method, Distance(point, ar_camera)));
}

เมื่อวิธีการติดตามของจุดตำแหน่งทันทีเปลี่ยนจาก AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE ไปเป็น AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING ให้ใช้ระยะทางที่บันทึกไว้เพื่อรับมือกับการเปลี่ยนแปลงขนาดที่ปรากฏ

void OnUpdate() {
  for (auto& wrapped_point : wrapped_points_) {
    ArInstantPlacementPoint* point = wrapped_point.point;

    ArTrackingState tracking_state = AR_TRACKING_STATE_STOPPED;
    ArTrackable_getTrackingState(ar_session, (ArTrackable)point,
                                 &tracking_state);

    if (tracking_state == AR_TRACKING_STATE_STOPPED) {
      wrapped_points_.remove(wrapped_point);
      continue;
    }
    if (tracking_state == AR_TRACKING_STATE_PAUSED) {
      continue;
    }

    ArInstantPlacementPointTrackingMethod tracking_method;
    ArInstantPlacementPoint_getTrackingMethod(ar_session, point,
                                              &tracking_method);
    ArCamera* ar_camera = nullptr;
    ArFrame_acquireCamera(ar_session, ar_frame, &ar_camera);
    CHECK(ar_camera);
    const float distance_to_camera = Distance(point, ar_camera);
    if (tracking_method ==
        AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_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.
      wrapped_point.previous_distance_to_camera = distance_to_camera;
    } else if (
        tracking_method ==
            AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING &&
        wrapped_point.previous_tracking_method ==
            AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_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.
      wrapped_point.scale_factor =
          distance_to_camera / wrapped_point.previous_distance_to_camera;
      // Apply the scale factor to the model.
      // ...
      previous_tracking_method =
          AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING;
    }
  }
}

ข้อพิจารณาด้านประสิทธิภาพ

เมื่อเปิดใช้ตำแหน่งทันใจ ARCore จะใช้รอบ CPU เพิ่มเติม หากกังวลเรื่องประสิทธิภาพ ให้ลองปิดใช้ตำแหน่งโฆษณาทันทีหลังจากที่ผู้ใช้วางออบเจ็กต์เรียบร้อยแล้ว และวิธีการติดตามของจุดตำแหน่งทันใจทั้งหมดเปลี่ยนเป็น AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING

เมื่อปิดตำแหน่งทันใจ ให้ใช้ ArFrame_hitTest แทน ArFrame_hitTestInstantPlacement

ArConfig* ar_config = NULL;
ArConfig_create(ar_session, &ar_config);
// Disable Instant Placement.
ArConfig_setInstantPlacementMode(ar_session, ar_config,
                                 AR_INSTANT_PLACEMENT_MODE_DISABLED);
CHECK(ArSession_configure(ar_session, ar_config) == AR_SUCCESS);
ArConfig_destroy(ar_config);