# Rotate 3D camera relative to what's on screen (in threejs)

I'm trying to have a camera in Threejs react to respective left/right/up/down/forward/backward keys, always based on the direction the camera is currently facing. It works fine left/right (camera.rotation.y) and up/down (camera.translateZ) but rotating forward-down/ backward-up (camera.rotation.z) won't work correctly once the camera is already facing up/ downways. Any help please?

Thanks!

-
This has now been answered, I'm waiting to check who will post it as answer (github.com/mrdoob/three.js/issues/1950). – Philipp Lenssen May 20 '12 at 19:41

In general mathematics there are two ways to do this, treejs might have some functions that make this easier, but it's nice to see the math

(note that I'll be using a right-handed coordinate system here, OpenGL and XNA use this system but DirectX uses a left-handed system, in that case the math is different)

Using the view matrix

Also note that if I say MatrixName.M12 I'll mean the value at the first row in the second column.

Given a view matrix V you can always obtain the right, up and backward vector quite easily:

``````right = (viewMatrix.M11, viewMatrix.M21, viewMatrix.M31);
up = (viewMatrix.M12, viewMatrix.M22, viewMatrix.M33);
backward = (viewMatrix.M13, viewMatrix.M23, viewMatrix.M33);
``````

This gives:

``````left = -right;
up = -down;
forward = -backward;
``````

Now if you want to move something forward you have to take all 3 axises in to account:

``````position.X += forward.X * speed;
position.Y += forward.Y * speed;
position.Z += forward.Z * speed;
``````

Using a forward pointing vector

We can also construct a vector that points forward by substracting the current camera position from the camera look-at, and then use the cross product to find the other axis, however this works well if we don't touch up (say for an FPS camera):

``````up = (0,1,0);
forward = camera.LookAt - camera.Position;
right = forward CROSS up;
``````

This again gives:

``````down = -up;
backward = -forward = camera.Position - camera.LookAt;
left = - right;
``````

Again you'll have to take all 3 axises into account when moving something forward (or along any other axis).

Arcball camera

I could also interpret your question as if you want a camera that keeps focused on a single object while you rotate around it. In that case you're looking for an arcball camera. I hate to plug my own stuff but here is an implementation that you can reference when making your own for threejs.

-
Thanks! What I am actually looking for is the rotation of the camera around its own axis independent of the world. For movement, translateZ currently does the job in Threejs well. However, the forward-downward-falling rotation of the camera (and its opposite, backwards-upwards) doesn't currently work when the camera loses its default rotation setup (i.e. it only works in the beginning, using camera.rotation.x += value). – Philipp Lenssen May 20 '12 at 9:54
Argh I only now see that you said rotate not translate. Doh! I'm still not 100% clear on what your problem is though. Are you using euler angles for your camera rotation or are you using quaternions? Using euler angles can cause gimballock, maybe that's what you're experiencing? – Roy T. May 20 '12 at 10:13
Thanks again in any case -- and someone now answered the question here! github.com/mrdoob/three.js/issues/1950 – Philipp Lenssen May 20 '12 at 19:40

Thanks to WestLangley for this working answer!

``````// "Up-less" Camera Controller
// Rotations are with respect to the camera's internal frame of reference.
// Only use this controller if your scene has no sense of "up". If there is a horizon, for example, this kind of
// camera controller can be very disconcerting.
// WestLangley

var mesh;
var container;
var camera, scene, renderer;
var geometry, material;

var speed = 10, radians = .025;

onKeyDown = function( event ) {

switch( event.keyCode ) {

case 87: // w (move forward)
camera.translateZ( -speed );
break;

case 83: // s (move backward)
camera.translateZ( speed );
break;

case 37: // left arrow (look left)
camera.rotation.getRotationFromMatrix( camera.matrix ); // r49
//camera.rotation.setEulerFromRotationMatrix( camera.matrix, camera.eulerOrder ); // dev50
break;

case 39: // right arrow (look right)
camera.rotation.getRotationFromMatrix( camera.matrix ); // r49
//camera.rotation.setEulerFromRotationMatrix( camera.matrix, camera.eulerOrder ); // dev50
break;

case 38: // up arrow (look up)
camera.rotation.getRotationFromMatrix( camera.matrix ); // r49
//camera.rotation.setEulerFromRotationMatrix( camera.matrix, camera.eulerOrder ); // dev50
break;

case 40: // down arrow (look down)
camera.rotation.getRotationFromMatrix( camera.matrix ); // r49
//camera.rotation.setEulerFromRotationMatrix( camera.matrix, camera.eulerOrder ); // dev50
break;

}

render();

};

document.addEventListener( 'keydown', onKeyDown, false );

init();

//animate();
render();

function init() {

// renderer
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

// scene
scene = new THREE.Scene();

// camera
camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 100;

// geometry
geometry = new THREE.CubeGeometry( 20, 20, 20, 1, 1, 1 );

// mesh
for ( var i = 0; i < 500; i++ ) {

material = new THREE.MeshBasicMaterial( { color: 0xFFFFFF * Math.random(), wireframe: true } );

mesh = new THREE.Mesh( geometry, material );
mesh.position.set( Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 );
mesh.position.multiplyScalar( 1000 );

}

}

function animate() {

requestAnimationFrame( animate );
render();

}

function render() {

renderer.render(scene, camera);

}

​
``````
-