Coordinate System Rotation: Euler Angles, Rotation Matrix, and Quaternions

CS 493 Lecture, Dr. Lawlor

To move the camera, objects in the world, or physical effects, we need a reliable way to represent arbitrary rotations in 3D.

Euler Angles

This is by far the simplest scheme--you store three rotation angles, and rotate first about X, then about Y, and finally about Z.  The only problem is that the Z has changed by the time you get to it, so it's difficult to control the later rotations properly.  By default, THREE.js supports Euler angles in the "rotation" field of each object.  So "sim.spheres[i].rotation.x" is the rotation angle around the X axis, measured in radians.

You can try this out with the "WASD" (X and Y rotation) and "QZ" (Z rotation) keys here:
   Keyboard Euler Rotation Demo

Note that initially, the Y rotation (W and S) is aligned with the pointy tips, which stick along the Y axis.  But if I rotate in Z (press Z), then the Y rotation direction has changed.

Quaternions

Quaternions are a cool mathematical construct that lets you represent an arbitrary rotation as a 4D vector.  The 3D XYZ parts of the vector give the rotation axis for the direction.  The magnitude of the 3D part is the sine of half the rotation angle; the cosine of half the rotation angle is stored in the final W component of the vector.

It's a strange definition, but the neat part is that 'multiplying' quaternions (using a cross product looking formula) gives you the composition of the underlying rotations.

In THREE.js, you can set "obj.useQuaternion=true" to disable that object's rotation angles and use "obj.quaternion" instead.  You can now rotate the object by building a quaternion to represent the new rotation, and computing "obj.quaternion.multiplySelf(newRot);"
  Keyboard Quaternion Rotation Demo

It's not immediately obvious there's a difference from Euler mode, but now the rotations stick to the coordinate frame of the object--Y rotations are always along the pointy tips.

Rotation Matrix / Orthonormal Coordinate Frame

I personally find quaternions fun but confusing, and I usually find a need for object-local 3D vectors pointing in all directions: for example, Z is used for run, X is used for strafe, Y is used for jump.  So I usually just keep my own object-local X, Y, and Z vectors for each object, and keep them (1) normalized to unit length, and (2) orthogonal to each other (I use cross product for this).

Any set of three orthonormal vectors actually forms the 3x3 core of a rotation matrix.   To get from object-Local coordinates L into world-Global coordinates G:
[ G.x ]   [ X.x  Y.x  Z.x ]  [ L.x ]
[ G.y ] = [ X.y  Y.y  Z.y ]  [ L.y ]
[ G.z ] [ X.z  Y.z  Z.z ]  [ L.z ]
This is equivalent to the vector equation G = L.x*X + L.y*Y + L.z*Z.

To get back into local coordinates, we just transpose the matrix (transposing a rotation matrix gives you the inverse matrix):
[ L.x ]   [ X.x  X.y  X.z ]  [ G.x ]
[ L.y ] = [ Y.x  Y.y  Y.z ]  [ G.y ]
[ L.z ] [ Z.x  Z.y  Z.z ]  [ G.z ]
This is equivalent to L=vec3(dot(X,G),dot(Y,G),dot(Z,G)).

To adjust a rotation matrix, I often just push the coordinate system around.  For example, to push the camera Z direction in the +X direction due to mouse movement, I'd use something like:
   Z' = Z + 0.001*mouse_dx*X;

The scalar in front of X is approximately an angle in radians (for small angles).  Of course, after pushing the coordinate axes around, you need to re-orthonormalize by taking cross products.

Here's a demo of building an object coordinate system and matrix:
   Mouse Matrix Rotation Demo