在 AR Foundation Android 应用中使用“深度”功能

Depth API 可帮助设备的相机了解场景中真实对象的大小和形状。它使用相机来创建深度图像或深度图,从而为您的应用增添一层 AR 真实感。您可以使用深度图像提供的信息让虚拟对象准确显示在真实对象的前方或后方,从而实现沉浸式真实用户体验。

深度信息通过运动计算,可与来自硬件深度传感器(例如飞行时间 (ToF) 传感器(如有))的信息结合使用。设备不需要 ToF 传感器即可支持 Depth API

前提条件

确保您了解 AR 基础概念 以及如何在继续之前配置 ARCore 现场录像

将应用配置为 Depth RequiredDepth Optional(仅限 Android)

如果您的应用需要 Depth API 支持, 要么是因为 AR 体验的核心部分依赖于深度,或者是因为 应用中使用深度的部分没有优雅的回退机制,您可能 选择限制您的应用在 Google Play 商店中的分发范围, 支持 Depth API 的设备

将应用设为Depth Required

导航到 Edit > Project Settings > XR Plug-in Management > ARCore

Depth 默认设置为 Required

将应用设为Depth Optional

  1. 导航到 Edit > Project Settings > XR Plug-in Management > ARCore

  2. Depth 下拉菜单中选择 Optional 将应用设为可选深度。

启用景深数据

为节省资源,ARCore 默认不会启用 Depth API。拍摄 以充分利用支持设备上的深度,您必须手动添加 AROcclusionManager 组件添加到 AR 相机游戏对象中,并使用 CameraARCameraBackground 组件。请参阅 自动遮挡

新的 ARCore 会话中,检查 用户的设备支持深度和 Depth API,如下所示:

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

获取深度图像

从 Google Cloud 控制台获取最新的环境深度图像: 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.
    }
}

您可以将原始 CPU 映像转换为 RawImage, 以获得更大的灵活性。一个 您可以在 Unity 的 ARFoundation 示例中找到有关如何执行此操作的示例。

了解深度值

在观测到的真实几何图形上给定点 A 和 2D 点 a 深度图像中的相同点,深度值 a 处的 API 等于投影到主轴上的 CA 的长度。 这也可称为 A 相对于镜头的 z 坐标 来源:C。使用 Depth API 时,请务必了解 深度值不是光线 CA 本身的长度,而是投影

遮盖虚拟对象并直观呈现深度数据

查看 Unity 的博文 简要了解深度数据以及如何使用这些数据进行遮挡 虚拟映像此外,Unity 的 ARFoundation 示例 演示了遮挡虚拟图像和可视化深度数据。

您可以使用双通道渲染或按对象正向渲染来渲染遮挡。每种方法的效率取决于场景的复杂性和其他特定于应用的注意事项。

按对象正向渲染

按对象正向渲染可确定对象在其材料着色器中每个像素的遮挡。如果像素不可见,则通常会通过 alpha 混合来裁剪它们,从而模拟用户设备上的遮挡。

两步渲染

使用双通道渲染时,第一通道会将所有虚拟内容渲染到中间缓冲区。第二步则根据现实世界深度与虚拟场景深度之间的差异,将虚拟场景与背景混合。此方法不需要额外的特定于对象的着色器工作,并且通常比前向传播方法生成的结果更一致。

从深度图像中提取距离

如需将 Depth API 用于遮挡虚拟对象或直观呈现深度数据以外的目的,请从深度图像中提取信息。

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

后续步骤