【方案分享】基于Three.js和Stencil Buffer的AR实物遮挡方案,支持不规则动态区域(AR地下设施、AR虚实遮挡)

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

js 复制代码
const renderer = new THREE.WebGLRenderer({
  antialias: true,
  stencil: true // 启用 stencil buffer
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

3.2 构造遮挡区域多边形

假设我们通过实体识别/检测得到了桌子的边界点、或是通过获取开挖地下坑洞模型的尺寸计算边界:

js 复制代码
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 模型,不写入颜色

js 复制代码
const stencilMaterial = new THREE.MeshBasicMaterial({
  colorWrite: false // 不写入颜色缓冲
});
const stencilMesh = new THREE.Mesh(geometry, stencilMaterial);
stencilScene.add(stencilMesh);

3.4 渲染流程

(一)Stencil Pass :写入 stencil
js 复制代码
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 区域渲染
js 复制代码
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);

四、实时更新遮挡区域

对于需要实时识别/检测遮挡区域的场景,可以进行下列操作:

  1. 通过图像识别、点云进行线描边界
  2. 生成新的多边形顶点 array
  3. 重置 ShapeGeometry 更新 stencil mesh
js 复制代码
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 实现流程

  1. 根据模型计算开挖坑洞的3D空间坐标
  2. 转换成屏幕2D坐标
  3. 创建 stencilMesh
  4. 培入渲染流程,实现遮挡

5.3 效果展示

以下数据非真实数据,仅是功能测试时编造的数据。


六、进阶技巧与优化

6.1 多个遮挡区域支持

  • 使用多个 stencilMesh 合并成一个 geometry
  • 可通过不同值 stencil id 区分区域

6.2 控制遮挡优先级

  • 利用 renderOrder 先后顺序
  • 配合 depthTest,确保正确遮挡

6.3 半透明遮挡

  • 结合 alpha mask + stencil 实现透明遮挡
  • 可用于玻璃遮挡效果

七、总结

  • 通过 stencil buffer,可以在 WebAR/现实场景中实现实物遮挡虚拟效果
  • 支持任意形状,高性能,可实时调整
  • 推荐配合 AI 规划或点云线描实现自动遮挡
  • 未来可展望进一步接入 WebXR 和深度信息,进一步提升真实感

相关推荐
程序员Bears2 小时前
深入理解现代JavaScript:从ES6+语法到Fetch API
前端·javascript·python·es6
亦世凡华、3 小时前
React--》掌握react构建拖拽交互的技巧
javascript·经验分享·react.js·react-dnd·拖拽实现
IoOozZzzz3 小时前
ES6-Set-Map对象小记
前端·javascript·es6
松树戈3 小时前
idea结合CopilotChat进行样式调整实践
前端·javascript·vue.js·copilot
change_fate5 小时前
AbortController 取消请求
javascript·http
咖啡の猫6 小时前
JavaScript基础-分支流程控制
开发语言·javascript
香蕉可乐荷包蛋7 小时前
Three.js在vue中的使用(二)-动画、材质
javascript·vue.js·材质
钢铁男儿14 小时前
Python基本语法(函数partial)
前端·javascript·python
七灵微15 小时前
ES6入门---第二单元 模块二:关于数组新增
前端·javascript·es6