3.1 求交手推:球体、三角形与包围盒


3.1 求交手推:球体、三角形与包围盒

上一章的范式地图上,所有路线都经过同一个收费站:求交。本节就是收费站的设计图纸——三种基元、三种解法风格,全部手推,并各配一个数值算例。不求你看完就会背公式,只求你跟着算完一遍后,日后面对任何求交 bug 都能回到推导链上定位。

球体:代入法解一元二次

球心 c、半径 r 的球面满足"点到球心距离平方等于 r 平方"。把射线表达式 p 加 t 乘 d 代入,整理成关于 t 的一元二次方程:

|o + t·d − c|² = r² 展开并整理: a = d·d (d 已归一化时 a = 1) b = 2·(d·(o − c)) c₀ = (o − c)·(o − c) − r² 判别式 disc = b² − 4·a·c₀ t = (−b ± √disc) / (2a)

判别式的几何含义一目了然:小于零无交,等于零相切,大于零穿过。取两个根中落在 tmin 与 tmax 之间较小的那个作为命中。数值算例走一遍:设球心在原点、半径为 1,射线起点 o 取 (0, 0, 5)、方向 d 取 (0, 0, -1)。则 c₀ 等于 5 平方减 1 等于 24,b 等于 -10,判别式等于 100 减 96 等于 4,开方得 2,两个 t 分别为 4 与 6——射线在距离 4 处进入球、距离 6 处穿出。若把方向稍微抬高到掠射角度,判别式会一路萎缩到零(相切)再转负(脱靶),相切附近的浮点抖动是球体求交在低精度下最常见的漏交来源,工程上常用 EPS 容差兜底。

一个新手必踩的坑顺带交代:b 忘了乘 2 或 c₀ 忘了减 r 平方是最常见的两处笔误;更隐蔽的是方向未归一化时 a 不等于 1,此时若仍按简化公式计算,t 的含义不再是距离,后续所有距离比较全部错位。

三角形:Möller-Trumbore 的白送赠品

三角形求交的两步本质:先求射线与三角形所在平面的交点,再判断交点是否落在三角形内部。经典做法把两步合并成一个 3 元线性方程组,用克莱姆法则展开后得到著名的 Möller-Trumbore 算法——它只有一次行列式判定加三次行列式计算,无分支、易向量化,是全行业二十余年的默认实现。

记边 e1 = v1 − v0,e2 = v2 − v0 p = d × e2 (叉积) det = e1 · p (行列式) 若 det 接近 0:射线与三角形所在平面平行,脱靶 inv = 1 / det s = o − v0 u = inv · (s · p) (重心坐标之一) 若 u < 0 或 u > 1:脱靶 q = s × e1 v = inv · (d · q) (重心坐标之二) 若 v < 0 或 u + v > 1:脱靶 t = inv · (e2 · q) 若 t 在有效区间:命中,重心权重为 (1−u−v, u, v)

推导不值得死记,但两个设计意图值得咀嚼。其一,行列式接近零的判定同时挡掉了平行与退化(共线顶点)两类病态;其二,u 与 v 恰好是交点的重心坐标——法线插值、纹理坐标插值所需的三个权重 (1−u−v, u, v) 不用再算,算法白送。这就是它在无分支环境下依旧统治的深层原因:一次计算,三份产出。数值算例:取 v0 为 (0,0,0)、v1 为 (1,0,0)、v2 为 (0,1,0),射线起点 (0.25, 0.25, 1)、方向 (0, 0, -1)。代入可得 det 为 -1,u 为 0.25,v 为 0.25,u 加 v 不超过 1,t 为 1——命中 z 等于 0 平面上的点 (0.25, 0.25, 0),与几何直觉完全吻合。

图:射线命中三角形时的重心坐标权重

图:射线命中三角形时的重心坐标权重

包围盒:平板法与区间思想

轴对齐包围盒(AABB)不求交点,只回答"射线在多远的区间内可能穿过我"。把盒子的三组平行面想象成三对垂直的墙壁:射线与每组墙壁各有一个进入与离开参数,三组区间的重叠部分若仍为正区间,盒子即被穿过。这就是平板法(slab method):

