three求交算法对比BVH、AABB、raycaster
slab:把 3D 相交问题拆成 3 个一维区间问题,然后求区间交集
1.快速剔除(最重要)
2.提供进入/离开信息(还能推导:1.命中哪个面,2.交点位置)
3.作为加速结构基础
BVH(层级包围盒)
KD-Tree
八叉树(Octree)
Raycaster 优化
流程:
P(t) = origin + t * direction
//盒子范围:x ∈ [min, max]
//射线:
x(t) = ox + t * dx
//解一个不等式:
min ≤ ox + t·dx ≤ max
//得到:
t ∈ [tNearX, tFarX]
//同理:
y → [tNearY, tFarY]
z → [tNearZ, tFarZ]
//关键:三个轴的区间取交集
tMin = max(tNearX, tNearY, tNearZ)
tMax = min(tFarX, tFarY, tFarZ)
//相交判断
如果 tMin ≤ tMax → 相交
否则 → 不相交
//直觉图(你可以这样想)
x轴允许时间: [------]
y轴允许时间: [--------]
z轴允许时间: [----]
交集: [----] ← 只有这一段同时满足三个轴
slab 不去算“交点”,而是问:
哪些 t 的取值,让点仍然在盒子里?
AABB(slab):问题降级:对比raycaster,只取了前置部分(AABB检测)规避了后续的三角面检测节约成本
raycaster:先进性aabb判断,然后进行三角面检测,获取精确命中的结果
BVH的优化点:分层包围盒 + slab 快速剔除,大幅减少需要做精确相交计算的数量
slab 的真正职责
快速排除(culling),而不是精确求交
它回答的是:
这个盒子有没有可能被射线击中?
而不是:
击中了哪个三角形?
raycaster:
Raycaster 在做什么(对比)
Raycaster =
粗检测(bounding)
↓
精确检测(triangle)
没有 BVH 时发生什么
假设场景:
一个模型有 10000 个三角形
Raycaster(无加速):
for 每个三角形:
做 Ray-Triangle 相交
👉 复杂度:
O(N) = 10000 次
有 BVH 后(核心变化)
BVH 把几何体组织成一棵树:
[大盒子]
/ \
[子盒子] [子盒子]
/ \ / \
... ... ... ...
raycast 流程(BVH重点)
每一步都在做:
Ray vs AABB(slab)
🎯 流程图
Ray →
[Root AABB] ✔
↓
[Left] ❌ ← 被剔除
[Right] ✔
↓
[Leaf AABB] ✔
↓
Triangle(才精确计算)
AABB示例:立方体求相交面:
function intersectUnitCube(ray) {
const min = -0.5;
const max = 0.5;
let tMin = -Infinity;
let tMax = Infinity;
let entryAxis = -1;
let entrySign = 0;
let exitAxis = -1;
let exitSign = 0;
for (let axis = 0; axis < 3; axis += 1) {
const origin = ray.origin.getComponent(axis);
const direction = ray.direction.getComponent(axis);
if (Math.abs(direction) < 1e-8) {
if (origin < min || origin > max) return null;
continue;
}
let near = (min - origin) / direction;
let far = (max - origin) / direction;
let nearSign = -1;
let farSign = 1;
console.log((near - far) * direction, direction);
if (near > far) {
[near, far] = [far, near];
[nearSign, farSign] = [farSign, nearSign];
}
if (near > tMin) {
tMin = near;
entryAxis = axis;
entrySign = nearSign;
}
if (far < tMax) {
tMax = far;
exitAxis = axis;
exitSign = farSign;
}
if (tMin > tMax) return null;
}
const t = tMin >= 0 ? tMin : tMax;
if (t < 0) return null;
const axis = tMin >= 0 ? entryAxis : exitAxis;
const sign = tMin >= 0 ? entrySign : exitSign;
return {
distance: t,
faceIndex: faceIndexFromAxisSign(axis, sign),
point: ray.at(t, tempVectorC),
};
}
function faceIndexFromAxisSign(axis, sign) {
if (axis === 0) return sign > 0 ? 2 : 3;
if (axis === 1) return sign > 0 ? 4 : 5;
return sign > 0 ? 0 : 1;
}

浙公网安备 33010602011771号