Three.js - 推开然后在“鼠标移动”上恢复元素位置

Posted

技术标签:

【中文标题】Three.js - 推开然后在“鼠标移动”上恢复元素位置【英文标题】:Three.js - Push away and then restore elements position on "mouse move" 【发布时间】:2016-06-29 13:35:24 【问题描述】:

大家好,我正在Three.js 开发一个项目,用户可以在其中hover 镶嵌面,并且每个网格在与不可见的球体相交时都应该被推开,当它在外面时回到原来的位置从它的区域。我使用this 和this example 作为参考,但我被卡住了,因为我不知道如何使它在Three.js 中工作。

在我的代码中,我可以hover 网格的每个面,使其变为红色并在鼠标消失后恢复其原始颜色。但我不能对这个职位做类似的事情。这是我认为问题所在的代码部分:

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

【问题讨论】:

要让球体跟随鼠标,您需要将屏幕坐标转换为threejs的世界位置。 Check this link 。并更新fiddle here,更改代码从第 375 行开始。 谢谢你!这是another question问题的一部分,我问过,如果你愿意,你可以在那里发布答案并获得奖励。 【参考方案1】:

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

【讨论】:

我已经编辑了这个问题,也许你可以再试试看。谢谢!【参考方案2】:

您没有将某些东西分配回您的几何体。大意:

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

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

【讨论】:

帅哥!但是我应该如何存储和重置位置?因为现在hover 上的每个区块都消失了。 我猜你需要另一个数组/缓冲区来保存你的顶点的state 这是一种可能性,我认为实现这一点的方法是每当与鼠标的距离低于球体半径时,将tessellated face 的质心推出。然后当它出来时,将它送回原来的位置。你怎么看? 为什么使用质心(这是额外的计算)而不是顶点本身?【参考方案3】:

这是一个关于这个主题的小测试: 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;    

    
 

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

【讨论】:

以上是关于Three.js - 推开然后在“鼠标移动”上恢复元素位置的主要内容,如果未能解决你的问题,请参考以下文章

如何通过鼠标移动事件获取相机位置并在three.js中转换为屏幕坐标? [关闭]

Three.js案例从0到1对象跟随鼠标动起来

在鼠标移动中创建涂抹/液化效果,使用webgl连续动画回原始状态

使用vue学习three.js之移动相机-使用飞行控件FlyControls控制相机

Three.js探索:目录

Three.js - 在自定义几何图形上平滑着色 Lambert 材质的问题