12
votes

I have a camera that moves in a few different ways in the scene. The camera should rotate around a target position. In my case, this is a point on a mesh that the user has targeted. Because the camera usually doesn't require moving relative to this point, I was not able to use the pivot idea here: https://github.com/mrdoob/three.js/issues/1830. My current solution uses the following code:

var rotationY = new THREE.Matrix4();
var rotationX = new THREE.Matrix4();
var translation = new THREE.Matrix4();
var translationInverse = new THREE.Matrix4();
var matrix = new THREE.Matrix4();
function rotateCameraAroundObject(dx, dy, target) {  
    // collect up and right vectors from camera perspective
    camComponents.up = rotateVectorForObject(new THREE.Vector3(0,1,0), camera.matrixWorld);
    camComponents.right = rotateVectorForObject(new THREE.Vector3(1,0,0), camera.matrixWorld);

    matrix.identity();

    rotationX.makeRotationAxis(camComponents.right, -dx);
    rotationY.makeRotationAxis(camComponents.up, -dy);
    translation.makeTranslation(
        target.position.x - camera.position.x,
        target.position.y - camera.position.y,
        target.position.z - camera.position.z);
    translationInverse.getInverse(translation);

    matrix.multiply(translation).multiply(rotationY).multiply(rotationX).multiply(translationInverse);
    camera.applyMatrix(matrix);
    camera.lookAt(target.position); 
}

The issue is that we do not want to use lookAt, because of the reorientation. We want to be able to remove that line.

If we use the code above without lookAt, we rotate around the point but we do not look at the point. My understanding is that my method should rotate the camera's view as much as the camera itself is rotate, but instead the camera is rotated only a small amount. Could anyone help me understand what's wrong?

EDIT: Cleaned up the original post and code to hopefully clarify my question.

My thinking is that I can translate to the origin (my target position), rotate my desired amount, and then translate back to the beginning position. Because of the rotation, I expect to be in a new position looking at the origin.

In fact, I'm testing it now without the translation matrices being used, so the matrix multiplication line is:

matrix.multiply(rotationY).multiply(rotationX);

and it seems to be behaving the same. Thanks for all the help so far!

ONE MORE THING! A part of the problem is that when the camera behaves badly close to the north or south poles. I am looking for a 'free roaming' sort of feel.

2
(1) camera.lookAt( vector ) does not take a matrix as an argument. (2) What, exactly, are you trying to do?WestLangley
I messed it up while trying to clean up my code, sorry! I've edited the post to reflect the right informationmaaachine
I'm looking for the exact same thing .. in 2017. I may have missed something, but I really hope I don't have to pull out my Calc textbook and revisit vectors and 3D trig. I just want to be able to rotate "freely" around an object. It's a bit tricky, indeed.dylnmc

2 Answers

12
votes

Put the following in your render loop:

camera.position.x = target.position.x + radius * Math.cos( constant * elapsedTime );         
camera.position.z = target.position.z + radius * Math.sin( constant * elapsedTime );
camera.lookAt( target.position );

renderer.render( scene, camera );

Alternatively, you can use THREE.OrbitControls or THREE.TrackballControls. See the three.js examples.

0
votes

The Gimbal lock that you are referring to (reorientation) is because of the use of Euler angles in the default implementation of the camera lookat. If you set

camera.useQuaternion = true;

before your call to lookat, then euler angles will not be used. Would this solve your problem ?