瞭解如何在您的應用程式中使用 Instant Placement 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 工作階段中,使用 ArFrame_hitTestInstantPlacement
執行即時刊登位置命中測試。然後使用命中結果 ARTrackable
中的 ArInstantPlacementPoint
姿勢建立新的 ArAnchor
。
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 可以針對任何方向的表面執行即時刊登位置命中測試,但命中結果一律會根據重力方向傳回 +Y 軸的姿勢。在水平途徑上,命中測試能夠更快傳回準確位置。
監控即時刊登位置點追蹤方法
如果 ARCore 針對 ArFrame_hitTestInstantPlacement
傳回的 ArInstantPlacementPoint
有準確的 3D 姿勢,會以追蹤方法 AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING
開頭。否則,會從追蹤方法 AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE
開始,並在 ARCore 有準確的 3D 姿勢後轉換為 AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING
。一旦追蹤方法變成 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
時,姿勢會根據所設的約略距離,前往新位置的準確距離。這項即時變更會改變錨定至 ArInstant PlacementPoint 的所有物件的明顯縮放比例。也就是說,物件看起來比在上一個影格中突然大或更小。
請按照下列步驟操作,避免因明顯物件比例變更而造成視覺跳轉:
- 追蹤每個影格的
ArInstantPlacementPoint
姿勢和追蹤方法。 - 等待追蹤方法變更為
AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING
。 - 使用前一個影格的姿勢和目前影格中的姿勢,判斷物件在兩個影格中的距離。
- 計算因距離攝影機距離變化造成的明顯變化。
- 調整物件縮放比例,因應感知到的縮放比例,避免物件看起來不會因為尺寸改變而改變。
- 視需要在多個影格將物件比例流暢地調整回原始值。
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);