🌌 计算机图形学奇谈

OBB + 分离轴定理(SAT)的碰撞检测之术

"世界本无碰撞,皆因空间不分。" ------ 图形学祖师爷·虚构版


🧠 引子:什么是 OBB?

我们都听说过 AABB(轴对齐包围盒),它就像你搬家打包用的纸箱------永远正对 X、Y、Z 轴。但 OBB(Oriented Bounding Box)不同,它像旋转过的收纳箱,可以适应物体的姿态,不拘泥于坐标轴的秩序。

OBB 的基本构成是:

  • 一个中心点(Center)
  • 三个正交单位向量(代表盒子的局部 X、Y、Z 轴方向)
  • 每个方向上的"半长"(half-length)

所以,它不仅会转,还会摆,会扭,堪称三维空间中的"瑜伽高手"。


💥 问题:两个 OBB 怎么判断是否碰撞?

面对两个旋转中的物体,光靠目视显然不靠谱------你需要一位判官,它的名字叫:

🧪 分离轴定理(Separating Axis Theorem)

它告诉我们一句真理:

"若存在一条轴,使两个物体在该轴上的投影不重叠 ,则它们没有碰撞。"

换句话说,想知道两个盒子有没有撞上,只需要:

  • 找出所有可能的分离轴
  • 把两个盒子在这些轴上投影
  • 看看有没有哪一条轴上的投影互不相交

如果有 → 安然无恙

如果没有 → 撞上了!


🔍 分离轴都有哪些?

对两个 OBB(记作 A 和 B),我们必须检验 15 条轴

  1. A 的局部 3 轴:A₁, A₂, A₃
  2. B 的局部 3 轴:B₁, B₂, B₃
  3. A×B 的轴对组合叉积(9 条):A₁×B₁, A₁×B₂, ..., A₃×B₃

这些轴,足以穿透一切迷雾,找出可能的分离空间。


🧮 实现步骤(JavaScript)

以下是一个简化的伪代码逻辑(实战中建议搭配 gl-matrixthree.js 的向量类):

scss 复制代码
function isOBBCollision(boxA, boxB) {
  const axes = [];

  // 1. 添加 A 和 B 的局部坐标轴
  axes.push(...boxA.axes);
  axes.push(...boxB.axes);

  // 2. 添加各对轴的叉积
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      const cross = boxA.axes[i].clone().cross(boxB.axes[j]);
      if (cross.lengthSq() > 1e-6) { // 排除零向量
        axes.push(cross.normalize());
      }
    }
  }

  // 3. 检查所有分离轴
  for (const axis of axes) {
    if (!overlapOnAxis(boxA, boxB, axis)) {
      return false; // 找到了分离轴,碰撞失败
    }
  }

  return true; // 所有轴都有重叠,发生碰撞
}

关键的 overlapOnAxis

ini 复制代码
function overlapOnAxis(boxA, boxB, axis) {
  const projA = projectOBB(boxA, axis);
  const projB = projectOBB(boxB, axis);

  return projA.max >= projB.min && projB.max >= projA.min;
}

function projectOBB(box, axis) {
  // 计算中心点在轴上的投影
  const centerProj = box.center.dot(axis);

  // 计算半径:把每个轴长度乘以在投影轴上的投影
  let radius = 0;
  for (let i = 0; i < 3; i++) {
    const lengthOnAxis = Math.abs(box.axes[i].dot(axis)) * box.halfLengths[i];
    radius += lengthOnAxis;
  }

  return {
    min: centerProj - radius,
    max: centerProj + radius
  };
}

⚙️ OBB 的结构定义

arduino 复制代码
const box = {
  center: new Vector3(0, 0, 0),
  axes: [ // 本地 x, y, z 三个单位向量
    new Vector3(1, 0, 0),
    new Vector3(0, 1, 0),
    new Vector3(0, 0, 1),
  ],
  halfLengths: [1, 2, 1] // 半长沿 x, y, z
};

你可以使用 THREE.Vector3 替代以上的向量类型,实际工程中建议封装成类。


🚀 性能优化建议

  • 减少检测频率:动态物体之间使用"宽相碰撞"先过滤(如 Sphere 检查)。
  • 分帧检测 :利用 requestIdleCallback 或 Web Worker 分担部分计算。
  • 批量合并检测:适用于密集体积群组的整体处理。

🎭 彩蛋:用文学讲述碰撞

想象两个宇宙飞船,漂浮在三维太空中。它们不是矩形,不是球体,而是由工程师用 OBB 封装的三维实体。若它们交错而过,分离轴间一丝不交,那便是和平共处;若它们十五轴皆重,那便是命运的拥抱------一次壮烈的相撞。


🧩 总结

概念 说明
OBB 有向包围盒,可旋转的碰撞检测体
SAT 判断碰撞是否发生的算法核心定理
分离轴数量 OBB 对间共 15 条轴需判断
判断标准 任一轴无投影重叠 → 无碰撞
应用场景 精确三维碰撞检测、刚体模拟、AI导航

"图形学不仅是三角和像素,它是数字宇宙的哲学。"

------ 某无名开发者,在调试 SAT 崩溃后如此感慨


如何实现一个完整的三维 OBB-SAT 碰撞检测库、搭配 Three.js 动画演示?

相关推荐
顾安r1 小时前
11.8 脚本网页 星际逃生
c语言·前端·javascript·flask
Hello.Reader1 小时前
Data Sink定义、参数与可落地示例
java·前端·网络
im_AMBER2 小时前
React 17
前端·javascript·笔记·学习·react.js·前端框架
一雨方知深秋2 小时前
2.fs模块对计算机硬盘进行读写操作(Promise进行封装)
javascript·node.js·promise·v8·cpython
谷歌开发者3 小时前
Web 开发指向标 | Chrome 开发者工具学习资源 (六)
前端·chrome·学习
一晌小贪欢3 小时前
【Html模板】电商运营可视化大屏模板 Excel存储 + 一键导出(已上线-可预览)
前端·数据分析·html·excel·数据看板·电商大屏·大屏看板
发现你走远了3 小时前
连接模拟器网页进行h5的调试(使用Chrome远程调试(推荐)) 保姆级图文
前端·chrome
街尾杂货店&4 小时前
css - 实现三角形 div 容器,用css画一个三角形(提供示例源码)简单粗暴几行代码搞定!
前端·css
顺凡4 小时前
删一个却少俩:Antd Tag 多节点同时消失的原因
前端·javascript·面试
小白路过4 小时前
CSS transform矩阵变换全面解析
前端·css·矩阵