Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to animate the camera in three.js to look at an object?

In Three.js, I would like to have the camera looking at an object in the scene, and when I click on another object, to have the camera rotate smoothly to look at the new object. (i.e animate the rotation of the camera).

I´ve checked in SO and this is the most similar question :

Three.js How to use quaternion to rotate camera

I've also tried modifying the code in this website and I manage to get something like this 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();

But when I try to rotate the camera instead of the object all I get is: http://jsfiddle.net/5Peq9/1/

What am I missing? Thanks in advance

like image 648
EduG Avatar asked Aug 23 '13 11:08

EduG


3 Answers

I managed to animate a camera smoothly in three.js using quaternions. It took me a while to figure it out, but once it is done it is beautiful to watch how nicely quaternions work.

The method is:

  • store the initial quaternion
  • define a target quaternion
  • tween something from 0 to 1
  • interpolate quaternions on every frame during the tween
  • apply interpolated quaternion back to camera on every frame

And a quick example with the key parts of the code:

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

The last step is to find the target Euler rotation to pass to moveCamera. In my case I was using TrackballControls to find some interesting camera positions/rotations, then retrieving them with euler = camera.rotation.clone() and passing that as a target. For example:

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

An application of this method can be seen here: http://brunoimbrizi.com/experiments/#/08

like image 193
imbrizi Avatar answered Oct 18 '22 03:10

imbrizi


For those using the Tween.js library: I adapted imbrizi's example to rotate a group of objects. The same could be applied to the camera as well.

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();
}

I had originally used Tween.js to tween between an original and a target vector, and used the group.lookat(vector) function to point the object at the tweened vector at each step. But I found it to give jerky, inconsistent results, especially when the original and target vectors were close to antiparallel. This method, using spherical linear interpolation (slerp) gave me much smoother results.

like image 33
Chris Chalmers Avatar answered Oct 18 '22 05:10

Chris Chalmers


Based on the previous answers and the docs I worked out this solution, which seems a bit cleaner.

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

like image 25
arc Avatar answered Oct 18 '22 04:10

arc