three.js 设置和读取相机的查看向量

16

不使用camera.rotation或lookAt()函数旋转相机,我希望可以直接传递一个“观察向量”给相机...是否可以直接设置相机的“观察向量”,并且是否可以从相机读取“观察向量”?

4个回答

35
相机没有“观察向量”,因此您无法设置它。但是,您可以通过将观察向量添加到相机的位置上,然后调用point来构建要查看的点。
camera.lookAt( point );

如果相机没有父级(场景之外的其他对象),以下是确定相机朝向的方法:

相机沿着其内部负z轴朝下看,因此需要创建一个指向负z轴的向量:

var vector = new THREE.Vector3( 0, 0, - 1 );

现在,将应用于相机的旋转同样应用于应用于该相机的向量:

vector.applyQuaternion( camera.quaternion );

生成的向量将指向相机所看的方向。

或者,您可以使用以下方法,即使相机是另一个对象的子对象也可以使用:

camera.getWorldDirection( dirVector );

three.js r.73


这是很有用的信息,感谢West。我仍然认为在内部一定有一个视线向量,它应该是3D相机的基本要素...啊,好了,我想我明白了这背后的原因,相机从未移动,整个场景围绕着它进行变换,始终朝向-z轴,因此视线向量始终是0 0 -1,而其他所有东西都被旋转。 - Dev
不。相机在世界空间中移动,但是从相机的参考系中,它是沿着负z轴向下查看的。 - WestLangley
当然,另一种方法(如果您正在使用lookat点)是对相机-lookat进行归一化处理。这种方法应该能够避免在父级时进行额外的计算。 - Dev

11
这些其他答案非常有见地,但并非完全正确。该代码返回一个向与相机指向相同方向的向量。这是一个很好的开始!
但是,除非相机在原点(0, 0, 0)处(或正好位于连接原点和旋转向量点的线段上,而不超过该点,使该点在相机后面),否则相机将不会看向该点。很明显 - 就是常识 - 相机的位置也影响了所看点的位置。想一想!!
相机方法 lookAt() 会查看 3D 空间中的一个点,而不管相机在哪里。因此,要获取相机正在查看的点,您需要通过调用进行相机位置调整: vec.add( camera.position ); 还值得一提的是,相机不是在查看一个点,而是在查看无限多个点的线路,每个点与相机的距离不同。其他答案中的代码返回一个长度为一的向量,因为将四元数应用于归一化的 z 轴向量(0, 0, -1)会返回另一个归一化向量(朝不同方向)。要计算距相机任意距离 x 的观察点,请使用: THREE.Vector3( 0, 0, -x ).applyQuaternion( camera.quaternion ).add( camera.position ); 这将获取沿着 z 轴距原点 x 的点,将其旋转到相机方向,然后通过添加相机的位置创建一个“世界点”。我们需要一个“世界点”(而不仅仅是“相对于相机的点”),因为 camera.lookAt() 也需要一个“世界点”作为参数。

1
在three.js中,Vector3类可用于表示点、向量或方向向量。在这种情况下,问题是关于方向向量的。所接受的答案解释了如何从方向向量中“设置”相机方向。它还解释了如何从相机方向“推断”出方向向量。关于所接受的答案没有任何错误之处。你正在回答别人的问题。 - WestLangley
是的,你说得对。我认真考虑了一下,发现你甚至提到了将向量添加到相机位置。你做得很好。对我来说,有趣的是思考如何使用 nlerp() 而不是 slerp(),因为它的计算成本要低得多,并且可以通过选择距离 x 处的相机 lookAt 点并插值到所需的 lookAt 点也在距离 x 处来实现近乎相等的效果。但你是正确的。这是另一个话题。对于任何误解,我表示歉意。抱歉。 - chorpita
被接受的答案解释了如何从方向向量设置相机方向。不,你没有。 - zwcloud

