Usar profundidade no app Android AR Foundation

A API Depth ajuda a câmera de um dispositivo a entender o tamanho e o formato dos objetos reais em uma cena. Ele usa a câmera para criar imagens ou mapas de profundidade, adicionando uma camada de realismo em RA aos apps. É possível usar as informações fornecidas por uma imagem de profundidade para fazer com que objetos virtuais apareçam com precisão na frente ou atrás de objetos do mundo real, permitindo experiências do usuário imersivas e realistas.

As informações de profundidade são calculadas com base no movimento e podem ser combinadas com as informações de um sensor de profundidade de hardware, como um sensor de tempo de voo (ToF), se disponível. Um dispositivo não precisa de um sensor ToF para oferecer suporte à API Depth.

Pré-requisitos

Entenda os conceitos fundamentais de RA. e como configurar uma sessão do ARCore antes de continuar.

Configurar o app como Depth Required ou Depth Optional (somente Android)

Se o app precisar de suporte para a API Depth: seja porque uma parte essencial da experiência de RA depende da profundidade ou porque não há substituto elegante para as partes do app que usam profundidade, você pode optar por restringir a distribuição do app na Google Play Store para dispositivos com suporte à API Depth.

Transforme seu app em Depth Required

Navegue para Edit > Project Settings > XR Plug-in Management > ARCore.

Depth é definido como Required por padrão.

Transforme seu app em Depth Optional

  1. Navegue para Edit > Project Settings > XR Plug-in Management > ARCore.

  2. No menu suspenso Depth, selecione Optional. para definir o Profundidade de um app como opcional.

Ativar profundidade

Para economizar recursos, o ARCore não ativa a API Depth por padrão. Para aproveitar a profundidade em dispositivos compatíveis, será preciso adicionar manualmente o AROcclusionManager para o objeto do jogo Câmera AR com os recursos Camera e Componente ARCameraBackground. Consulte Oclusão automática na documentação do Unity para mais informações.

Em uma nova sessão do ARCore, verifique se um o dispositivo do usuário oferece suporte a profundidade e à API Depth, da seguinte maneira:

// 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.
}

Obter imagens de profundidade

Obtenha a imagem de profundidade do ambiente mais recente do 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.
    }
}

Você pode converter a imagem bruta da CPU em um RawImage para ter mais flexibilidade. Um exemplo de como fazer isso pode ser encontrado nos exemplos de ARFoundation (link em inglês) do Unity.

Entender os valores de profundidade

Ponto A na geometria real observada e um ponto 2D a que representam o mesmo ponto na imagem de profundidade, o valor fornecido pelo atributo A API em a é igual ao comprimento de CA projetado no eixo principal. Também pode ser referida como a coordenada z de A em relação à câmera. C. Ao trabalhar com a API Depth, é importante entender que os valores de profundidade não são o comprimento da CA do raio, mas a projeção sobre ele.

Oculte objetos virtuais e visualize dados de profundidade

Confira a postagem do blog (link em inglês) do Unity. para ter uma visão geral dos dados de profundidade e saber como eles podem ser usados para ocultar de imagens virtuais. Além disso, a interface Amostras da ARFoundation (link em inglês) demonstrar a obstrução de imagens virtuais e a visualização de dados de profundidade.

Você pode renderizar a oclusão usando a renderização em duas etapas ou a renderização de passagem direta por objeto. A eficiência de cada abordagem depende da complexidade do cenário e de outras considerações específicas do app.

Renderização por objeto, de passagem direta

A renderização de passagem direta por objeto determina a oclusão de cada pixel do objeto no shader do Material Design. Se os pixels não estiverem visíveis, eles serão cortados, normalmente por meio da mistura Alfa, simulando a oclusão no dispositivo do usuário.

Renderização em duas etapas

Na renderização em duas etapas, a primeira passagem renderiza todo o conteúdo virtual em um buffer intermediário. O segundo passo combina a cena virtual ao plano de fundo com base na diferença entre a profundidade do mundo real e a profundidade da cena virtual. Essa abordagem não exige outro trabalho de shader específico ao objeto e geralmente produz resultados de aparência mais uniforme do que o método de passagem direta.

Extrair distância de uma imagem de profundidade

Para usar a API Depth para outros fins que não sejam a ocultação de objetos virtuais ou a visualização de dados de profundidade, extraia informações da imagem de profundidade.

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

O que vem em seguida?