【问题标题】:How to animate the camera in three.js to look at an object?如何在three.js中为相机设置动画以查看对象?
【发布时间】:2013-08-26 09:49:54
【问题描述】:

在 Three.js 中,我想让相机看着场景中的一个对象,当我点击另一个对象时,让相机平滑旋转以查看新对象。 (即动画相机的旋转)。

我已经签入了,这是最相似的问题:

Three.js How to use quaternion to rotate camera

我也尝试过修改 website 中的代码,并设法得到类似 http://jsfiddle.net/F7Bh3/ 的代码

 var quat0 = mesh2.quaternion;
 var eye = mesh2.position;
 var center = mesh.position;
 var mat = new THREE.Matrix4();
 mat.lookAt(center, eye, new THREE.Vector3(0,1,0));
 var quat1 = new THREE.Quaternion();
 quat1.setFromRotationMatrix( mat );

 var qm = new THREE.Quaternion();

 deltaTheta = angleBetweenQuats(quat0,quat1);
 var frac =  0.2/deltaTheta;
 if (frac>1)  frac=1;

 mesh2.quaternion.slerp(quat1,frac);
 mesh2.quaternion.normalize();

但是当我尝试旋转相机而不是对象时,我得到的只是:http://jsfiddle.net/5Peq9/1/

我错过了什么?提前致谢

【问题讨论】:

标签: rotation three.js quaternions


【解决方案1】:

我设法使用四元数在 three.js 中平滑地为相机设置动画。我花了一些时间才弄明白,但一旦完成,就很高兴看到四元数工作得多么好。

方法是:

  • 存储初始四元数
  • 定义一个目标四元数
  • 介于 0 到 1 之间
  • 在补间期间在每一帧上插入四元数
  • 在每一帧上将插值四元数应用回相机

还有一个包含代码关键部分的快速示例:

var camera       // camera
var cameraPos0   // initial camera position
var cameraUp0    // initial camera up
var cameraZoom   // camera zoom
var iniQ         // initial quaternion
var endQ         // target quaternion
var curQ         // temp quaternion during slerp
var vec3         // generic vector object
var tweenValue   // tweenable value 

// init camera
function setup()
{
    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 2000)
    camera.position = new THREE.Vector3(0, 0, 80)
    cameraPos0 = camera.position.clone()
    cameraUp0 = camera.up.clone()
    cameraZoom = camera.position.z
}

// set a new target for the camera
function moveCamera(euler, zoom)
{
    // reset everything
    endQ = new THREE.Quaternion()
    iniQ = new THREE.Quaternion().copy(camera.quaternion)
    curQ = new THREE.Quaternion()
    vec3 = new THREE.Vector3()
    tweenValue = 0

    endQ.setFromEuler(euler)
    TweenLite.to(this, 5, { tweenValue:1, cameraZoom:zoom, onUpdate:onSlerpUpdate })
}

// on every update of the tween
function onSlerpUpdate()
{
    // interpolate quaternions with the current tween value
    THREE.Quaternion.slerp(iniQ, endQ, curQ, tweenObj.value)

    // apply new quaternion to camera position
    vec3.x = cameraPos0.x
    vec3.y = cameraPos0.y
    vec3.z = cameraZoom
    vec3.applyQuaternion(curQ)
    camera.position.copy(vec3)

    // apply new quaternion to camera up
    vec3 = cameraUp0.clone()
    vec3.applyQuaternion(curQ)
    camera.up.copy(vec3)
}

最后一步是找到目标欧拉旋转传递给moveCamera。在我的例子中,我使用 TrackballControls 来查找一些有趣的摄像机位置/旋转,然后使用 euler = camera.rotation.clone() 检索它们并将其作为目标传递。例如:

 moveCamera(new THREE.Euler(2.20, -0.15, 0.55), 120)

这个方法的一个应用可以看这里:http://brunoimbrizi.com/experiments/#/08

