Android NDK uygulamanızda Derinliği kullanma

Depth API, cihaz kamerasının bir sahnedeki gerçek nesnelerin boyutunu ve şeklini anlamasına yardımcı olur. Kamerayı kullanarak derinlikli görüntüler veya derinlik haritaları oluşturur. Böylece uygulamalarınıza artırılmış gerçeklik katmanı ekler. Sanal nesnelerin gerçek dünyadaki nesnelerin önünde veya arkasında doğru bir şekilde görünmesini sağlamak için derinlikli bir resmin sağladığı bilgileri kullanabilir, böylece sürükleyici ve gerçekçi bir kullanıcı deneyimi sağlayabilirsiniz.

Derinlik bilgileri hareketten hesaplanır ve varsa uçuş süresi (ToF) sensörü gibi bir donanım derinlik sensöründen gelen bilgilerle birleştirilebilir. Cihazların Depth API'yi desteklemek için ToF sensörü olması gerekmez.

Ön koşullar

Devam etmeden önce temel AR kavramlarını ve ARCore oturumunu nasıl yapılandıracağınızı anladığınızdan emin olun.

Derinliği destekleyen cihazlara erişimi kısıtlama

Uygulamanız, AR deneyiminin temel bir bölümü derinliğe dayalı olduğu veya uygulamanın derinlik kullanan bölümleri için sorunsuz bir yedek olmadığı için Depth API desteği gerektiriyorsa uygulamanızın Google Play Store'daki dağıtımını Depth API'yi destekleyen cihazlara aşağıdaki satırı ekleyerek kısıtlayabilirsiniz: Kılavuzda açıklanan ARCore değişikliklerinin yanı sıra AndroidManifest.xml'da açıklanan ARCore değişikliklerine ek olarak aşağıdaki satırı ekleyerek uygulamanızın Google Play Store'daAndroidManifest.xml

<uses-feature android:name="com.google.ar.core.depth" />

Derinliği Etkinleştir

Yeni bir ARCore oturumunda, kullanıcının cihazının Derinliği destekleyip desteklemediğini kontrol edin. ARCore uyumlu cihazların tümü, işlem gücü kısıtlamaları nedeniyle Depth API'yi desteklemez. Kaynakları kaydetmek için, derinlik ARCore'da varsayılan olarak devre dışı bırakılır. Uygulamanızın Depth API'yi kullanması için derinlik modunu etkinleştirin.

// Check whether the user's device supports the Depth API.
int32_t is_depth_supported = 0;
ArSession_isDepthModeSupported(ar_session, AR_DEPTH_MODE_AUTOMATIC,
                               &is_depth_supported);

// Configure the session for AR_DEPTH_MODE_AUTOMATIC.
ArConfig* ar_config = NULL;
ArConfig_create(ar_session, &ar_config);
if (is_depth_supported) {
  ArConfig_setDepthMode(ar_session, ar_config, AR_DEPTH_MODE_AUTOMATIC);
}
CHECK(ArSession_configure(ar_session, ar_config) == AR_SUCCESS);
ArConfig_destroy(ar_config);

Derinlikli resimler edinme

Geçerli karenin derinlik resmini almak için ArFrame_acquireDepthImage16Bits() numaralı telefonu arayın.

// Retrieve the depth image for the current frame, if available.
ArImage* depth_image = NULL;
// If a depth image is available, use it here.
if (ArFrame_acquireDepthImage16Bits(ar_session, ar_frame, &depth_image) !=
    AR_SUCCESS) {
  // No depth image received for this frame.
  // This normally means that depth data is not available yet.
  // Depth data will not be available if there are no tracked
  // feature points. This can happen when there is no motion, or when the
  // camera loses its ability to track objects in the surrounding
  // environment.
  return;
}

Döndürülen görüntü, işlenmemiş görüntü arabelleğini sağlar. Bu arabelleği, oluşturulacak her bir oluşturulmuş nesnenin GPU'da kullanımı için bir parça gölgelendiriciye geçirilebilir. AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES hedeflenir ve ArFrame_transformCoordinates2d() çağrısı yapılarak AR_COORDINATES_2D_TEXTURE_NORMALIZED olarak değiştirilebilir. Derinlik resmine bir nesne gölgelendiriciden erişildiğinde, bu derinlik ölçümlerine kapama işlemi için doğrudan erişilebilir.

Derinlik değerlerini anlama

Gözlemlenen gerçek geometrideki A noktası ve derinlik resminde aynı noktayı temsil eden 2D bir nokta a verildiğinde a'de Derinlik API'si tarafından verilen değer, ana eksene yansıtılan CA uzunluğuna eşittir. Bu, C kamera kaynağına göre A öğesinin z koordinatı olarak da adlandırılabilir. Depth API ile çalışırken derinlik değerlerinin CA ışınının uzunluğu değil, onun projeksiyonu olduğunu anlamak önemlidir.

