A worked example: translate-then-rotate vs rotate-then-translate
Take a concrete case. Let R = R(90^\circ) = \left[\begin{smallmatrix} 0 & -1 \\ 1 & 0 \end{smallmatrix}\right]
(a quarter-turn) and \vec{t} = (3, 0) (slide three to the right). Apply
both to the point \vec{p} = (1, 0), in each order.
Step 1 — translate, then rotate. First slide, then turn the result about the
origin:
R(\vec{p} + \vec{t}) = R\begin{bmatrix} 4 \\ 0 \end{bmatrix} = \begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix} \begin{bmatrix} 4 \\ 0 \end{bmatrix} = \begin{bmatrix} 0 \\ 4 \end{bmatrix}.
Step 2 — rotate, then translate. First turn about the origin, then slide:
R\vec{p} + \vec{t} = \begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix} \begin{bmatrix} 1 \\ 0 \end{bmatrix} + \begin{bmatrix} 3 \\ 0 \end{bmatrix} = \begin{bmatrix} 0 \\ 1 \end{bmatrix} + \begin{bmatrix} 3 \\ 0 \end{bmatrix} = \begin{bmatrix} 3 \\ 1 \end{bmatrix}.
Step 3 — compare. Same point, same rotation, same translation —
different destinations: (0, 4) versus
(3, 1).
R(\vec{p} + \vec{t}) = (0, 4) \;\neq\; (3, 1) = R\vec{p} + \vec{t}.
Step 4 — see why geometrically. Rotation is always about the
origin. If you translate first, the object is now far from the origin,
so the rotation swings it in a wide arc around the origin — the "hammer thrower". If you
rotate first, the object spins in place at the origin and only then slides out to its
spot — which is what you almost always actually want.
Which way do I read the product?
Step 5 — mind the vector convention. The order you write the matrices
depends on whether your vectors are columns or rows, and the two graphics conventions read in
opposite directions:
-
Column vectors (OpenGL, most maths): the vector is on the
right, M\vec{x}, so the product is read
right-to-left — in T R S\,\vec{x}, scale acts
first.
-
Row vectors (DirectX, older APIs): the vector is on the left,
\vec{x}^{\top} M, so the product is read
left-to-right — in \vec{x}^{\top} S R T, scale acts
first.
Step 6 — note they're transposes of one another. The two write the same pipeline
in reversed order, because
(ABC)^{\top} = C^{\top} B^{\top} A^{\top}. Pick a convention and stick
to it — half of all "my transforms are backwards" bugs are a column/row mix-up.
Because matrix multiplication is not commutative, the order of transforms is load-bearing:
-
In general AB \neq BA — composing transforms in a different order
gives a different result.
-
In particular T \cdot R \neq R \cdot T: rotating
after translating swings the object around the origin, while rotating
before spins it in place and then moves it.
-
Column vectors read the product right-to-left
(M\vec{x}); row vectors read it
left-to-right (\vec{x}^{\top} M). The two are
transposes — fix one convention.
You place a coin at (10, 0) and ask it to spin to show off, so you
set its rotation each frame. But it doesn't spin where it stands — it sails in a huge circle
around the world origin, ten units away. The fix is the whole lesson: you built
R \cdot T (translate first, then rotate, so the rotation orbits the
far-away coin around the origin) when you wanted T \cdot R (rotate
the coin in place at the origin, then translate it out to
(10, 0)).
\underbrace{T \cdot R}_{\text{spins in place}} \quad\text{not}\quad \underbrace{R \cdot T}_{\text{orbits the origin}}.
To spin about the object's own centre when it's already elsewhere, the general recipe
is "translate to the origin, rotate, translate back" —
T(\vec{c})\,R\,T(-\vec{c}) — the same non-commutativity, now
working for you.