ARCore 中文文档 下载 App

Depth API 深度数据获取与配置

Depth API 能做什么

Depth API 为每一帧摄像头画面生成一张深度图(Depth Map)——一张与 RGB 画面同分辨率(或较低分辨率)的灰度图,每个像素的值表示该像素点到手机相机的距离。

有了深度图,ARCore 应用就能做到:

Depth API 的三种模式

模式Session.Config.DepthMode说明设备要求
DISABLEDDISABLED不启用 Depth API(默认)无
HARDWAREAUTOMATIC(自动选择 HARDWARE 或 SOFTWARE)优先用硬件深度传感器(ToF)有 ToF 或双摄的设备
SOFTWARE自动降级没有硬件时用被动深度(RGB 双目立体匹配)所有 ARCore 设备

ToF vs 被动深度的区别

对比项ToF 硬件深度被动深度
工作原理发射红外光,测量飞行时间对比多帧 RGB 画面的视差
精度高(厘米级)较低(尤其远距离)
最小测量距离近远(近景容易失败)
刷新率高(可达 30fps+)取决于摄像头帧率
适用设备Pixel 4+、三星 S 系列旗舰等所有 ARCore 设备
能耗较高(需要发射红外)较高(需要大量计算)

Session 配置启用 Depth API

Android 原生

val session = Session(context)

// 检查设备是否支持
if (session.isDepthSupported()) {
    session.configure(
        Session.Config(context).apply {
            setDepthMode(Session.Config.DepthMode.AUTOMATIC)
            // 可选:设置深度图质量
            // setDepthTextureSize() 控制分辨率
        }
    )
} else {
    // 设备不支持,降级到平面检测
}

Unity AR Foundation

// 在 Unity 的 ARCoreLoaderSettings 中配置
// 或运行时:
var sessionSubsystem = XRGeneralSettings.Instance.Manager.activeLoader.GetSubsystem<XRSessionSubsystem>();
if (sessionSubsystem != null && sessionSubsystem is ARCoreSessionSubsystem arcoreSessionSubsystem)
{
    arcoreSessionSubsystem.SetDepthMode(ARCoreDepthMode.Automatic);
}

Unity 的 ARFoundation 在 4.0+ 版本原生支持 Depth API,AROcclusionManager 组件可以直接启用遮挡渲染。

获取深度图数据

Depth API 有两种数据访问方式,取决于你的场景:

方式一:Texture(推荐渲染用)

Depth API 把深度数据直接渲染成一个 OpenGL 纹理(Android)或 Metal 纹理(iOS),你可以直接传给着色器做遮挡渲染:

// 在每一帧 session.update() 之后
val frame = session.update()

// 深度纹理的句柄(OpenGL 纹理 ID)
val depthTextureId = frame.depthTexture

// 深度纹理的尺寸
val depthWidth = frame.depthWidth
val depthHeight = frame.depthHeight

// 深度图格式:通常是 GL_R32F(单通道 32 位浮点数,值 = 距离米数)

方式二:Image(计算/测量用)

如果你需要在 CPU 端读取深度值(比如做尺寸测量),用 Frame.acquireDepthImage():

// 获取深度 Image
val depthImage: Image = frame.acquireDepthImage()

// 深度 Image 的 format 通常是 YUV_420_888,Y 平面存储深度值
val planes = depthImage.planes
val plane0 = planes[0]  // Y 平面

// 读取某个像素的深度值
// 注意:深度图可能低于 RGB 分辨率,需要缩放坐标
val depthAtPixel = readDepthFromPlane(plane0, x, y)
// depthAtPixel 就是该像素到相机的距离(米)

// 用完必须释放(否则内存泄漏)
depthImage.close()

深度值的单位与约定

坐标系转换:深度图坐标 → 世界坐标

深度图的像素坐标 (u, v) 和 RGB 画面是对齐的(同一个像素对应同一个物理点),但分辨率可能不同(深度图分辨率通常比 RGB 低)。

从深度图像素获取对应世界坐标的流程:

// 1. 在 RGB 画面上有一个屏幕像素坐标 (screenX, screenY)
//    先映射到深度图坐标
val depthX = screenX * (depthWidth / rgbWidth.toFloat())
val depthY = screenY * (depthHeight / rgbHeight.toFloat())

// 2. 从深度图读取该点的距离
val depth = readDepthAt(depthX, depthY)
if (depth <= 0) return  // 无效深度

// 3. 用相机内参把 (depthX, depthY, depth) 反投影到相机坐标系
val cx = frame.camera.textureIntrinsics[0]  // principal point X
val cy = frame.camera.textureIntrinsics[1]  // principal point Y
val fx = frame.camera.textureIntrinsics[2]  // focal length X
val fy = frame.camera.textureIntrinsics[3]  // focal length Y

val cameraX = (depthX - cx) / fx * depth
val cameraY = (depthY - cy) / fy * depth
val cameraZ = depth

// 4. 相机坐标系 → 世界坐标系
val worldPoint = frame.camera.pose.transformPoint(Vector3(cameraX, cameraY, cameraZ))

Unity 中读取深度值

Unity AR Foundation 封装得更简单:

public class DepthReader : MonoBehaviour
{
    public AROcclusionManager occlusionManager;

    void Update()
    {
        if (occlusionManager.TryAcquireEnvironmentDepthCpuImage(out var cpuImage))
        {
            // cpuImage 就是深度图的 CPU 端访问
            // 每个像素值 = 距离(米)
            // 用完记得 Release
            cpuImage.Dispose();
        }

        // 或获取 GPU 端的纹理
        if (occlusionManager.TryAcquireEnvironmentDepthTexture(out var texture))
        {
            // texture 可以直接传给材质做遮挡
        }
    }
}

设备支持查询

// 在创建 Session 之前或之后都可以查
val arSession = Session(context)
if (!arSession.isDepthSupported()) {
    // 设备完全不支持 Depth API(极少数情况)
    // 只能用平面检测做遮挡的近似
}

// 或者查询具体哪种模式可用
val hardwareSupported = arSession.isDepthModeSupported(Session.Config.DepthMode.AUTOMATIC)

Depth API 并不是所有 ARCore 设备都支持——低端手机可能只支持基础的平面检测而没有 Depth。在设计功能时要考虑降级方案。