Gölgelendiricilerde derinlik kullanma

Geçerli çerçeve için derinlik bilgilerini ayrıştırma

Geçerli ekran konumuna ilişkin derinlik bilgilerine erişmek için bir parça gölgelendiricide DepthGetMillimeters() ve DepthGetVisibility() yardımcı işlevlerini kullanın. Daha sonra, oluşturulan nesnenin belirli bölümlerini seçerek kapatmak için bu bilgileri kullanın.

// Use DepthGetMillimeters() and DepthGetVisibility() to parse the depth image
// for a given pixel, and compare against the depth of the object to render.
float DepthGetMillimeters(in sampler2D depth_texture, in vec2 depth_uv) {
  // Depth is packed into the red and green components of its texture.
  // The texture is a normalized format, storing millimeters.
  vec3 packedDepthAndVisibility = texture2D(depth_texture, depth_uv).xyz;
  return dot(packedDepthAndVisibility.xy, vec2(255.0, 256.0 * 255.0));
}

// Return a value representing how visible or occluded a pixel is relative
// to the depth image. The range is 0.0 (not visible) to 1.0 (completely
// visible).
float DepthGetVisibility(in sampler2D depth_texture, in vec2 depth_uv,
                         in float asset_depth_mm) {
  float depth_mm = DepthGetMillimeters(depth_texture, depth_uv);

  // Instead of a hard Z-buffer test, allow the asset to fade into the
  // background along a 2 * kDepthTolerancePerMm * asset_depth_mm
  // range centered on the background depth.
  const float kDepthTolerancePerMm = 0.015f;
  float visibility_occlusion = clamp(0.5 * (depth_mm - asset_depth_mm) /
    (kDepthTolerancePerMm * asset_depth_mm) + 0.5, 0.0, 1.0);

 // Use visibility_depth_near to set the minimum depth value. If using
 // this value for occlusion, avoid setting it too close to zero. A depth value
 // of zero signifies that there is no depth data to be found.
  float visibility_depth_near = 1.0 - InverseLerp(
      depth_mm, /*min_depth_mm=*/150.0, /*max_depth_mm=*/200.0);

  // Use visibility_depth_far to set the maximum depth value. If the depth
  // value is too high (outside the range specified by visibility_depth_far),
  // the virtual object may get inaccurately occluded at further distances
  // due to too much noise.
  float visibility_depth_far = InverseLerp(
      depth_mm, /*min_depth_mm=*/7500.0, /*max_depth_mm=*/8000.0);

  const float kOcclusionAlpha = 0.0f;
  float visibility =
      max(max(visibility_occlusion, kOcclusionAlpha),
          max(visibility_depth_near, visibility_depth_far));

  return visibility;
}

Sanal nesneleri kapat

Parça gölgelendiricinin gövdesindeki sanal nesneleri gizle. Nesnenin alfa kanalını derinliğine göre güncelleyin. Bu işlem, gizlenmiş bir nesneyi oluşturur.

// Occlude virtual objects by updating the object’s alpha channel based on its depth.
const float kMetersToMillimeters = 1000.0;

float asset_depth_mm = v_ViewPosition.z * kMetersToMillimeters * -1.;

// Compute the texture coordinates to sample from the depth image.
vec2 depth_uvs = (u_DepthUvTransform * vec3(v_ScreenSpacePosition.xy, 1)).xy;

gl_FragColor.a *= DepthGetVisibility(u_DepthTexture, depth_uvs, asset_depth_mm);

İki geçişli oluşturma veya nesne başına, ileri geçiş oluşturma özelliğini kullanarak kapama işlemini oluşturabilirsiniz. Her yaklaşımın verimliliği, sahnenin karmaşıklığına ve uygulamaya özgü diğer önemli noktalara bağlıdır.

Nesne bazında, ileri geçiş oluşturma

Nesne bazında, ileriye doğru oluşturma, malzeme gölgelendiricisindeki nesnenin her bir pikselinin kapsanmasını belirler. Pikseller görünmüyorsa, genellikle alfa karıştırma kullanılarak kırpılarak kullanıcının cihazında kapama simülasyonu yapılır.

İki geçişli oluşturma

İki geçişli oluşturmada ilk geçiş, sanal içeriğin tamamını bir ara arabelleğe dönüştürür. İkinci geçişte, gerçek dünya derinliği ile sanal sahne derinliği arasındaki fark temel alınarak sanal sahneyi arka planla harmanlar. Bu yaklaşım, nesneye özgü ek bir gölgelendirici çalışması gerektirmez ve genellikle ileriye doğru geçiş yönteminden daha düzgün görünen sonuçlar üretir.