【讨论】:

    【解决方案2】:

    对于那些使用 Tween.js 库的人:我改编了 imbrizi 的示例来旋转一组对象。这同样适用于相机。

    function tweenRotation(targetQuaternion, duration){
        //tweens between zero and 1 values along Quaternion's SLERP method (http://threejs.org/docs/#Reference/Math/Quaternion)
    
        qm = new THREE.Quaternion(); //initiate an empty Qt to be filled by the .slerp function
        curQuaternion = group.quaternion; //the starting point of your rotation
    
        var tween = new TWEEN.Tween({t:0}).to({t:1}, duration)
            .easing( TWEEN.Easing.Quadratic.InOut )
            .onUpdate(function(){
                THREE.Quaternion.slerp(curQuaternion, targetQuaternion, qm, this.t);
                qm.normalize();
                group.rotation.setFromQuaternion(qm);
        });
    
       tween.start();
    }
    

    我最初使用 Tween.js 在原始向量和目标向量之间进行补间,并使用 group.lookat(vector) 函数在每一步将对象指向补间向量。但我发现它会给出不稳定、不一致的结果,尤其是当原始向量和目标向量接近反平行时。这种使用球面线性插值 (slerp) 的方法给了我更平滑的结果。

    【讨论】:

      【解决方案3】:

      根据之前的答案和文档,我制定了这个解决方案,看起来有点干净。

      const targetOrientation = new THREE.Quaternion().set(0, 0, 0, 1).normalize();
      gsap.to({}, {
          duration: 2,
          onUpdate: function() {
              camera.quaternion.slerp(targetOrientation, this.progress());
          }
      });
      

      slerp documenation

      【讨论】:

        【解决方案4】:

        相机有自己的 LookAt 方法。我怀疑这是因为相机的lookAt逻辑与两个3d对象的逻辑略有不同,因为相机的运动反向影响渲染视图(向右移动相机,场景似乎向左移动),因此需要逆向思考或隐藏该逆逻辑的特定方法。

        我建议你试试相机的外观。

        【讨论】:

        • 好吧,我在问这个问题之前尝试了相机的lookAt方法。对我来说,lookAt 的问题是相机会立即改变其旋转,而我想要的是将相机从一个对象旋转到另一个对象的动画。不过感谢您的回答。
        • 这个怎么样:1) 制作相机矩阵的副本 2) 使用相机查看生成目标矩阵 3) 使用三中的分解矩阵例程将两个矩阵分解为它们的平移, quat 和缩放组件。 4) 使用 tween.js 在两个分解矩阵的值之间进行插值。完成。
        【解决方案5】:

        从 2021 年开始

        我发现动画相机的非常简单的答案:

        如果您使用 Orbit Controler,最好为 vector3 的 controler.target 设置动画

        // Controls
        const controls = new OrbitControls(camera, canvas)
        controls.enableDamping = true;
        ....
        // later in this code
        gsap.to(controls.target, {...someObject3d.postion, duration: 3})
        

        【讨论】:

          【解决方案6】:

          接受的答案似乎是正确的,但是,它允许您查看特定的旋转,而不是查看目标,那么您打算如何获得特定的旋转?

          这就是我的做法:

          1. 获取相机的当前四元数(initialQuaternion)
          2. 创建相机的克隆
          3. 使用 lookAt 方法使克隆查看目标
          4. 问题是,相机的 lookAt 方法实际上会使其看起来远离目标,因此您有两个选择:​​i>
          • 要么直接使用lookAt方法,然后反转旋转
          • 或者计算目标应该有的位置,这样当你离开它时,你实际上看向了正确的方向
          1. 获取克隆的新四元数(targetQuaternion)
          2. 通过使用动画循环,在 initialQuaternion 和 targetQuaternion 之间执行 slerp

          【讨论】:

            猜你喜欢
            • 2019-12-06
            • 2012-05-14
            • 1970-01-01
            • 1970-01-01
            • 2018-08-21
            • 1970-01-01
            • 2018-08-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多