【C++游戏引擎开发】第33篇:物理引擎(Bullet)—射线检测
一、射线检测核心理论体系
1.1 射线检测的数学基础
1.1.1 参数化射线方程
射线在三维空间中的数学表达采用参数方程:
r ( t ) = o + t d ^ ( t ∈ [ 0 , L ] ) \mathbf{r}(t) = \mathbf{o} + t\mathbf{\hat{d}} \quad (t \in [0, L]) r(t)=o+td^(t∈[0,L])
其中:
- o ∈ R 3 \mathbf{o} \in \mathbb{R}^3 o∈R3 表示射线起点
- d ^ \mathbf{\hat{d}} d^ 是单位方向向量( ∣ ∣ d ^ ∣ ∣ = 1 ||\mathbf{\hat{d}}|| = 1 ∣∣d^∣∣=1)
- L L L 为射线最大长度
该方程定义了一个从起点出发沿指定方向无限延伸的几何线段,实际应用中通常限制 t t t的范围以提高检测效率。
1.1.2 坐标系变换原理
Bullet3内部使用世界坐标系进行碰撞计算。当物体存在刚体变换时,射线需要转换到物体的局部坐标系:
r ′ ( t ) = M − 1 ( o + t d ^ − T ) \mathbf{r}'(t) = \mathbf{M}^{-1}(\mathbf{o} + t\mathbf{\hat{d}} - \mathbf{T}) r′(t)=M−1(o+td^−T)
其中:
- M \mathbf{M} M 为物体的旋转矩阵
- T \mathbf{T} T 为平移向量
这种变换使得与复杂形状(如凸包、网格)的相交测试可以在局部空间高效完成。
1.1.3 形状相交测试
不同几何形状的相交检测算法差异显著:
形状类型 | 检测算法 | 时间复杂度 |
---|---|---|
球体 | 代数法解二次方程 | O(1) |
平面 | 平面方程代入 | O(1) |
凸包 | GJK算法 | O(n) |
三角网格 | Moller-Trumbore算法 | O(k) |
其中 n n n为凸包顶点数, k k k为遍历的三角形数量。
1.2 碰撞检测流程
1.2.1 Broadphase阶段
采用层次包围体(Bounding Volume Hierarchy)加速结构:
- 构建动态AABB树,每个节点存储物体的轴对齐包围盒
- 通过射线与AABB的快速相交测试筛选候选物体
- 使用栈结构进行深度优先遍历,算法复杂度从O(N)降至O(log N)
AABB相交测试公式:
{ t m i n = max ( b m i n x − o x d x , b m i n y − o y d y , b m i n z − o z d z ) t m a x = min ( b m a x x − o x d x , b m a x y − o y d y , b m a x z − o z d z ) \begin{cases} t_{min} = \max\left(\frac{b_{min}^x - o^x}{d^x}, \frac{b_{min}^y - o^y}{d^y}, \frac{b_{min}^z - o^z}{d^z}\right) \\ t_{max} = \min\left(\frac{b_{max}^x - o^x}{d^x}, \frac{b_{max}^y - o^y}{d^y}, \frac{b_{max}^z - o^z}{d^z}\right) \end{cases} ⎩ ⎨ ⎧tmin=max(dxbminx−ox,dybminy−oy,dz