
WebGPU 实时光线追踪渲染器实现原理
基于 BabylonJS + WebGPU 的浏览器端路径追踪渲染器,支持 PBR 材质、全局光照,无需安装任何插件。
编辑器演示视频:www.bilibili.com/video/BV1dZ...
编辑器在线地址:webgpu.moyunhe.com/
一、整体架构
渲染器分为五个核心层:
┌─────────────────────────────────────────┐
│ GPUEditor3D(对外接口) │
├─────────────────────────────────────────┤
│ GPUEditor(编辑器逻辑 + 插件管理) │
├─────────────────────────────────────────┤
│ WebgpuPreview(WebGPU 引擎 + 场景管理)│
├──────────────┬──────────────────────────┤
│ GPURayTrace │ BVHBuilderPlugin │
│ (路径追踪) │ (加速结构构建) │
├──────────────┴──────────────────────────┤
│ GPUAssetManager(资源管理) │
└─────────────────────────────────────────┘
| 模块 | 职责 |
|---|---|
WebgpuPreview |
WebGPU 引擎初始化、场景管理、渲染循环 |
GPURayTrace |
路径追踪计算着色器、采样、降噪调度 |
BVHBuilderPlugin |
BVH 加速结构构建(BLAS + TLAS)、材质打包 |
GPUAssetManager |
glTF 模型加载、HDR 环境贴图、纹理管理 |
TileRender |
自适应分块渲染,防止 GPU 超时 |
二、WebGPU 引擎初始化
引擎使用 BabylonJS 的 WebGPUEngine,开启全部 WebGPU 特性:
typescript
new WebGPUEngine(canvas, {
enableAllFeatures: true, // 开启所有 WebGPU 扩展
setMaximumLimits: true, // 使用最大资源限制
antialias: true,
useHighPrecisionMatrix: true
})
用到的 WebGPU 特性:
- Compute Shader --- 路径追踪主管线
- Storage Buffer --- BVH 节点、三角形、法线、材质、光源数据
- Storage Texture --- 累积帧缓冲(ping-pong 双缓冲)
- Texture Array --- 材质纹理图集
- Uniform Buffer --- 相机矩阵、采样参数、光源数量
- Command Encoder --- GPU buffer 拷贝与后处理
三、BVH 加速结构
光线追踪的性能核心是 BVH(Bounding Volume Hierarchy,层次包围盒)。本渲染器采用两级 BVH:
3.1 BLAS(Bottom-Level AS,网格级)
每个网格独立构建一棵 BVH,流程如下:
css
网格顶点数据
│
▼
计算每个三角形的 AABB
│
▼
计算 Morton Code(Z-order 曲线,空间编码)
│
▼
GPU Radix Sort(按 Morton Code 排序)
│
▼
构建 Radix Tree(确定父子关系)
│
▼
自底向上合并 AABB(BVH 内部节点)
│
▼
重排三角形数据(缓存友好布局)
Morton Code 将三维空间坐标编码为一维整数,排序后相邻三角形在空间上也相邻,BVH 遍历时缓存命中率更高。
GPU Radix Sort 是整个构建流程中最复杂的部分,通过多趟 GPU Compute Shader 完成,避免 CPU-GPU 数据回传。
3.2 TLAS(Top-Level AS,场景级)
将所有 BLAS 合并为场景级 BVH,每个节点存储:
- 网格实例的世界矩阵 / 逆矩阵
- 对应 BLAS 的索引
- 材质 ID
光线先与 TLAS 求交,命中后再进入对应 BLAS 精确求交,大幅减少无效计算。
四、材质系统与纹理图集
4.1 纹理图集(Texture Atlas)
场景中所有材质的纹理被打包进 2D Texture Array,每层 1024×1024,按属性分组:
| 图集 | 内容 |
|---|---|
| Albedo Atlas | 基础色 + 透明度 |
| Normal Atlas | 法线贴图 |
| Emission Atlas | 自发光 |
| MRCC Atlas | 金属度 / 粗糙度 / 清漆 |
| Sheen Atlas | 光泽颜色 / 强度 |
| Transmission Atlas | 透射 / 次表面散射 |
这样在着色器中只需一次纹理绑定,通过材质 ID 和 UV 变换矩阵索引任意材质属性,避免了频繁切换纹理绑定的开销。
4.2 材质数据打包
每个材质的参数(粗糙度、金属度、IOR、各图集层索引等)被打包进 Storage Buffer,着色器通过材质 ID 直接读取,支持完整的 PBR 材质模型:
- 漫反射 / 镜面反射
- 清漆(Clearcoat)
- 光泽(Sheen)
- 透射(Transmission)
- 次表面散射(Subsurface)
五、路径追踪核心
5.1 采样策略
渲染器使用分层采样(Stratified Sampling)结合抖动(Jitter):
每像素 → 预计算分层采样表 → 加噪声纹理抖动 → 低差异序列
- 每次弹射使用 11 个采样维度(方向、材质、光源选择等)
- 最大弹射深度:11 次
- 深度 ≥ 2 后启用**俄罗斯轮盘赌(Russian Roulette)**随机终止路径,无偏估计能量
5.2 光照采样
直接光照(Next Event Estimation):
- 支持最多 100 个光源
- 按光源强度加权随机选择,减少方差
环境光照(Environment Map Importance Sampling):
- 预计算环境贴图的 CDF(累积分布函数)
- 采样时用二分查找 CDF,优先采样亮区,大幅降低噪声
5.3 Ping-Pong 累积缓冲
渲染器维护两张累积纹理(A / B),每帧交替作为读写目标:
css
第 N 帧:读 A,写 B(混合新采样)
第 N+1 帧:读 B,写 A
相机静止时,帧数不断累积,画面逐渐收敛到无噪声的最终结果。相机移动时立即重置累积。
5.4 Tone Mapping
最终输出使用 ACES Filmic 色调映射曲线,还原电影级色彩表现,并进行 sRGB Gamma 校正。
六、自适应分块渲染
WebGPU 的 GPU 超时机制(TDR)会在单次提交耗时过长时强制重置设备。为此,渲染器将每帧的计算任务拆分为多个小块(Tile)分批提交:
自适应算法:
ini
目标:每块耗时 ≈ 21ms
实际耗时 > 21ms → 缩小 pixelsPerTile
实际耗时 < 21ms → 扩大 pixelsPerTile
调整公式:
pixelsPerTile += strength × sign(error) × √|error|
(strength = 5000,快速收敛)
范围限制:[8192, 全屏像素数]
这样在不同性能的 GPU 上都能保持流畅,既不会触发超时,也不会因块太小而浪费调度开销。
七、渲染流程总览
markdown
用户打开场景
│
▼
GPUAssetManager 加载 glTF 模型
│
▼
BVHBuilderPlugin 构建 BLAS(每个网格)
│
▼
BVHBuilderPlugin 构建 TLAS(场景级)
│
▼
打包材质纹理图集 → Storage Buffer
│
▼
渲染循环开始
│
├─ 相机移动?→ 重置累积缓冲
│
▼
TileRender 分块调度
│
▼
GPURayTrace Compute Shader(路径追踪)
│
├─ BVH 求交
├─ 材质采样(PBR)
├─ 直接光照 + 环境光照
└─ 写入累积缓冲
│
▼
Tone Mapping(ACES)→ 输出到屏幕
八、关键技术亮点
-
纯 WebGPU 实现:所有计算(BVH 构建、路径追踪、后处理)全部在 GPU 上完成,CPU 只负责调度,无需 CPU-GPU 数据回传。
-
GPU Radix Sort:BVH 构建中的排序完全在 GPU 上执行,避免了传统 CPU 排序的性能瓶颈。
-
自适应分块:动态调整每块像素数,在任意 GPU 上都能保持流畅,不触发 TDR 超时。
-
CDF 环境光采样:预计算环境贴图亮度分布,采样时优先选择高亮区域,相比均匀采样噪声降低数倍。
-
完整 PBR 支持:支持清漆、光泽、透射、次表面散射等高级材质,与 glTF 2.0 扩展完全兼容。