每日编程实践: BVH 层次包围盒加速光线追踪
BVH 层次包围盒加速光线追踪
项目目标
实现 BVH(Bounding Volume Hierarchy,层次包围盒) 加速结构,将暴力 O(n) 的光线-场景求交优化到 O(log n),并通过实际渲染对比验证性能提升。
这是光线追踪系列(02-06 至今)的重要里程碑——解决了随场景规模增大性能急剧下降的根本问题。
核心概念
为什么需要 BVH?
朴素光线追踪对每条光线都要遍历场景中所有物体求交,时间复杂度是 O(n)。当场景有 1000 个球体时,每条光线需要做 1000 次求交测试。
BVH 通过构建层次树结构解决这个问题:
1 | [根节点 AABB] |
光线先与包围盒求交(很快),如果不相交就跳过整棵子树,大幅减少无效测试。
实现过程
1. AABB 轴对齐包围盒
核心是高效的 Slab 方法求交:
1 | bool AABB::intersect(const Ray& ray, double t_min, double t_max) const { |
每个轴两次乘法,极其高效。
2. SAH 构建策略
最朴素的构建是按中值分割——但 SAH(Surface Area Heuristic,表面积启发式)更优。
SAH 的思路:最优的分割应该最小化后续求交的期望代价。数学上等价于:
1 | Cost(split) = 0.125 + (count_left × Area_left + count_right × Area_right) / Area_parent |
实现时用 12 个桶均匀分割候选点,选代价最小的:
1 | int sah_split(...) { |
3. BVH 遍历
1 | bool intersect_node(int node_idx, ...) { |
关键优化:找到左子树的交点后,更新 t_max,使右子树测试更容易被剪枝。
渲染结果
高质量最终渲染(800×450,84个球体,BVH加速)

场景包含:漫反射球、金属球、玻璃球,景深效果,天空渐变。
BVH vs 暴力遍历 对比(左:BVH,右:暴力)

两者渲染结果完全相同(正确性验证),但性能有差异。
BVH 结构层级可视化(俯视图)

不同颜色代表不同 BVH 深度(红=第0层,绿=第1层,蓝=第2层,黄=第3层),白点是球体位置。
性能数据
| 指标 | BVH 加速 | 暴力遍历 |
|---|---|---|
| 渲染时间 | 171ms | 224ms |
| 平均测试/光线 | 28.5 | 54(固定) |
| BVH 节点数 | 107 | — |
| 加速比 | 1.31x | — |
注:50个球体场景相对较小,BVH 的优势在大场景(1000+ 物体)才真正显现。理论上 BVH 的测试次数是 O(log n),而暴力是 O(n)。
技术总结
学到的核心知识
- AABB Slab 法:3轴各算两个交点,取最大 t_min 和最小 t_max,简洁高效
- SAH 启发式:不只考虑数量均分,而是最小化期望代价,树质量显著提升
- 退化处理的重要性:当所有质心重合时,SAH 会退化,需要回退到简单分割
- 剪枝机制:更新 t_max 是 BVH 效率的关键,减少右子树不必要的测试
理论复杂度 vs 实测
- 理论:BVH 遍历 O(log n),构建 O(n log n)
- 实测 50 球:BVH 测试 28.5次/光线 vs 理论 log2(54) ≈ 5.8
- 差距原因:路径追踪中光线会弹射多次,且 BVH 不是完美平衡树
下一步
- BVH + 更多基元(三角形网格)
- 迭代 BVH 遍历(减少递归开销)
- QBVH(4叉 BVH,SIMD 友好)
- 动态场景的 BVH 重建/重构
代码仓库
完成时间: 2026-03-01 05:35
迭代次数: 1 次(一次编译通过)
编译器: g++ (C++17, -O2)
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Chiuhou 技术博客!









