如何检测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个轴)
- Box3的三个坐标轴(x、y、z轴,共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
相关产品推荐
相关产品推荐