9
上述答案被封装为实用程序,这是我在我的三个实用程序中所做的:
THREE.Utils = {
    cameraLookDir: function(camera) {
        var vector = new THREE.Vector3(0, 0, -1);
        vector.applyEuler(camera.rotation, camera.eulerOrder);
        return vector;
    }
};

使用 THREE.Utils.cameraLookDir(camera); 调用。

马特,你的风格非常不错。我能期待另一个工具以相同的格式跟随 cameraLookDir 的右括号吗? - Dev
是的,你只需要用逗号将它们分开。这是字面上的JavaScript对象表示法,你应该注意,在此脚本之前必须确保THREE已加载,以使其正常工作。 - mattdlockyer
关于大写字母,我假设:package = THREE,class = Utils,method = cameraLookDir。如果这看起来有点奇怪,请告诉我。 - mattdlockyer
看起来不错 @mattdlockyer,有没有办法在绕着3D模型旋转时,使文本标签始终朝向相机? - Agent Zebra
1
使用精灵来制作文本标签,它们将始终指向并正确朝向相机。 - mattdlockyer

-1
我所做的是在渲染场景之前使用lookAt(Vector)方法,就像下面的代码一样,只需在新的HTML文件中尝试使用它即可 :)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
    <style >
      *{
        margin: 0 ;
      }
      #WebGlElement {
        height: 500px;
        width:  500px;
        background-color: red
      }
    </style>
  </head>
  <body>
    <script src="js/three.min.js"></script>

    <div id="WebGlElement"></div>

    <script>
      var contianer = document.getElementById("WebGlElement");
      var origin = new THREE.Vector3(0,0,0);
      // CREATE THREE BASIC ELEMENTS
      var scene = new THREE.Scene();
      var camera = new THREE.PerspectiveCamera( 40, 300/300, 1, 1000 );

      var renderer = new THREE.WebGLRenderer();
      // SET RENDERER AND ATTACH IT TO BODY
      renderer.setSize( 500, 500 );
      //document.body.appendChild( renderer.domElement );
      contianer.appendChild( renderer.domElement);
      // CREATE A GEOMETRY AND ADD IT TO SCENE

      /*var geometry = new THREE.BoxGeometry( 1, 1, 5 );
      var material = new THREE.MeshBasicMaterial( { color: 0x1abc9c } );
      material.wireframe = true ;
      material.wireframeLinewidth = 0.1 ;
      var cube = new THREE.Mesh( geometry, material );
      scene.add( cube );*/
      var geometry = new THREE.Geometry();
      geometry.elementsNeedUpdate=true;

      geometry.vertices.push(
       new THREE.Vector3( -0,  1, 0 ),
       new THREE.Vector3( -1, -1, 0 ),
       new THREE.Vector3(  1, -1, 0 )
      );
      geometry.faces.push(
        new THREE.Face3( 0, 1, 2 ),
        new THREE.Face3( 2, 1, 0 )
     );

      var material = new THREE.MeshBasicMaterial( { color: 0x1abc9c } );
      //material.wireframe = true ;
      //material.wireframeLinewidth = 0.1 ;
      var cube = new THREE.Mesh( geometry, material );
      scene.add( cube );


      var axisHelper = new THREE.AxisHelper( 1 );
      scene.add( axisHelper );


      // POSITION OF CAMER ON Z
      camera.position.z = 5;
      camera.position.y = 5;
      camera.up = new THREE.Vector3(0,0,1);
      
      var dir = 1;
      var number = 0.115

      // CREATE FUNCTION FOR RENDER
      var render = function () {

      requestAnimationFrame( render );
      //circle.rotation.x += 0.01;
          if ( camera.position.x> 5) {
            dir = -1;
          }
          if ( camera.position.x< -5) {
             dir = 1;
          }
          camera.lookAt(cube.position);

          //camera.rotation.y += 0.015 * dir;
          camera.position.x += number * dir;
          renderer.render(scene, camera);

     };


      // EXECUTE FIRST RENDER
      render();

    </script>



  </body>
</html>


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