MAKE · WRITE · SHARE

Base-BVH构建与遍历

一条射线若与场景中每个三角形逐一求交,模型越多成本越高。BVH(Bounding Volume Hierarchy,包围体层次结构)用便宜的包围盒测试,排除大量不可能命中的图元。

它首先是对象的层次分组,而不是把空间平均切成格子。构建时怎样分组,决定了后续遍历要付出多少无效测试;构建越贵,也不一定对动态场景越划算。

父包围盒排除整组图元,叶节点再执行精确相交测试

1. 节点包住一组图元

内部节点保存子节点引用与包围盒,叶节点保存一小段图元范围。父节点包围其全部后代,两个子节点的盒子可以重叠。

射线不与父盒相交,就能跳过整个子树。射线进入叶节点,才执行更精确的三角形求交。BVH 的效率来自层次排除,不是盒子本身比三角形更精确。

原笔记把八叉树称为平均结构也过于简单。八叉树可以自适应细分;它与常见 BVH 更关键的区别,是按空间分割还是按对象分组。

把几何包围体分层组织后的可视化

原笔记配图:把几何包围体分层组织后的可视化;包围盒用于排除整组不相关图元。

2. 先理解 AABB 求交

对每个轴计算射线进入和离开该轴 slab 的 t 区间,再取三轴区间交集。交集为空则不相交。

零方向分量、盒子边界以及浮点误差都需要稳健处理。简单写 1/dir 再依赖无穷大参与所有运算,可能在边界上产生 NaN;应采用经过验证的实现或明确分支。

射线参数 t 的含义还取决于方向是否归一化。若前后使用不同空间或尺度,最近命中比较必须保持一致。

3. 一个中位数分割构建器

1
2
3
4
5
6
Build(primitives):
计算所有图元的联合包围盒
数量很少 → 生成叶节点
选择质心分布最宽的轴
按质心排序,沿中位数分成两组
递归构建左右子树

这个方法容易实现,通常能控制树深,却可能让细长图元和空间重叠造成大量额外访问。相同质心、空分区和递归终止需要专门处理。

旧记录中的中位数分割伪代码与重心排序思路

原笔记配图:旧记录中的中位数分割伪代码与重心排序思路。它说明构建步骤,不作为可直接编译的完整实现。

4. SAH:估计一次分割是否值得

Surface Area Heuristic(表面积启发式,SAH)用子盒表面积占比估计射线访问概率,再乘图元求交成本,比较拆分与保留叶节点的代价。

简化形式为 C_split=C_traverse + A_L/A_P*N_L*C_hit + A_R/A_P*N_R*C_hit。它是建立在射线分布假设上的成本模型,不是所有实际射线都会遵循的精确概率。

桶化 SAH 减少候选划分的评估成本。LBVH 则常用 Morton 编码把空间位置变成可排序键,便于并行构建;构建速度与遍历质量需要权衡。

5. 遍历取决于查询目标

最近命中需要保留当前最小 t,并尽量先访问更近的节点,以便缩短后续有效区间。可见性查询只关心有没有遮挡,找到合法命中便能提前结束。

递归适合理解;GPU 实现常需要显式栈、紧凑节点布局或其他遍历策略。栈容量、分支发散和访存局部性都会影响表现。

6. 动态更新的选择

只更新包围盒的 refit 比重建便宜,但图元移动后,原有分组可能变差,子盒重叠增加。持续变形的对象需要在 refit 成本、遍历劣化和重建频率之间平衡。

硬件光追还会区分底层与顶层加速结构。自写 CPU BVH 的节点布局,不等于 API 暴露的加速结构内部布局,也不该据此推断硬件实现。

总结

BVH 把精确测试推迟到真正相关的局部。构建器优化分组,遍历器利用分组,更新策略维护分组质量;评价时要同时观察构建时间、内存与实际射线工作量。

参考资料