Guía para desarrolladores sobre la Posición instantánea en el NDK de Android

Obtén información sobre cómo usar la API de Instant Placement en tus propias apps.

Requisitos previos

Asegúrate de comprender los conceptos fundamentales de RA y cómo configurar una sesión de ARCore antes de continuar.

Configurar una nueva sesión con la Posición instantánea

En una nueva sesión de ARCore, habilita el modo de posición instantánea.

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

Coloca un objeto

En una nueva sesión de ARCore, realiza una prueba de posicionamiento instantáneo de posiciones con ArFrame_hitTestInstantPlacement. Luego, crea una nueva ArAnchor con la pose ArInstantPlacementPoint del ARTrackable del resultado del hit.

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

La Posición instantánea admite el seguimiento del espacio de pantalla con distancia aproximada y cambia automáticamente al seguimiento completo una vez que el punto de Posición instantánea se ancla en el mundo real. Recupera la pose actual con ArInstantPlacementPoint_getPose(). Obtén el método de seguimiento actual con ArInstantPlacementPoint_getTrackingMethod().

Si bien ARCore puede realizar pruebas de posicionamiento de posición instantánea en superficies con cualquier orientación, los resultados de hits siempre mostrarán una pose con +Y hacia arriba, en dirección de la gravedad. En superficies horizontales, las pruebas de posicionamiento muestran posiciones precisas mucho más rápido.

Supervisar el método de seguimiento de puntos de Instant Placement

Si ARCore tiene una pose 3D precisa para el ArInstantPlacementPoint que muestra ArFrame_hitTestInstantPlacement, comienza con el método de seguimiento AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING. De lo contrario, comenzará con el método de seguimiento AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE y pasará a AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING una vez que ARCore tenga una pose 3D precisa. Una vez que el método de seguimiento sea AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING, no se revertirá a AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE.

Suavizar la transición del método de seguimiento

Cuando el método de seguimiento cambia de AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE en un fotograma a AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING en el siguiente, la posición salta de su ubicación inicial en función de la distancia aproximada proporcionada a una nueva ubicación a una distancia precisa. Este cambio instantáneo en la pose modifica la escala aparente de los objetos que están anclados al ArInstantPlacementPoint. Es decir, un objeto aparece de repente más grande o más pequeño de lo que era en el fotograma anterior.

Sigue estos pasos para evitar el salto visual debido al cambio repentino en la escala aparente de los objetos:

  1. Mantén un registro de la postura y del método de seguimiento de ArInstantPlacementPoint en cada fotograma.
  2. Espera a que el método de seguimiento cambie a AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING.
  3. Usa la pose del fotograma anterior y la del fotograma actual para determinar la distancia del objeto al dispositivo en ambos fotogramas.
  4. Calcula el cambio aparente en la escala debido al cambio en la distancia respecto de la cámara.
  5. Ajusta la escala del objeto para contrarrestar el cambio percibido en la escala, de modo que el objeto no parezca cambiar visualmente de tamaño.
  6. De manera opcional, vuelve a ajustar sin problemas la escala del objeto a su valor original en varios fotogramas.
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_;

Después de crear el punto de Posición instantánea, modifica OnTouched() para unirlo.

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

Cuando el método de seguimiento del punto de Posición instantánea pase de AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_SCREENSPACE_WITH_APPROXIMATE_DISTANCE a AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING, usa la distancia guardada para contrarrestar el cambio de escala aparente.

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

Consideraciones de rendimiento

Cuando se habilita la Posición instantánea, ARCore consume ciclos de CPU adicionales. Si te preocupa el rendimiento, considera inhabilitar la Posición instantánea después de que el usuario haya colocado correctamente su objeto y de que el método de seguimiento de todos los puntos de la función haya cambiado a AR_INSTANT_PLACEMENT_POINT_TRACKING_METHOD_FULL_TRACKING.

Cuando la Posición instantánea esté inhabilitada, usa ArFrame_hitTest en lugar de 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);