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
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:
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
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With