Three.js Integration 架构
Visionary 在同一个 WebGPU 设备上渲染高斯 Splat 和经典的 Three.js 内容。本文档解释了协调层是如何连接的,深度和覆盖层是如何捕获的,以及低级集成(GaussianSplattingThreeWebGPU)如何在编辑器之外保持可复用性。
高层布局
THREE.WebGPURenderer (GPUDevice/GPUCanvasContext 的所有者)
│
├─ Scene Graph (网格, 灯光, Gizmo, GaussianModel 实例)
│ └─ GaussianThreeJSRenderer (继承自 THREE.Mesh)
│ ├─ 挂钩到 onBeforeRender 以运行高斯预处理
│ ├─ 通过 renderThreeScene() 捕获颜色+深度
│ ├─ 使用共享的命令编码器发布 drawSplats()
│ └─ 合成可选的 Gizmo 覆盖层
│
└─ Low-level helper: GaussianSplattingThreeWebGPU
├─ 拥有 GaussianRenderer (计算 + 渲染管道)
├─ 管理 PointCloud / DynamicPointCloud 生命周期
└─ 使用 DirectCameraAdapter 进行相机转换
动机
- 单 GPU 设备 – WebGL 和 WebGPU 上下文之间没有握手,零纹理拷贝,没有画布堆叠逻辑。
- 确定性深度 – 场景网格和 Splat 读/写相同的深度值(每帧捕获一次)。
- 可扩展性 –
GaussianThreeJSRenderer存在于场景图中,因此 XR、后期处理、编辑器 Gizmo 和 OrbitControls 均可正常工作。
帧时间线
function animate(nowMs: number) {
requestAnimationFrame(animate);
gaussianRenderer.updateDynamicModels(camera, nowMs * 0.001); // (1) ONNX 变形
gaussianRenderer.renderOverlayScene(gizmoScene, camera); // (2) 可选辅助
gaussianRenderer.renderThreeScene(camera); // (3) 捕获场景颜色+深度
gaussianRenderer.drawSplats(threeRenderer, scene, camera); // (4) Splat + 覆盖层合成
}
- 动态更新 – 相机矩阵(通过
CameraAdapter转换)驱动基于 ONNX 的DynamicPointCloud,因此它们可以烘焙视差感知着色数据。 - 覆盖层渲染 – Gizmo 或 HUD 场景渲染到
gizmoOverlayRT中,使用相同的 Three.js 渲染器以确保一致的色调映射。 - 场景捕获 – 主场景渲染到一个内部的 HalfFloat 渲染目标,带有 Float 深度纹理。WebGPU 全屏通道将颜色缓冲区 Blit(传输)到实际画布,处理 Three.js 期望的线性→sRGB 转换。
- 高斯通道 –
onBeforeRender预计算变换并分发计算工作。drawSplats()提交一个渲染通道,该通道读取捕获的深度纹理(当自动深度开启时),并可选择在交换链之上合成 Gizmo 覆盖层。
共享设备命令流
GaussianThreeJSRenderer从THREE.WebGPURenderer.backend获取GPUDevice和GPUCanvasContext。- 每一帧它为每个阶段(
prepareMulti和drawSplats)创建一个单一命令编码器。没有二级上下文或画布——Splat 直接绘制到context.getCurrentTexture()中。 - 渲染器依赖于
GaussianRenderer.prepareMulti(...)/renderMulti(...),因此所有可见模型在一个计算/渲染对中批处理。这使得即使有许多模型,队列提交也保持最小。
相机转换
DirectCameraAdapter(由 GaussianSplattingThreeWebGPU 使用)和 CameraAdapter(在应用的其他地方使用)遵循相同的规则集:
- 从
camera.matrixWorldInverse(Three 的视图矩阵)开始,乘以R_y(π)以采用渲染器的 +Z 向前约定,而不改变手性。 - 将投影矩阵乘以相同的
R_y(π),以保持P * V与 Three.js 输出相同。 - 在预处理后将第二行取反,以抵消打包 Splat 时发生的单次 Y 翻转。
- 从活动视口推导像素空间中的焦距,以便计算着色器接收准确的镜头数据。
适配器暴露 viewMatrix(), projMatrix(), position(), frustumPlanes(), 以及一个与高斯渲染器期望相匹配的 projection.focal() 垫片(shim)。
深度捕获与覆盖层
自动深度模式(默认)
renderThreeScene()懒加载分配一个THREE.RenderTarget(width, height, HalfFloat)和一个匹配的THREE.DepthTexture(width, height, FloatType)。- Three.js 场景每帧渲染到该目标一次。
- WebGPU 渲染通道将目标颜色纹理 Blit 到交换链,同时从线性转换为 sRGB。
- 当
drawSplats()运行时,它获取支持DepthTexture的 WebGPU 纹理句柄,并将其作为depthStencilAttachment(load/store = load)插入渲染通道描述符。因此,Splat 会自动遵守所有网格遮挡。 - Gizmo 覆盖层随后使用简单的带纹理全屏四边形进行合成,并使用预乘 Alpha 混合渲染。
禁用自动深度(setAutoDepthMode(false))将控制权交还给调用者。旧项目随后可以调用 setOccluderMeshes(...),这会在每一帧将精简后的场景渲染到临时深度纹理中。保留此路径仅出于兼容性原因。
诊断
diagnoseDepth()打印自动深度是否启用、渲染目标尺寸和当前 GaussianRenderer 深度状态。disposeDepthResources()清除缓存的渲染目标,以便下一帧重新创建它们——这在设备丢失或画布调整大小异常后很有用。
GaussianSplattingThreeWebGPU 内部结构
GaussianSplattingThreeWebGPU 被有意设计得很小:
const gs = new GaussianSplattingThreeWebGPU();
await gs.initialize(renderer.backend.device);
await gs.loadPLY('/models/room.ply');
const encoder = device.createCommandEncoder();
gs.render(encoder, swapChainView, camera, [width, height], depthView);
device.queue.submit([encoder.finish()]);
initialize(device)实例化GaussianRenderer(device, 'bgra8unorm', 3)并确保 GPU 排序器管道已就绪。loadPLY/loadFile依赖于defaultLoader并将结果包装在PointCloud中。render(...)更新内部DirectCameraAdapter,运行prepareMulti,通过setDepthEnabled可选地切换深度,并发出一个渲染通道。- 该助手暴露
setVisible,numPoints,setDepthEnabled, 和dispose用于生命周期管理。
动态模型处理
GaussianModel.update()执行 ONNX 推理,将当前变换、视图矩阵、投影矩阵和可选动画时间传递给DynamicPointCloud。GaussianThreeJSRenderer.updateDynamicModels()简单地迭代每个注册的模型并等待model.update(...)。这在任何 GPU 工作之前运行,因此 Splat 缓冲区始终包含最新的位置/SH 数据。- 渲染器还暴露了细粒度的 setter(
setModelGaussianScale,setModelOpacityScale,setModelRenderMode等)以及对每个GaussianModel进行操作的全局变体(setGlobalTimeScale,startAllAnimations等)。
错误处理
- 缺少 WebGPU 支持:调用者应保护渲染器的创建(参见
initThreeContext)并回退到仅 WebGL 的场景。 - 相机不匹配:将所有转换保持在
CameraAdapter/DirectCameraAdapter中——临时的矩阵调整很快会导致输出镜像。 - 设备丢失:在
device.lost事件后调用disposeDepthResources()和diagnoseDepth();下一次renderThreeScene()调用将重新创建目标。
值得阅读的文件
src/app/GaussianThreeJSRenderer.ts– 编排层,自动深度,覆盖层,运行时控制。src/three-integration/GaussianSplattingThreeWebGPU.ts– 用于直接集成的轻量级助手。src/camera/CameraAdapter.ts– 可复用的相机转换逻辑。src/app/GaussianModel.ts– 带有自动同步和动画助手的 Object3D 包装器。src/renderer/gaussian_renderer.ts– 共享计算/渲染管道。