AABB是各边平行于坐标轴的矩形或长方体包围盒,其碰撞检测快是因为仅需6次(3D)或4次(2D)浮点比较,无需旋转、叉乘或点积;任一轴无重叠即返回false。

什么是 AABB,为什么它比其他包围盒快 AABB(Axis-Aligned Bounding Box)就是各边平行于坐标轴的矩形(2D)或长方体(3D)包围盒。它的碰撞检测快,根本原因就一条:**不需要旋转、不涉及向量叉乘或点积,只做 6 次(3D)或 4 次(2D)浮点比较**。只要两个 AABB 在每个轴上投影都有重叠,它们就相交;任一轴无重叠,立即返回 false。
实际中别用 std::array<float></float> 或六个独立成员手动管理上下界——容易写错顺序、难复用。推荐封装成结构体,显式暴露 min 和 max 成员(都是 glm::vec3 或自定义 Vec3),这样语义清晰,也方便后续扩展(比如加入 center()、extents() 等辅助函数)。
怎么写一个零开销的 AABB::intersects() 函数
核心逻辑是逐轴判断区间是否重叠:对每个轴 i,检查 a.min[i] 。注意不是“中心距小于半宽和”,那是球体思路;AABB 必须用区间重叠判据,否则会漏检(尤其当盒子尺寸差异大时)。
<p>实操建议:</p>
<ul>
<li>用 <code>constexpr 实现,让编译器内联并常量折叠(如果输入是字面量)
const AABB&,别传值;成员变量用 float 而非 double,除非你真需要双精度glm,可借助 glm::all() + glm::lessThanEqual(),但要注意它生成的是向量化指令,调试时可能难断点;手写展开更可控struct AABB {
glm::vec3 min;
glm::vec3 max;
constexpr bool intersects(const AABB& other) const {
return (min.x <= other.max.x) && (other.min.x <= max.x) &&
(min.y <= other.max.y) && (other.min.y <= max.y) &&
(min.z <= other.max.z) && (other.min.z <= max.z);
}
};
构建 AABB 时最容易踩的坑:空盒与 NaN 传播
空盒指 min > max(例如初始化时只设了 min 没设 max,或插入点时没更新)。一旦出现,intersects() 会恒返回 true(因为 NaN 是 false,但更常见的是逻辑反转导致误判)。而 NaN 往往来自除零、<code>sqrt(-1) 或未初始化的 float 成员。
防御性做法:
- 构造函数里用
std::numeric_limits<float>::max()</float>初始化min,用std::numeric_limits<float>::lowest()</float>初始化max - 提供
isValid()辅助函数:return min.x - 在关键路径(如物理步进前)加
assert(aabb.isValid());,开发期快速暴露问题 - 别依赖
glm::aabb的默认构造——它不保证初始化,且不同版本行为可能不一致
和 BVH 配合时,AABB 的内存布局影响很大
如果你把 AABB 塞进 BVH 节点,别把 min 和 max 拆成两个独立数组(SoA),也别混着其他字段(如子节点索引)交错存储(AoS 过度分散)。最佳实践是单个节点结构体保持紧凑,例如:
struct BVHNode {
AABB bounds; // 6×float → 24 字节
uint32_t left; // 4 字节
uint32_t right; // 4 字节
uint32_t primOffset; // 4 字节(若为叶子)
uint32_t primCount; // 4 字节
}; // 总共 40 字节,刚好缓存行对齐
这样遍历时 CPU 可以预取整个节点,bounds 访问不会跨缓存行。若把 min 全放一块、max 全放另一块,一次相交检测就要触发两次非连续内存访问,实测在大规模场景下性能跌 15% 以上。
立即学习“C++免费学习笔记(深入)”;
AABB 本身很简单,但嵌入真实管线后,初始化逻辑、内存排布、NaN 防御这些细节,才是决定它到底快不快的关键。


















