Usa Depth en tu app de AR Foundation para Android

La API de Depth ayuda a la cámara de un dispositivo a comprender el tamaño y la forma de los objetos reales de una escena. Usa la cámara para crear imágenes de profundidad (o mapas de profundidad), lo que agrega una capa de realismo de RA a tus apps. Puedes usar la información proporcionada por una imagen de profundidad para hacer que los objetos virtuales aparezcan con precisión delante o detrás de objetos del mundo real, lo que permite experiencias del usuario envolventes y realistas.

La información de profundidad se calcula a partir del movimiento y se puede combinar con información de un sensor de profundidad de hardware, como un sensor de tiempo de vuelo (ToF), si está disponible. Un dispositivo no necesita un sensor ToF para admitir la API de Depth.

Requisitos previos

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

Configura tu app para que sea Depth Required o Depth Optional (solo para Android).

Si tu app requiere compatibilidad con la API de Depth, ya sea porque una parte central de la experiencia de RA se basa en la profundidad o porque no hay un resguardo ordenado para las partes de la app que la usan, puedes optar por restringir la distribución de tu app en Google Play Store a dispositivos compatibles con la API de Depth.

Establecer que tu app sea Depth Required

Navega a Edit > Project Settings > XR Plug-in Management > ARCore.

Depth se establece en Required de forma predeterminada.

Establecer que tu app sea Depth Optional

  1. Navega a Edit > Project Settings > XR Plug-in Management > ARCore.

  2. En el menú desplegable Depth, selecciona Optional para configurar una app en Depth opcional.

Habilitar profundidad

Para guardar recursos, ARCore no habilita la API de Depth de forma predeterminada. Para aprovechar la profundidad en los dispositivos compatibles, debes agregar manualmente el componente AROcclusionManager al objeto de juego Cámara de RA con los componentes Camera y ARCameraBackground. Consulta Oclusión automática en la documentación de Unity para obtener más información.

En una nueva sesión de ARCore, verifica si el dispositivo de un usuario admite profundidad y la API de Depth de la siguiente manera:

// Reference to AROcclusionManager that should be added to the AR Camera
// game object that contains the Camera and ARCameraBackground components.
var occlusionManager = …

// Check whether the user's device supports the Depth API.
if (occlusionManager.descriptor?.supportsEnvironmentDepthImage)
{
    // If depth mode is available on the user's device, perform
    // the steps you want here.
}

Adquiere imágenes de profundidad

Obtén la imagen de profundidad del entorno más reciente de AROcclusionManager.

// Reference to AROcclusionManager that should be added to the AR Camera
// game object that contains the Camera and ARCameraBackground components.
var occlusionManager = …

if (occlusionManager.TryAcquireEnvironmentDepthCpuImage(out XRCpuImage image))
{
    using (image)
    {
        // Use the texture.
    }
}

Puedes convertir la imagen de CPU sin procesar en un RawImage para mayor flexibilidad. Puedes encontrar un ejemplo de cómo hacerlo en las muestras de ARFoundation de Unity.

Comprende los valores de profundidad

Dado el punto A de la geometría real observada y un punto a en 2D que representa el mismo punto en la imagen de profundidad, el valor dado por la API de Depth en a es igual a la longitud de CA proyectada en el eje principal. Esto también puede denominarse coordenada z de A en relación con el origen de la cámara C. Cuando trabajas con la API de Depth, es importante comprender que los valores de profundidad no son la longitud del CA del rayo, sino la proyección de este.

Ocluye objetos virtuales y visualiza datos de profundidad

Consulta la entrada de blog de Unity a fin de obtener una descripción general de alto nivel de los datos de profundidad y cómo se pueden usar para ocluir imágenes virtuales. Además, los ejemplos de ARFoundation de Unity demuestran la oclusión de imágenes virtuales y la visualización de datos de profundidad.

Puedes renderizar la oclusión mediante la renderización de dos pases o la renderización de pase hacia delante por objeto. La eficiencia de cada enfoque depende de la complejidad de la escena y otras consideraciones específicas de la app.

Renderización de pase hacia delante por objeto

La renderización de avance por objeto determina la oclusión de cada píxel del objeto en su sombreador de material. Si los píxeles no son visibles, se recortan, por lo general, mediante la combinación alfa, simulando así la oclusión en el dispositivo del usuario.

Renderización de dos pases

Con la renderización de dos pases, el primer pase renderiza todo el contenido virtual en un búfer intermedio. En la segunda pasada, se combina la escena virtual con el fondo en función de la diferencia entre la profundidad del mundo real y la de la escena virtual. Este enfoque no requiere trabajo adicional de sombreador específico del objeto y, por lo general, produce resultados más uniformes que el método de avance.

Extrae la distancia de una imagen de profundidad

Si quieres usar la API de Depth para otros fines que no sean ocluir objetos virtuales o visualizar datos de profundidad, extrae información de la imagen de profundidad.

Texture2D _depthTexture;
short[] _depthArray;

void UpdateEnvironmentDepthImage()
{
  if (_occlusionManager &&
        _occlusionManager.TryAcquireEnvironmentDepthCpuImage(out XRCpuImage image))
    {
        using (image)
        {
            UpdateRawImage(ref _depthTexture, image, TextureFormat.R16);
            _depthWidth = image.width;
            _depthHeight = image.height;
        }
    }
  var byteBuffer = _depthTexture.GetRawTextureData();
  Buffer.BlockCopy(byteBuffer, 0, _depthArray, 0, byteBuffer.Length);
}

// Obtain the depth value in meters at a normalized screen point.
public static float GetDepthFromUV(Vector2 uv, short[] depthArray)
{
    int depthX = (int)(uv.x * (DepthWidth - 1));
    int depthY = (int)(uv.y * (DepthHeight - 1));

    return GetDepthFromXY(depthX, depthY, depthArray);
}

// Obtain the depth value in meters at the specified x, y location.
public static float GetDepthFromXY(int x, int y, short[] depthArray)
{
    if (!Initialized)
    {
        return InvalidDepthValue;
    }

    if (x >= DepthWidth || x < 0 || y >= DepthHeight || y < 0)
    {
        return InvalidDepthValue;
    }

    var depthIndex = (y * DepthWidth) + x;
    var depthInShort = depthArray[depthIndex];
    var depthInMeters = depthInShort * MillimeterToMeter;
    return depthInMeters;
}

¿Qué sigue?