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;
      }

 

posted @ 2026-05-06 13:36  SimoonJia  阅读(1)  评论(0)    收藏  举报