“在三维空间的迷宫中,谁能判断光线会不会穿越那方方正正的盒子?答案藏在 Slab 的神殿中——只要三次乘法,你就能获得预言。”
—— 伽玛·冯·图形学,第十卷,浮点纪元。
🧭 引子:什么是“盒子求交”?
在计算机图形学的世界中,我们常需要判断:
“一条光线是否撞上了某个盒子?”
这在很多场景中都至关重要,比如:
- 射线拾取(Ray Picking)
- 碰撞检测
- 光线追踪中的加速结构(BVH、KD-Tree)
而我们讨论的这个“盒子”,通常是 轴对齐包围盒(AABB, Axis-Aligned Bounding Box) ,就是说它永远不歪头,不斜眼,乖乖地沿着 XYZ 轴站着。
🎩 Slab 法的魔法:空间切片的哲学
Slab 这个词的本意是“厚板、层板”的意思,在这里,它指的是——
在每个坐标轴上由两个平面夹起来的一层空间区域
换句话说,一个 3D AABB,可以看成是 X、Y、Z 三个 Slab 的交集。
⛓️ 举个例子:
假设你有一个盒子:
const boxMin = { x: -1, y: -1, z: -1 };
const boxMax = { x: 1, y: 1, z: 1 };
这表示这个盒子从 -1 到 1,在每个坐标轴上都占有一席之地。
一条光线:
const origin = { x: 0, y: 0, z: -5 }; // 从盒子前面出发
const direction = { x: 0, y: 0, z: 1 }; // 沿 Z 正方向射出
现在问题是:这条光线,会穿过这个盒子吗?Slab 法说:“只需三次乘法,我就告诉你。”
⚙️ Slab 法原理:拿时间去切割空间
我们先抽象下光线的定义:
光线 = 起点 + t × 方向
对于某个轴,比如 X 轴:
x(t) = origin.x + t * direction.x
我们要找出 t 的范围,使得这个 t 落在盒子的 X、Y、Z 范围内。
在每个轴上:
- 计算 ray 和两个 slab 面(min、max)的相交时间(t 值)
- 找到所有轴中进入的最大 t 和退出的最小 t
- 如果这两个值交叉了,恭喜你!光线穿过盒子!
🧠 实现:Slab 法代码 JavaScript 版
function intersectRayAABB(origin, direction, boxMin, boxMax) {
let tMin = -Infinity;
let tMax = Infinity;
for (const axis of ['x', 'y', 'z']) {
const o = origin[axis];
const d = direction[axis];
const min = boxMin[axis];
const max = boxMax[axis];
if (Math.abs(d) < 1e-8) {
// 光线与轴平行
if (o < min || o > max) return false; // 永远不可能进入
} else {
const t1 = (min - o) / d;
const t2 = (max - o) / d;
const tEnter = Math.min(t1, t2);
const tExit = Math.max(t1, t2);
tMin = Math.max(tMin, tEnter);
tMax = Math.min(tMax, tExit);
}
}
return tMax >= tMin && tMax >= 0;
}
🧪 示例调用:
const boxMin = { x: -1, y: -1, z: -1 };
const boxMax = { x: 1, y: 1, z: 1 };
const origin = { x: 0, y: 0, z: -5 };
const direction = { x: 0, y: 0, z: 1 };
console.log(intersectRayAABB(origin, direction, boxMin, boxMax)); // true ✅
🧾 核心步骤回顾
- 每轴计算进入 / 退出 t 值
- 更新全局 tMin(最大进入)和 tMax(最小退出)
- 如果 tMin > tMax 或 tMax < 0 → 没交集
📈 为什么叫“三次乘法”?
这是因为最核心的计算是:
(min - origin) / direction
(max - origin) / direction
对于每个轴,做一次乘除操作(可变形为乘法:除以 d 相当于乘以 1/d),所以我们说:三次乘法决定交与否。
🎭 若你用
invDir = 1 / dir预计算后重用,甚至可以避免除法开销,快得像穿墙!
💡 延伸思考
-
✅ 光线可能从内部射出吗?
- 当然可以,tMin < 0 但 tMax > 0,依然命中。
-
✅ 如何返回相交的最近点?
- 直接返回 tMin 即可:
intersection = origin + direction * tMin
- 直接返回 tMin 即可:
-
✅ 可以用在 BVH、Octree 中?
- 完美适配,简洁又高效!
🧙 一句话总结
Slab 法就像一位空间裁缝,它不问光线的情感,只根据三条正义之轴,决定命运的交点是否存在。
“你看似在检查盒子,实则盒子也在衡量你。”
—— 图形学圣经·BVH卷·Slab章