视锥相交测试
View Frustum Intersection Tests
层次视锥剔除遍历 BVH 时最频繁调用的就是视锥与包围体的测试,理想结果分三态:完全在外(剪掉子树)、完全在内(子树免测)、相交(递归)。本页讲从组合矩阵直接提取六个视锥平面、把"BV 对视锥"转化为"点对扩张/收缩体积"的推导思路,以及视锥-球、视锥-盒的高效保守测试及其误报情况。
三态与保守性(22.14 节)
只区分"排除/可能在内"的简化测试会面临两难:把"可能在内"当作完全在内,会渲染整个子树而不再剔除;对子树每个节点再测排除又常常白费,因为大部分可能确实在内。所以即使不完美,尽量快速区分相交与包含通常值得。
对场景图剔除,快速分类只需保守:排除与包含之间只能错向包含(应排除的被包含只是浪费时间;应包含的被排除会导致渲染错误);包含与相交之间两种误判都合法,只影响效率。保存父 BV 的相交状态是有用的优化;平面掩码和时间相干在 BVH 上效果明显,但对 SIMD 实现帮助较小。
通用推导思路:选一个相对 BV 固定的点 ,让 BV 紧贴视锥外侧滑动一周, 的轨迹围出一个外扩体积;紧贴内侧滑动则得到与原视锥平面平行的收缩视锥。 在外扩体积之外则 BV 在视锥外,在收缩体积之内则 BV 完全在内,其余为相交。这些体积只取决于 BV 的形状和 的相对位置,与 BV 位置无关,所以任意位置的 BV 都可对同一组体积测试。
平面提取(22.14.1 节)
组合矩阵 ,点 ()变换为 ,透视除法后得 。OpenGL 约定下视锥内的点满足 (DirectX 中 )。以左平面为例:
其中 表示 的第 行。取负使法线朝外,六个平面为:
| 平面 | 方程 |
|---|---|
| 左 | |
| 右 | |
| 下 | |
| 上 | |
| 近 | |
| 远 |
(近平面一行按 OpenGL 的 推出;DirectX 的 需相应调整。)
视锥-球(22.14.2 节)
- 正交视锥是盒子,可用球-OBB 测试;再检查球心在各轴上距边界都大于半径即完全包含。
- 一般视锥:取球心为 。精确的外扩体积有圆角,为效率用"把每个平面沿法线外推 "近似,收缩体积为每个平面内推 。实现:对六个平面(正半空间朝外)计算球心有符号距离,任一大于 则在外;全部小于 则在内;否则报告相交。球心落在尖角区域(圆角之外但在所有外推平面之内)时会把实际在外的球误报为相交——这是保守的,偶尔误报只多一些测试,加更多平面反而总体更慢。
- 分块着色中的瓦片视锥细长不对称,需要额外的球-盒测试(见 分块着色);还有把视锥分成八分区、按物体中心所在八分区每次省去三个平面的方法。
视锥-盒(22.14.3 节)
| 方法 | 做法 | 特点 |
|---|---|---|
| 裁剪空间测试 | 把 8 个角用视图和投影矩阵变换到规范视体,做 裁剪测试:全部在某边界外则拒绝,全部在内则包含 | 无需提取平面,适用于任何由点界定的对象(线段、三角形、k-DOP),简单自洽,适合计算着色器 |
| 平面-盒测试 | 对六个平面用平面-AABB/OBB 测试,每个平面最多测两个角:最近角在外则完全在外可提前结束;每个平面的最远角都在内则包含 | CPU 上明显更高效;近远平面平行可共享点积;只需先提取平面 |
误报:盒在视锥外但靠近角落时会被判为相交,对固定大小的地形网格等大物体更常见。Quílez 的补救是在报告相交后再用视锥角点测试盒的各平面(相当于 SAT 的第二部分),全部在某盒平面外则不相交;但这可能得不偿失——一个 GIS 渲染器中该优化每帧花 2 ms CPU 只省下几个绘制调用。Wihlidal 反其道而行,只用四个侧平面:游戏中近平面几乎被侧平面覆盖,远平面通常设置为能看到所有物体。也可以用 SAT 写一个通用的凸多面体测试,同时适用于线段、三角形、AABB、OBB、k-DOP、视锥和凸多面体的任意组合。
出处
- realtime_rendering_4ed.pdf 第 22 章 Intersection Test Methods