THREE.js从raycaster相交点检测相邻面

时间:2019-01-23 16:11:58

标签: javascript algorithm three.js 3d face

我有一个用Mesh创建的BufferGeometry。 我还使用Mesh获得了鼠标与Raycaster相交的位置的坐标。

我正在尝试检测相交点以内(并触摸)半径之内的人脸。 一旦检测到“相切”的面孔,便要给面孔着色。因为我正在使用BufferGeometry,所以我正在操纵几何体上的缓冲区属性。

这是我的代码:

let vertexA;
let vertexB;
let vertexC;
let intersection;
const radius = 3;
const color = new THREE.Color('red');
const positionsAttr = mesh.geometry.attributes.position;
const colorAttr = mesh.geometry.attributes.color;

// on every mouseMove event, do below:

vertexA = new THREE.Vector3();
vertexB = new THREE.Vector3();
vertexC = new THREE.Vector3();
intersection = raycaster.intersectObject(mesh).point;

// function to detect tangent edge
function isEdgeTouched(v1, v2, point, radius) {
  const line = new THREE.Line3();
  const closestPoint = new THREE.Vector3();
  line.set(v1, v2);
  line.closestPointToPoint(point, true, closestPoint);
  return point.distanceTo(closestPoint) < radius;
}

// function to color a face
function colorFace(faceIndex) {
  colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
  colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
  colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
  colorAttr.needsUpdate = true;
}


// iterate over each face, color it if tangent
for (let i=0; i < (positionsAttr.count) /3); i++) {
  vertexA.fromBufferAttribute(positionsAttr, i * 3 + 0);
  vertexB.fromBufferAttribute(positionsAttr, i * 3 + 1);
  vertexC.fromBufferAttribute(positionsAttr, i * 3 + 2);
  if (isEdgeTouched(vertexA, vertexB, point, radius)
    || isEdgeTouched(vertexA, vertexB, point, radius)
    || isEdgeTouched(vertexA, vertexB, point, radius)) {
    colorFace(i);
}

尽管此代码有效,但是它的性能似乎非常差,尤其是当我使用具有许多面的几何图形时。当我在Chrome DevTools上检查性能监视器时,我注意到isEdgeTouchedcolorFace函数在每次迭代中都占用过多的时间。

是否有一种方法可以改进此算法,或者有更好的算法来检测相邻的人脸?

编辑

我从THREE.js松弛通道获得了一些帮助,并修改了算法以使用Three的Sphere。我现在不再进行“边缘”检测,而是检查人脸是否在Sphere

之内

下面的更新代码:

const sphere = new THREE.Sphere(intersection, radius);

// now checking if each vertex of a face is within sphere
// if all are, then color the face at index i
for (let i=0; i < (positionsAttr.count) /3); i++) {
  vertexA.fromBufferAttribute(positionsAttr, i * 3 + 0);
  vertexB.fromBufferAttribute(positionsAttr, i * 3 + 1);
  vertexC.fromBufferAttribute(positionsAttr, i * 3 + 2);
  if (sphere.containsPoint(vertexA)
    && sphere.containsPoint(vertexA)
    && sphere.containsPoint(vertexA)) {
    colorFace(i);
}

当我在应用程序中对此进行测试时,我注意到性能肯定比以前的版本有所提高。但是,我仍然想知道是否可以进一步改善这一点。

1 个答案:

答案 0 :(得分:0)

这似乎是一个经典的最近邻问题。

通过为网格(例如AABB树)建立边界体积层次(BVH),可以非常快速地找到最接近给定点的三角形,从而缩小搜索范围。

BVH: https://en.m.wikipedia.org/wiki/Bounding_volume_hierarchy

AABB树: https://www.azurefromthetrenches.com/introductory-guide-to-aabb-tree-collision-detection/

然后,您可以使用球形或给定半径的框针对BVH查询范围查询。这相当于使用球体/框“查询”遍历BVH,该球/框“查询”用于快速,非常早地丢弃不会裁剪球/框“查询”的边界体积节点。最后,仅使用BV与球体/盒子“查询”相交的三角形进行实际距离或相交测试,通常只占三角形的一小部分。

针对BVH的查询的复杂度为O(log n),而您的方法为O(n)。