Kamera resimleri ile derinlik resimleri arasında koordinatları dönüştürme

ArFrame_acquireCameraImage() kullanılarak elde edilen resimler, derin resimlere kıyasla farklı bir en boy oranına sahip olabilir. Bu durumda derinlik resmi, kamera görüntüsünün bir kısmıdır ve kamera resmindeki tüm piksellere karşılık gelen geçerli bir derinlik tahmini yoktur.

CPU görüntüsü üzerindeki koordinatların derinlik resim koordinatlarını almak için:

const float cpu_image_coordinates[] = {(float)cpu_coordinate_x,
                                 (float)cpu_coordinate_y};
float texture_coordinates[2];
ArFrame_transformCoordinates2d(
    ar_session, ar_frame, AR_COORDINATES_2D_IMAGE_PIXELS, 1,
    cpu_image_coordinates, AR_COORDINATES_2D_TEXTURE_NORMALIZED,
    texture_coordinates);
if (texture_coordinates[0] < 0 || texture_coordinates[1] < 0) {
  // There are no valid depth coordinates, because the coordinates in the CPU
  // image are in the cropped area of the depth image.
} else {
  int depth_image_width = 0;
  ArImage_getWidth(ar_session, depth_image, &depth_image_width);
  int depth_image_height = 0;
  ArImage_getHeight(ar_session, depth_image, &depth_image_height);

  int depth_coordinate_x = (int)(texture_coordinates[0] * depth_image_width);
  int depth_coordinate_y = (int)(texture_coordinates[1] * depth_image_height);
}

Derinlik resim koordinatları için CPU resim koordinatlarını almak üzere:

int depth_image_width = 0;
ArImage_getWidth(ar_session, depth_image, &depth_image_width);
int depth_image_height = 0;
ArImage_getHeight(ar_session, depth_image, &depth_image_height);

float texture_coordinates[] = {
    (float)depth_coordinate_x / (float)depth_image_width,
    (float)depth_coordinate_y / (float)depth_image_height};
float cpu_image_coordinates[2];
ArFrame_transformCoordinates2d(
    ar_session, ar_frame, AR_COORDINATES_2D_TEXTURE_NORMALIZED, 1,
    texture_coordinates, AR_COORDINATES_2D_IMAGE_PIXELS,
    cpu_image_coordinates);

int cpu_image_coordinate_x = (int)cpu_image_coordinates[0];
int cpu_image_coordinate_y = (int)cpu_image_coordinates[1];

Derinlik vuruş testi

Çarpışma testleri, kullanıcıların nesneleri sahnede gerçek bir konuma yerleştirmesine olanak tanır. Daha önce, isabet testleri yalnızca algılanan düzlemlerde yürütülebiliyordu ve bu da, konumları yeşil Android'lerin gösterdiği sonuçlar gibi büyük, düz yüzeylerle sınırlıyordu. Derinlik vuruşu testleri, düz olmayan ve düşük dokulu yüzeylerde bile daha doğru isabet sonuçları sağlamak için hem düzgün hem de ham derinlik bilgisinden yararlanır. Bu simge kırmızı Android'lerle gösterilmektedir.

Derinlik özellikli isabet testlerini kullanmak için ArFrame_hitTest() çağrısı yapın ve döndürülen listede AR_TRACKABLE_DEPTH_POINT olup olmadığını kontrol edin.

// Create a hit test using the Depth API.
ArHitResultList* hit_result_list = NULL;
ArHitResultList_create(ar_session, &hit_result_list);
ArFrame_hitTest(ar_session, ar_frame, hit_x, hit_y, hit_result_list);

int32_t hit_result_list_size = 0;
ArHitResultList_getSize(ar_session, hit_result_list, &hit_result_list_size);

ArHitResult* ar_hit_result = NULL;
for (int32_t i = 0; i < hit_result_list_size; ++i) {
  ArHitResult* ar_hit = NULL;
  ArHitResult_create(ar_session, &ar_hit);
  ArHitResultList_getItem(ar_session, hit_result_list, i, ar_hit);

  ArTrackable* ar_trackable = NULL;
  ArHitResult_acquireTrackable(ar_session, ar_hit, &ar_trackable);
  ArTrackableType ar_trackable_type = AR_TRACKABLE_NOT_VALID;
  ArTrackable_getType(ar_session, ar_trackable, &ar_trackable_type);
  // Creates an anchor if a plane or an oriented point was hit.
  if (AR_TRACKABLE_DEPTH_POINT == ar_trackable_type) {
    // Do something with the hit result.
  }
  ArTrackable_release(ar_trackable);
  ArHitResult_destroy(ar_hit);
}

ArHitResultList_destroy(hit_result_list);

Sonraki adımlar