Three.js - 鼠标移动时推开并恢复元素位置

4

我想要实现的样例

大家好,我正在开发一个基于Three.js的项目,用户可以悬停在一个镶嵌面上,每个网格应该在与不可见球体相交时被推开,并在离开其区域时回到原始位置。我参考了这个这个示例,但由于不知道如何在Three.js中使其工作而卡住了。

在我的代码中,我能够悬停在网格的每个面上,将其变为红色并在鼠标移开后恢复其原始颜色。但我无法用类似的方式控制位置。这是我认为问题所在的代码部分:

if (intersects.length > 0) {

    // if the closest object intersected is not the currently stored intersection object
    if (intersects[0].object != INTERSECTED) {

        // Restore previous intersection objects (if they exist) to their original color
        if (INTERSECTED) {
            INTERSECTED.face.color.setHex(INTERSECTED.currentHex);
        }

        // Intersected elements
        INTERSECTED = intersects[0];
        var intGeometry = INTERSECTED.object.geometry;
        var intFace     = INTERSECTED.face;

        // Difference between intersected faces and geometry
        INTERSECTED.currentHex = intFace.color.getHex();
        intFace.color.setHex(0xc0392b);
        intGeometry.colorsNeedUpdate = true;

        // Identify the vertices of each face
        var intVertices = intGeometry.vertices;
        var v1 = intVertices[intFace.a],
            v2 = intVertices[intFace.a],
            v3 = intVertices[intFace.a];

        // Calculate the centroid of the selected face
        var intPosition = new THREE.Vector3();
        intPosition.x = (v1.x + v2.x + v3.x) / 3;
        intPosition.y = (v1.y + v2.y + v3.y) / 3;
        intPosition.z = (v1.z + v2.z + v3.z) / 3;
        console.log(intPosition);
    }

}

通过使用console.log(),我可以看到人脸被正确识别并且其位置也被识别,但是球体没有正确地跟踪鼠标,并且我需要帮助来调整位置。这是一个包含完整代码的JSFiddle


1
要让球体跟随鼠标移动,您需要将屏幕坐标转换为threejs世界位置。请查看此链接。并更新fiddle here,修改的代码从第375行开始。 - uhura
谢谢,伙计!这是我之前提出的一个问题的一部分(另一个问题)。如果你愿意,可以在那里回答并得到悬赏。 - d_z90
3个回答

4
这是一个有关IT技术的小测试: https://jsfiddle.net/77xvepp7/ 目前它还不能正常工作,我将相机移动了一点,以便可以看到想法。
基本思路是从表面中选择一个顶点作为“基准点”,它可以是表面的中心,就像在你的示例中一样,将该点保持为参考点,通过相同的量移动三角形中的所有其他点。如果不保存原始值,则下一轮可能会具有无效值,并且三角形会随机移动一点。
然后,计算鼠标与“基准点”的距离,遍历所有顶点,并将其向基准点的方向移动一定量。如果距离大于某个定义的量,请从顶点恢复原始值。
您可以尝试使用顶点着色器来实现,但问题是当鼠标位于表面上时,每个顶点都会朝相反的方向移动,并且在鼠标下方的三角形会缩放以填充空白区域。选择一个点作为“基准点”可以解决这个问题。
  // Modify the geometry
  var geometry = backgroundMesh.geometry;
    for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) {

        var face = geometry.faces[ i ];

        if ( face instanceof THREE.Face3 ) {

            var a = geometry.vertices[face.a];
            var b = geometry.vertices[face.b];
            var c = geometry.vertices[face.c];
            var vList = [a,b,c];

            // The trick is to save the original face positions
            if(!a.origXSet) {
               a.origX = a.x;
               a.origY = a.y;
               a.origZ = a.z;
               a.origXSet = true;
            }

            var vect = a;
            var dx = (vect.origX - pos.x), 
                dy = (vect.origY - pos.y), 
                dz = (vect.origZ - pos.z);

            // distance to the mouse
            var dist = Math.sqrt( dx*dx + dy*dy);

            // magic constant, react if distance smaller than 4
            if(dist < 4) {
              vList.forEach(function(v) {
              if(!v.origXSet) {
                   v.origX = v.x;
                   v.origY = v.y;
                   v.origZ = v.z;
                   v.origXSet = true;
               }
               var len = Math.sqrt(dx*dx + dy*dy + dz*dz);
               if(len==0) return;
               var ndx = dx / len,
                   ndy = dy / len,
                   ndz = dz / len;
               v.x = v.origX + ndx * 2; // move 2 pixels
               v.y = v.origY + ndy * 2; 
               v.z = v.origZ + ndz * 2;
      });

      } else {
          // otherwise, reset coordinates
          vList.forEach(function(v) {
            if(v.origXSet) {
             v.x = v.origX;
             v.y = v.origY;   
             v.z = v.origZ;
             }
           });
      }

       geometry.verticesNeedUpdate = true;
       geometry.normalsNeedUpdate = true;    

    }
 }

主要问题是:鼠标指针在世界坐标系中的位置没有正确计算。现在我没有精力思考如何纠正这个问题,但决定发布这个答案,以便您可以从那里继续。

1

你没有将任何东西分配回你的几何体。可以尝试如下:

intVertices[intFace.a].copy( intPosition );
intVertices[intFace.b].copy( intPosition );
intVertices[intFace.c].copy( intPosition );

intGeometry.verticesNeedUpdate = true;
intGeometry.normalsNeedUpdate = true;

酷啊!但是我应该如何存储和重置位置呢?因为现在每个块在“悬停”时都会消失。 - d_z90
我猜你需要另一个数组/缓冲区来保存你的顶点的“状态”。 - gaitat
这是一个可能性,我认为实现这个的方法是在鼠标距离小于球体半径时,将“镶嵌面”的质心向外推。然后当它推出去时,再发送回原始位置。你觉得呢? - d_z90
为什么要使用质心(需要额外计算)而不是顶点本身? - gaitat

0
你遇到的问题是,你尝试使用 ray.intersectObjectstargetList 不是一个 THREE.Mesh 实例。 因此,当你尝试访问 intersectedFace.acc 时会抛出错误。

我已经编辑了问题,也许你可以再看一下。谢谢! - d_z90

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接