如何检测两个非索引BufferGeometry不同面的相交情况?
我来帮你解决这个问题,你遇到的情况其实是Raycaster在处理自身几何体和非索引面时的几个常见坑,咱们一步步来梳理解决方案:
问题根源分析
你的代码思路没问题,但有两个关键问题导致Raycaster没起作用:
- 射线起点刚好在当前面上,Raycaster会因为深度测试精度问题忽略这个面,同时如果法线向外,射线根本射不到几何体内部的其他面;
- 检测自身Mesh时,默认可能会返回当前发射射线的面,需要手动过滤。
解决方案1:修复Raycaster检测逻辑
调整射线起点和方向,再过滤当前面,就能正确检测到非索引几何体的面相交:
var box = new THREE.Mesh(geometry.toNonIndexed(), material); var raycaster = new THREE.Raycaster(); var intersects = []; var pos = box.geometry.attributes.position; var ori = new THREE.Vector3(); var dir = new THREE.Vector3(); var a = new THREE.Vector3(), b = new THREE.Vector3(), c = new THREE.Vector3(), tri = new THREE.Triangle(); var faces = pos.count / 3; for (let i = 0; i < faces; i++) { a.fromBufferAttribute(pos, i * 3 + 0); b.fromBufferAttribute(pos, i * 3 + 1); c.fromBufferAttribute(pos, i * 3 + 2); tri.set(a, b, c); tri.getMidpoint(ori); tri.getNormal(dir); // 关键1:反转法线方向,让射线射向几何体内部(如果检测内部相交) dir.negate(); // 关键2:微小偏移起点,避免射线起点与当前面重合,解决精度问题 ori.add(dir.multiplyScalar(0.001)); raycaster.set(ori, dir); intersects = raycaster.intersectObject(box, true); // 过滤掉当前发射射线的面,只保留其他面的相交结果 const validIntersects = intersects.filter(intersect => intersect.faceIndex !== i); if (validIntersects.length > 0) { console.log(`面 ${i} 与面 ${validIntersects[0].faceIndex} 相交`, validIntersects[0]); } }
解决方案2:直接检测三角面相交(更可靠,适合面数少的场景)
如果你的几何体面数不多,可以跳过Raycaster,直接用Three.js的Triangle.intersectsTriangle方法检测两两三角面是否相交,这种方式更直接,没有射线检测的精度问题:
var box = new THREE.Mesh(geometry.toNonIndexed(), material); var pos = box.geometry.attributes.position; var tri1 = new THREE.Triangle(); var tri2 = new THREE.Triangle(); var a1 = new THREE.Vector3(), b1 = new THREE.Vector3(), c1 = new THREE.Vector3(); var a2 = new THREE.Vector3(), b2 = new THREE.Vector3(), c2 = new THREE.Vector3(); var faces = pos.count / 3; // 遍历所有两两不同的面组合,避免重复检测 for (let i = 0; i < faces; i++) { a1.fromBufferAttribute(pos, i * 3 + 0); b1.fromBufferAttribute(pos, i * 3 + 1); c1.fromBufferAttribute(pos, i * 3 + 2); tri1.set(a1, b1, c1); for (let j = i + 1; j < faces; j++) { a2.fromBufferAttribute(pos, j * 3 + 0); b2.fromBufferAttribute(pos, j * 3 + 1); c2.fromBufferAttribute(pos, j * 3 + 2); tri2.set(a2, b2, c2); if (tri1.intersectsTriangle(tri2)) { console.log(`面 ${i} 与面 ${j} 相交`); } } }
方案选择建议
- 如果你的几何体面数较少(几百面以内),优先选三角面直接检测,逻辑简单且精度高;
- 如果面数较多(上千面),选Raycaster方案,时间复杂度更低,效率更高。
内容的提问来源于stack exchange,提问作者vinkovsky
相关产品推荐
相关产品推荐