对 x、y、z 三轴分别: t1 = (box.min − o) / d t2 = (box.max − o) / d t_near = max(三轴 t1 与 t2 的较小值) t_far = min(三轴 t1 与 t2 的较大值) 判定:若 t_near <= t_far 且 t_far > tmin,则命中 交区间取 [max(t_near, tmin), t_far]

两处细节藏着正确性。第一,d 的分量为零(射线与该轴平行)时除法产生无穷大,IEEE 浮点恰好让比较逻辑自然工作,无需特判——但前提是你按上述 max 与 min 的次序写,次序一换无穷大就会污染结果。第二,"t_near 大于 t_far 即脱靶"的几何含义是"进入最晚的墙,先于离开最早的墙",即三个通道没有公共重叠段。数值算例:盒子取 min (1,1,0)、max (3,4,2),射线起点原点、方向 (1, 1, 1) 归一化前的比例方向即可:x 轴区间 1 到 3,y 轴区间 1 到 4,z 轴区间 0 到 2;t_near 取三轴进入的最大值 1,t_far 取三轴离开的最小值 2,1 不大于 2,命中,区间 1 到 2。

平板法算出的不只是"是否命中"——它交付的区间 [t_near, t_far] 是第 4 章 BVH 遍历的命脉:遍历器靠"区间与当前最优命中比较"来决定要不要走进子树,靠区间排序决定先走哪个孩子。这个"不求精确、只求区间"的思想,是几何层与加速层之间真正的接口。

数值边界的三个补丁

手推公式只是半成品,浮点世界会补上另一半课程。补丁一,判别式容差:掠射时判别式在零附近抖动,同一颗球这一帧报相切、下一帧报脱靶,画面边缘随之闪烁。处理办法不是盲目加大精度,而是承认抖动:判别式落在零附近的小窗口内就按相切处理,命中距离取窗口中点,把抖动变成稳定近似。补丁二,退化三角在加载期剔除:三个顶点几乎共线的三角形会让重心坐标分母趋零、权重溢出;宁可建网格时多扫一遍把退化面扔掉,也不要在求交热路径里加分支——加载期剔一次,此后十亿次调用都干净。补丁三,精度档位分源头:渲染器主体全程单精度是主流,因为几何数据量巨大、带宽金贵;但世界变换矩阵连乘与相机光线生成这两个源头建议用双精度算完再转单精度——源头错一行,全图都是接缝纹与碎面阴影,够你查一星期。三个补丁共享一条哲学:浮点不是敌人,假装它是精确数学才是。

最后把三个基元串成一次对比手推。同一条射线从原点指向 x 正方向,穿过三个工件:球心 (3,0,0)、半径 1 的球;顶点 (2,-1,0)、(2,1,0)、(4,0,1) 的三角形;min (2,-2,-2)、max (5,2,2) 的盒子。球解出两个根 t=2 与 t=4,近根 2 是可见命中;三角形解出重心坐标 (0.5, 0.5, 0),命中点恰在第一条边正中、t 同样等于 2,与球的近根同深度;盒子给区间 [2, 5],正好把两个命中深度都罩住——这枚盒子就是这三个基元未来的 BVH 节点。一次手推同时看到三件事:精确根、插值权重、保守区间。第 4 章的加速结构,本质只是把"盒子罩基元"这件事递归做下去;几何层交付的所有信息,加速层全都用得上。

本节要点回顾

  • 球体求交是代入法解二次方程,判别式读出无交、相切、穿过三种几何命运;掠射处的浮点抖动要留容差。
  • Möller-Trumbore 把两步合并为解线性方程组,重心坐标权重是免费赠品,法线与纹理插值直接受益。
  • 平板法交付的是区间而非点,区间思想是 BVH 遍历的接口;除零交给 IEEE 浮点自然处理,但 max 与 min 次序不能乱。
  • 每个公式都要配一遍数值算例:手算通过,实现才敢信。
  • 方向必须归一化,否则 t 不再是距离,所有距离比较连锁错位。

三类基元的"撞得上"已经可靠。但场景里不是三件几何,而是百万件——下一节先讲场景如何组织,加速结构则留给第 4 章专门拆解。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U