2022年初,使用安卓原生(基于filament渲染引擎)采用AR虚实遮挡的技术实现了地下隐蔽设施的显示效果【AR开发示例】实现AR管线巡检,近期有粉丝问到其实现原理。现针对WebXR中使用Three.js,做一版解析。
类似文档:
【AR开发示例】实现AR管线巡检
以下正文
一、背景与应用场景
1.1 AR 中的“实挡虚”需求
在增强环境中,“实挡虚”是很重要的一种渲染技术。它的意思是:
实际物体可以遮挡虚拟对象,使得虚拟对象看起来真正在环境中。
例如,一个虚拟人物走进桌子后面,那么它应该被桌子遮挡,而不是全部显示出来。
1.2 常见的遮挡技术对比
方法 | 原理 | 优点 | 缺点 |
---|---|---|---|
使用深度摄像头 | 利用深度信息 | 自动化,真实 | 成本高,依赖硬件 |
人工遮罩模型 | 按照环境建模 | 可控性强 | 工作量大,无法实时 |
使用 Stencil Buffer | 基于模板缓冲的裁剪 | 性能好,实时 | 实现处理繁琐 |
而地下设施的显示,则是需要利用模板测试,裁剪出显示范围。
二、技术方案概述
2.1 Three.js 的裁剪限制
Three.js 本身提供了 setScissor()
和 clipping planes,但都有限制:
setScissor()
只能做矩形裁剪,无法处理不规则形状clippingPlanes
是平面裁剪,无法包围一个多边形区域
2.2 举揭 Stencil Buffer 技术
Stencil Buffer (模板缓冲),是一种可用于 WebGL 渲染流程中的控制技术:
- 先将遮挡区域写入 stencil buffer
- 再渲染场景,仅在 stencil 区域中显示
- 可实现 任意形状、实时更新的遮挡
三、实现过程:实时多边形遮挡
3.1 初始化 renderer
const renderer = new THREE.WebGLRenderer({
antialias: true,
stencil: true // 启用 stencil buffer
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
3.2 构造遮挡区域多边形
假设我们通过实体识别/检测得到了桌子的边界点、或是通过获取开挖地下坑洞模型的尺寸计算边界:
const shape = new THREE.Shape();
shape.moveTo(p0.x, p0.y);
points.forEach(p => shape.lineTo(p.x, p.y));
const geometry = new THREE.ShapeGeometry(shape);
3.3 创建 stencil 模型,不写入颜色
const stencilMaterial = new THREE.MeshBasicMaterial({
colorWrite: false // 不写入颜色缓冲
});
const stencilMesh = new THREE.Mesh(geometry, stencilMaterial);
stencilScene.add(stencilMesh);
3.4 渲染流程
(一)Stencil Pass :写入 stencil
renderer.state.buffers.stencil.setTest(true);
renderer.state.buffers.stencil.setFunc(gl.ALWAYS, 1, 0xff);
renderer.state.buffers.stencil.setOp(gl.REPLACE, gl.REPLACE, gl.REPLACE);
renderer.state.buffers.color.setMask(false);
renderer.state.buffers.depth.setMask(false);
renderer.render(stencilScene, orthoCamera);
(二)Scene Pass :仅在 stencil = 1 区域渲染
renderer.state.buffers.stencil.setFunc(gl.EQUAL, 1, 0xff);
renderer.state.buffers.stencil.setOp(gl.KEEP, gl.KEEP, gl.KEEP);
renderer.state.buffers.color.setMask(true);
renderer.state.buffers.depth.setMask(true);
renderer.render(mainScene, camera);
四、实时更新遮挡区域
对于需要实时识别/检测遮挡区域的场景,可以进行下列操作:
- 通过图像识别、点云进行线描边界
- 生成新的多边形顶点 array
- 重置 ShapeGeometry 更新 stencil mesh
shape.currentPath = new THREE.Path();
points.forEach(p => shape.lineTo(p.x, p.y));
mesh.geometry.dispose();
mesh.geometry = new THREE.ShapeGeometry(shape);
每帧样更新,就可以达到实时遮挡效果
五、实战场景
5.1 场景说明
显示地下设施
5.2 实现流程
- 根据模型计算开挖坑洞的3D空间坐标
- 转换成屏幕2D坐标
- 创建 stencilMesh
- 培入渲染流程,实现遮挡
5.3 效果展示
以下数据非真实数据,仅是功能测试时编造的数据。
六、进阶技巧与优化
6.1 多个遮挡区域支持
- 使用多个 stencilMesh 合并成一个 geometry
- 可通过不同值 stencil id 区分区域
6.2 控制遮挡优先级
- 利用
renderOrder
先后顺序 - 配合
depthTest
,确保正确遮挡
6.3 半透明遮挡
- 结合 alpha mask + stencil 实现透明遮挡
- 可用于玻璃遮挡效果
七、总结
- 通过 stencil buffer,可以在 WebAR/现实场景中实现实物遮挡虚拟效果
- 支持任意形状,高性能,可实时调整
- 推荐配合 AI 规划或点云线描实现自动遮挡
- 未来可展望进一步接入 WebXR 和深度信息,进一步提升真实感