Android NDK용 인스턴트 게재위치 개발자 가이드

앱에서 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를 사용하여 인스턴트 게재위치 Hit Test를 실행합니다. 그런 다음 조회 결과의 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가 위로 향하는 포즈를 반환합니다. 가로 표면에서는 Hit Test가 정확한 위치를 훨씬 빠르게 반환합니다.

인스턴트 게재위치 포인트 추적 방법 모니터링

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로 변경되면 포즈는 제공된 대략적인 거리를 기반으로 초기 위치에서 정확한 거리에 있는 새 위치로 이동합니다. 이렇게 포즈를 즉각적으로 변경하면 ArInstantPlacementPoint에 고정된 객체의 시각적 배율이 변경됩니다. 즉, 객체가 이전 프레임보다 크거나 작게 보입니다.

뚜렷한 객체 배율의 갑작스러운 변경으로 인한 시각적 점프를 방지하려면 다음 단계를 따르세요.

  1. 각 프레임에서 ArInstantPlacementPoint의 포즈와 추적 메서드를 추적합니다.
  2. 추적 메서드가 AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING로 변경될 때까지 기다립니다.
  3. 이전 프레임의 포즈와 현재 프레임의 포즈를 사용하여 두 프레임에서 기기까지의 객체 거리를 확인합니다.
  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_hitTestInstantPlacement 대신 ArFrame_hitTest을 사용하세요.

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