如何在three.js中让摄像头看一个物体做动画?

问题描述 投票:3回答:4

在Three.js中,我想让摄像头看着场景中的一个对象,当我点击另一个对象时,让摄像头平稳地旋转看着新对象。(即对相机的旋转进行动画处理)。

我在SO中查过,这是最类似的问题。

Three.js 如何使用四元组旋转相机?

我也试着修改了这段代码。网站 我设法得到这样的东西 http:/jsfiddle.netF7Bh3

 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.net5Peq91。

我缺少什么?先谢谢你

rotation three.js quaternions
4个回答
9
投票

我成功地使用四元组在three.js中流畅地制作了一个摄像头的动画。我花了点时间才弄明白,但一旦完成后,看着四元组的工作效果非常漂亮。

方法是

  • 存储初始四元数
  • 定义目标二次元
  • 之间的东西从0到1
  • 在tween过程中对每一帧进行四元插值
  • 在每一帧画面中,将插值四元数应用到相机上。

还有一个快速的例子,包含代码的关键部分。

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.comexperiments#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在一个原始矢量和一个目标矢量之间进行tween,并使用group.lookat(vector)函数在每一步将对象指向被tween的矢量。但我发现它给出的结果很卡顿,不一致,尤其是当原始向量和目标向量接近反平行时。这种使用球形线性插值(slerp)的方法给了我更平滑的结果。


0
投票

摄像机有自己的LookAt方法。我怀疑这是因为摄像机的LookAt逻辑与两个3d对象的LookAt逻辑略有不同,因为摄像机的运动会反过来影响渲染视图(向右移动摄像机,场景就会出现向左移动),所以需要逆向思维或者特定的方法来隐藏这个逆向逻辑。

我建议你试试摄像头的lookAt。


0
投票

根据前面的回答和文档,我研究出了这个解决方案,似乎更简洁一些。

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

slerp文档

© www.soinside.com 2019 - 2024. All rights reserved.