Rotation Matrices in 3-D
A game world is three-dimensional, and so are its rotations: a camera pitches up, a character
turns to face a door, a die tumbles. The
complex-number trick
was a single multiplication, but matrices generalise to three dimensions immediately — and
they remain the workhorse representation inside a vertex shader. We build the three
elementary rotations about the coordinate axes, then compose them, working in
3-D vectors.
Rotating about the x-axis
Step 1 — rotate about x. Spinning about the
x-axis leaves the x-coordinate
fixed and rotates the (y, z)-plane by
\theta — exactly the 2-D rotation we already know, now living in
the lower-right block:
R_x(\theta) = \begin{bmatrix} 1 & 0 & 0 \\ 0 & \cos\theta & -\sin\theta \\ 0 & \sin\theta & \cos\theta \end{bmatrix}.
The first row and first column are the untouched x-axis: a
1 on the diagonal and zeros around it.
Rotating about the y- and z-axes
Step 2 — rotate about y. Now the
y-coordinate is fixed and the
(z, x)-plane turns. The fixed row/column is the middle one (the
y-axis), and the sign pattern flips compared with the others — a
consequence of the cyclic order x \to y \to z \to x:
R_y(\theta) = \begin{bmatrix} \cos\theta & 0 & \sin\theta \\ 0 & 1 & 0 \\ -\sin\theta & 0 & \cos\theta \end{bmatrix}.
Step 3 — rotate about z. The
z-coordinate is fixed and the
(x, y)-plane turns — the plain 2-D rotation sitting in the top-left
block, with the z-axis (last row/column) untouched:
R_z(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix}.
In each one, the axis you spin about keeps its own coordinate, and the
2 \times 2 rotation block lives in the other two.
Composing for a general rotation
Step 4 — multiply elementary rotations to reach any orientation. A single
product of the three building blocks reaches every rotation in space. For example, pitch then
yaw then roll:
R = R_z(\gamma)\,R_y(\beta)\,R_x(\alpha).
Matrix multiplication composes the spins, and — unlike the plane — order
matters: R_x R_y \ne R_y R_x in general, because 3-D
rotations don't commute. This is precisely
composing transformations
applied to spins, and naming those three angles is the subject of the next page.
It is easy to think of "rotate around x, then
y, then z" and "rotate around
z, then y, then
x" as two descriptions of the same final orientation,
just listed in a different order. They are not — swapping which axis goes first genuinely
changes where the object ends up facing. Try it physically: hold a book flat, tip it
90° forward then 90° sideways, and
note which way the cover faces; now start over and do the sideways tip first, then the
forward tip. The book lands in two different orientations. That is exactly the statement
R_x R_y \ne R_y R_x above — matrix multiplication is not
commutative, so composing rotations is not commutative either, and the axis order is part
of the definition of the rotation, not a cosmetic labelling choice.
Inverting a rotation is free
Step 5 — the columns are an orthonormal right-handed frame. Each column of a
rotation matrix is the image of a basis vector; for a rotation those images are still
mutually perpendicular unit vectors (the spin doesn't stretch or shear). A matrix whose
columns are orthonormal is called orthogonal, and orthogonal matrices have a
defining property:
R^{\mathsf T} R = I.
Step 6 — read off the inverse. That equation says
R^{\mathsf T} is the inverse:
R^{-1} = R^{\mathsf T}.
Inverting a rotation costs nothing but
a transpose —
no determinant, no Gaussian elimination, just swap rows and columns. An engine that needs to
undo a camera rotation a thousand times a frame loves this. And since the frame is
right-handed (a proper rotation, no mirror flip),
\det R = +1.
Rotation about a coordinate axis by an angle \theta is given by one
of three elementary matrices, and:
-
R_x(\theta), R_y(\theta),
R_z(\theta) each fix their own axis and apply a
2\times 2 rotation to the other two coordinates;
-
any orientation is a product of these, e.g.
R = R_z(\gamma) R_y(\beta) R_x(\alpha) — and the product is
order-dependent (3-D rotations don't commute);
-
every rotation matrix is orthogonal:
R^{-1} = R^{\mathsf T}, so inverting a rotation is just a
transpose;
-
its columns are an orthonormal right-handed frame and
\det R = +1 (a proper rotation, no reflection).
Here is the most useful way to read a rotation matrix in a game. Its three columns
are the images of \hat{x}, \hat{y}, \hat{z} — which means they
are exactly the object's own local x, y and z axes expressed in world
coordinates:
R = \big[\; \mathbf{right} \;\big|\; \mathbf{up} \;\big|\; \mathbf{forward} \;\big].
The first column is where the object's right vector points in the world, the second its up
vector, the third its forward. So a rotation matrix isn't an abstract array — it is a
little gimbal-free description of which way the object is facing. Want to know where a
spaceship's nose points? Read off the appropriate column. This is also why
R^{\mathsf T} = R^{-1} is so natural: transposing turns the
columns (the local axes in world space) into rows (the world axes in local space), which is
exactly the reverse transform.
Turn a 3-D frame
Pick an axis — x, y or
z — and drag the angle. The little right-handed frame (its red
x, green y and blue
z arrows) spins about the chosen axis, shown in a simple isometric
projection. The axis you select stays put while the other two arrows swing around it — the
geometric meaning of "this row/column is fixed".