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

EduG picture EduG · Aug 23, 2013 · Viewed 17.2k times · Source

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

Answer

imbrizi picture imbrizi · Jun 11, 2014

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