You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测THREE.Triangle与THREE.Box3是否相交?

THREE.js中Triangle与Box3的重叠检测实现

Hey there! 很高兴能帮你解决这个问题。首先明确一点:THREE.js核心库目前没有提供直接用于检测THREE.Triangle和THREE.Box3是否重叠的现成函数,不过我们可以基于THREE.js已有的API,或者用标准的碰撞检测算法来实现这个功能。

下面给你两种实现思路,从简单直观到严谨精确,你可以根据场景需求选择:

一、基础实现(适合大多数常规场景)

这种方法基于顶点检测和边相交检测,逻辑简单易懂,能覆盖绝大多数常见的重叠情况:

步骤1:快速排斥预检测

先获取三角形的包围盒,和目标Box3做初步相交判断——如果两个包围盒都不相交,那三角形和Box3肯定没有重叠,直接返回结果即可:

function triangleIntersectsBox(triangle, box) {
  // 生成三角形的包围盒
  const triBox = new THREE.Box3();
  triBox.setFromPoints(triangle.vertices);
  
  // 包围盒不相交,直接返回false
  if (!box.intersectsBox(triBox)) return false;
  
  // 后续检测逻辑...
}

步骤2:检测三角形顶点是否在盒内

如果三角形的任意一个顶点位于Box3内部,说明两者必然重叠:

// 遍历三角形的三个顶点,检查是否在盒子内
for (const vertex of triangle.vertices) {
  if (box.containsPoint(vertex)) {
    return true;
  }
}

步骤3:检测盒子顶点是否在三角形内

反过来,如果Box3的8个顶点中有任意一个落在三角形内部,也说明两者重叠:

// 获取盒子的8个顶点
const boxVertices = [];
box.getCorners(boxVertices);

for (const v of boxVertices) {
  if (triangle.containsPoint(v)) {
    return true;
  }
}

步骤4:检测边与对方形状相交

最后检查两种边相交的情况:

  • 三角形的每条边是否与Box3相交(用THREE.js内置的Box3.intersectsSegment方法)
  • Box3的每条边是否与三角形相交(用THREE.js的Triangle.intersectsSegment方法)
// 检测三角形的边与盒子相交
const triEdges = [
  [triangle.vertices[0], triangle.vertices[1]],
  [triangle.vertices[1], triangle.vertices[2]],
  [triangle.vertices[2], triangle.vertices[0]]
];

for (const [start, end] of triEdges) {
  if (box.intersectsSegment(start, end)) {
    return true;
  }
}

// 检测盒子的边与三角形相交
const boxEdges = [
  [boxVertices[0], boxVertices[1]], [boxVertices[1], boxVertices[3]], [boxVertices[3], boxVertices[2]], [boxVertices[2], boxVertices[0]],
  [boxVertices[4], boxVertices[5]], [boxVertices[5], boxVertices[7]], [boxVertices[7], boxVertices[6]], [boxVertices[6], boxVertices[4]],
  [boxVertices[0], boxVertices[4]], [boxVertices[1], boxVertices[5]], [boxVertices[2], boxVertices[6]], [boxVertices[3], boxVertices[7]]
];

for (const [start, end] of boxEdges) {
  if (triangle.intersectsSegment(start, end)) {
    return true;
  }
}

// 所有检测都未命中,说明不重叠
return false;

把这些步骤整合起来,就是一个完整的基础检测函数。

二、严谨实现:分离轴定理(SAT)

如果需要处理更极端的边界情况(比如三角形完全包裹盒子、盒子完全包裹三角形),可以使用分离轴定理(SAT)——这是判断凸形状相交的标准算法,核心逻辑是:如果存在一个轴,两个形状在该轴上的投影区间没有重叠,则形状不相交;否则相交。

对于Triangle和Box3(轴对齐包围盒),需要检查的分离轴包括:

  1. 三角形所在平面的法向量(1个轴)
  2. Box3的三个坐标轴(x、y、z轴,共3个轴)
  3. 三角形的每条边与Box3的每条边的叉乘结果(共3×3=9个轴)

以下是简化的SAT实现:

// 计算一组点在指定轴上的投影区间
function projectOntoAxis(points, axis) {
  let min = Infinity;
  let max = -Infinity;
  for (const p of points) {
    const dot = p.dot(axis);
    min = Math.min(min, dot);
    max = Math.max(max, dot);
  }
  return { min, max };
}

// 判断两个投影区间是否重叠
function intervalsOverlap(aMin, aMax, bMin, bMax) {
  return aMin <= bMax && bMin <= aMax;
}

function triangleIntersectsBoxSAT(triangle, box) {
  const triVerts = triangle.vertices;
  const boxVerts = [];
  box.getCorners(boxVerts);
  
  // 1. 检查三角形平面的法向量轴
  const triNormal = triangle.getNormal(new THREE.Vector3());
  const triProj = projectOntoAxis(triVerts, triNormal);
  const boxProj = projectOntoAxis(boxVerts, triNormal);
  if (!intervalsOverlap(triProj.min, triProj.max, boxProj.min, boxProj.max)) {
    return false;
  }
  
  // 2. 检查Box3的三个坐标轴
  const axes = [new THREE.Vector3(1,0,0), new THREE.Vector3(0,1,0), new THREE.Vector3(0,0,1)];
  for (const axis of axes) {
    const triProj = projectOntoAxis(triVerts, axis);
    const boxProj = projectOntoAxis(boxVerts, axis);
    if (!intervalsOverlap(triProj.min, triProj.max, boxProj.min, boxProj.max)) {
      return false;
    }
  }
  
  // 3. 检查三角形边与盒子边的叉乘轴
  const triEdges = [
    triVerts[1].clone().sub(triVerts[0]),
    triVerts[2].clone().sub(triVerts[1]),
    triVerts[0].clone().sub(triVerts[2])
  ];
  const boxEdges = [
    boxVerts[1].clone().sub(boxVerts[0]), boxVerts[3].clone().sub(boxVerts[1]), boxVerts[0].clone().sub(boxVerts[2]),
    boxVerts[5].clone().sub(boxVerts[4]), boxVerts[7].clone().sub(boxVerts[5]), boxVerts[4].clone().sub(boxVerts[6]),
    boxVerts[4].clone().sub(boxVerts[0]), boxVerts[5].clone().sub(boxVerts[1]), boxVerts[6].clone().sub(boxVerts[2])
  ];
  
  for (const triEdge of triEdges) {
    for (const boxEdge of boxEdges) {
      const axis = new THREE.Vector3().crossVectors(triEdge, boxEdge).normalize();
      // 跳过零向量(当两条边平行时)
      if (axis.lengthSq() < 0.0001) continue;
      const triProj = projectOntoAxis(triVerts, axis);
      const boxProj = projectOntoAxis(boxVerts, axis);
      if (!intervalsOverlap(triProj.min, triProj.max, boxProj.min, boxProj.max)) {
        return false;
      }
    }
  }
  
  // 所有轴的投影都重叠,说明相交
  return true;
}

这个实现能处理所有边界情况,适合对检测精度要求较高的场景。


内容的提问来源于stack exchange,提问作者tomacco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:01:47