如何使用three.js动画相机以查看对象?

6
在Three.js中,我想让摄像机看向场景中的一个物体,并在单击另一个物体时,使摄像机平稳地旋转以查看新物体。(即动画旋转摄像机)。我已经在SO上检查过了,这是最相似的问题: Three.js如何使用四元数旋转相机。我还尝试修改了这个网站中的代码,并成功获得了类似于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/。我错过了什么吗?提前感谢。

对于同样的问题,如果需要更详细的答案,请查看这里:https://dev59.com/qGUq5IYBdhLWcg3wXvIC - kjyv
6个回答

13

我使用四元数在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


4

对于使用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 文档


1
相机有自己的LookAt方法。我怀疑这是因为相机的LookAt逻辑与两个3D对象略有不同,因为相机的运动会反向影响渲染视图(将相机向右移动,场景似乎向左移动),所以它需要反向思考或特定的方法来隐藏那种反向逻辑。
我建议你尝试使用相机的lookAt方法。

我在提问之前尝试了相机的lookAt方法。对于我来说,lookAt的问题在于相机会立即改变其旋转,而我想要的是将相机的旋转从一个对象动画到另一个对象。不过还是谢谢你的回答。 - EduG
3
这个怎么样: 1)复制相机矩阵 2)使用相机的lookAt函数生成目标矩阵 3)使用Three.js中的decompose matrix函数将两个矩阵分解为它们的平移、四元数和缩放组成部分。 4)使用tween.js在两个分解矩阵的值之间进行插值处理。 完成。 - Blake Senftner

1

来自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})

0

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

这是我做的方式:

  1. 获取相机的当前四元数(initialQuaternion)
  2. 创建相机的克隆
  3. 使用lookAt方法使克隆物体朝向目标
  4. 问题在于,相机的lookAt方法实际上会使其背离目标,因此您有两个选择:
  • 直接使用lookAt方法,然后反转旋转
  • 或计算目标应该具有的位置,以便当背离它时,您实际上朝向正确的方向
  1. 获取克隆体的新四元数(targetQuaternion)
  2. 通过使用动画循环,在initialQuaternion和targetQuaternion之间进行slerp

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