深度感知
混合现实通过虚拟对象与现实的视觉及逻辑交互增强沉浸感,例如深度遮挡、粒子碰撞和三维扫描。
深度感知实时估计现实物体距离。底层可采用激光雷达或计算机视觉,质量与可靠性各不相同。
WebXR 按视图提供与颜色匹配的深度。浏览器可能使用 CPU 或 GPU 路径,性能不同。PlayCanvas 尽量抽象差异,例如两种路径均提供纹理。
平台可能只支持一种路径,也可能两者都支持。
请求深度时这样启动会话:
app.xr.start(camera, pc.XRTYPE_AR, pc.XRSPACE_LOCALFLOOR, {
depthSensing: { // request access to camera depth
usagePreference: pc.XRDEPTHSENSINGUSAGE_GPU, // prefer GPU implementation
dataFormatPreference: pc.XRDEPTHSENSINGFORMAT_F32 // prefer data as Float 32 array/texture
}
});
支持
检查深度支持:
if (app.xr.views.supportedDepth) {
// camera depth access is supported
}
app.xr.on('start', () => {
if (app.xr.views.availableDepth) {
// camera depth information is available
if (app.xr.views.depthGpuOptimized) {
// GPU path
} else {
// CPU path
}
}
});
距离测量
深度估计取决于底层 AR 系统,数据可能暂时不可用。
WebXR 仅在 CPU 路径支持直接测距。U、V 为从左到右、从上到下的 0 到 1 屏幕坐标:
// get monoscopic view (mobile screens)
const view = app.xr.views.get(pc.XREYE_NONE);
if (view) {
// get distance from the middle of a screen
const distance = view.getDepth(0.5, 0.5);
if (distance !== null) {
// distance is in meters
}
}
纹理
PlayCanvas 将 CPU/GPU 差异统一为深度纹理,双目头显等情况下可能为数组纹理。
访问方法:
const view = app.xr.views.list[0];
if (view) {
const texture = view.textureDepth;
if (texture) {
// get global uniform
const scopeDepthMap = app.graphicsDevice.scope.resolve('depthMap');
// set uniform
scopeDepthMap.setValue(texture);
}
}
双目视图
着色器需根据单目或双目使用不同采样方式,可通过 define 控制:
const view = app.xr.views.list[0];
if (view && view.eye !== pc.XREYE_NONE) {
// add define for stereo views
fragShader = '#define XRDEPTH_ARRAY\n' + fragShader;
}
数据格式
深度支持 F32,即 32 位浮点数组,以及 LA8,即成对 8 位值打包。分别提供 32 位与 16 位精度,16 位对近距离通常也已足够。
可根据格式在着色器中解包:
if (app.xr.views.depthPixelFormat === pc.PIXELFORMAT_R32F) {
fragShader = '#define XRDEPTH_FLOAT\n' + fragShader;
}
UV 归一化
纹理可能旋转、翻转,应使用提供的矩阵归一化,设置如下:
// get a global uniform scope
const scopeDepthUvMatrix = app.graphicsDevice.scope.resolve('matrix_depth_uv');
// set UV normalization matrix
scopeDepthUvMatrix.setValue(view.depthUvMatrix.data);
着色器
完成准备后,可在单个着色器覆盖单/双目和不同格式:
screen_size 与 view_index 是前向渲染器提供的内置 uniform,view_index 为 uint。
uniform vec4 screen_size; // provided by the engine
uniform mat4 matrix_depth_uv;
#ifdef XRDEPTH_ARRAY
uniform uint view_index; // provided by the engine
uniform highp sampler2DArray depthMap;
#else
uniform sampler2D depthMap;
#endif
void main (void) {
// construct UV for screen-space
vec2 uvScreen = gl_FragCoord.xy * screen_size.zw;
#ifdef XRDEPTH_ARRAY
// stereo
// modify screen-space based on view_index (left/right eye)
uvScreen = uvScreen * vec2(2.0, 1.0) - vec2(float(view_index), 0.0);
// normalize UV using provided matrix
vec2 uvNormalized = (matrix_depth_uv * vec4(uvScreen.xy, 0.0, 1.0)).xy;
// use view_index for array-texture index
vec3 uv = vec3(uvNormalized, float(view_index));
#else
// mono
// flip it vertically and normalize
vec2 uv = (matrix_depth_uv * vec4(uvScreen.x, 1.0 - uvScreen.y, 0.0, 1.0)).xy;
#endif
#ifdef XRDEPTH_FLOAT
// F32
float depth = texture2D(depthMap, uv).r;
#else
// LA8
vec2 packedDepth = texture2D(depthMap, uv).ra;
// unpack from AlphaLuminance (2 floats) to a single float
float depth = dot(packedDepth, vec2(255.0, 256.0 * 255.0));
#endif
// normalize to meters
depth *= depth_raw_to_meters;
// render as greyscale, darker - closer, lighter - further
gl_FragColor = vec4(depth, depth, depth, 1.0);
}
示例
以下示例在摄像机前绘制矩形,使用与上文类似的着色器显示深度:
深度感知示例