不能直接用aabb的min/max比较,因为obb是旋转后的长方体,坐标轴不与世界坐标系对齐,投影到世界轴会漏判或误判;需用分离轴定理(sat),检验10个关键轴(3+3+4)上的投影重叠,结合中心差与投影半长公式高效判断,并注意数值稳定性与退化处理。

为什么不能直接用AABB的min/max比较
因为OBB(定向包围盒)是旋转后的长方体,坐标轴不与世界坐标系对齐,所以不能像AABB那样简单比对min_x和max_x。直接投影到世界轴上判断会漏判或误判——比如两个斜着相交但投影不重叠的盒子。
分离轴定理(SAT)是核心解法
OBB相交等价于:在所有可能的分离轴方向上,两个OBB的投影都存在重叠。OBB有3个局部轴(自身坐标系的x/y/z),两两叉乘可得额外9个潜在分离轴(3×3),共15个方向需要检验。实际只需检查15个中的10个:3个来自box A的轴、3个来自box B的轴、4个来自两组轴两两叉乘的唯一非零方向(去掉平行或零向量)。
- 每条轴上,把8个顶点分别投影到该轴,取最小/最大值得到两个投影区间
- 用
std::abs(dot(axis, center_diff)) 快速判断是否重叠 - 一旦某轴上无重叠,立即返回
false - 注意浮点误差:用
std::abs加小阈值(如1e-6f)避免误判
如何高效获取OBB的8个顶点或投影半长
不建议实时生成8个顶点再投影——计算量大且易出错。更优做法是:每个OBB维护其中心center、三个正交单位轴u/v/w、以及沿各轴的半长extents[3]。投影到任一单位轴n时,投影半长为std::abs(dot(n, u)) * extents[0] + std::abs(dot(n, v)) * extents[1] + std::abs(dot(n, w)) * extents[2]。
- 这个公式本质是Minkowski差在
n方向上的支撑函数值 - 避免sqrt和归一化:所有轴保持单位向量,
dot结果直接可用 - 若OBB由变换矩阵给出,需先提取
u/v/w(矩阵前3列)并单位化
常见错误:忽略数值稳定性与退化情况
当两个OBB共面或某轴几乎平行时,叉乘结果接近零向量,直接归一化会触发NaN或除零。必须提前检查:
- 对每个叉乘轴
cross(u_i, v_j),先算len = length(cross),若len 则跳过该轴 - 不要用
std::acos求夹角来过滤——精度差且慢,直接用长度判断更稳 - 如果OBB退化为平面或线段(某个
extents[i] == 0),对应轴的投影半长为0,公式仍成立
真正麻烦的是旋转矩阵本身病态(比如缩放+旋转混合导致轴不正交),这时应先用QR分解或极分解做正交化,否则u/v/w不构成标准正交基,投影公式失效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











