From naive blend to the great-circle arc
Step 1 — the naive try: lerp the components. The obvious idea is straight-line
interpolation, treating the four numbers as a vector:
\operatorname{lerp}(q_0, q_1, t) = (1 - t)\,q_0 + t\,q_1, \qquad t \in [0, 1].
At t=0 it is q_0, at
t=1 it is q_1 — good endpoints. But a unit
quaternion must lie on the 4-D unit sphere, and the straight chord between two points on a sphere cuts
through it, so the blend is no longer unit-length.
Step 2 — patch it: NLERP (normalised lerp). Just renormalise each step,
\operatorname{nlerp}(q_0, q_1, t) = \frac{(1-t)\,q_0 + t\,q_1}{\,|(1-t)\,q_0 + t\,q_1|\,}.
This stays on the sphere and is cheap. But because the chord is being projected back out to the sphere,
equal steps in t do not give equal steps in angle: the motion
speeds up in the middle and lingers near the ends. Fine for a quick blend, wrong for a
camera that should pan at constant rate.
Step 3 — do it right: walk the great-circle arc. The honest path between two points on
a sphere is the shortest great-circle arc joining them, traversed at constant
angular velocity. Call the angle between q_0 and
q_1 on the 4-D sphere \Omega; we want a point that
sweeps from q_0 to q_1 along that arc, covering an
equal slice of \Omega for each equal slice of t.
Step 4 — find the angle from the dot product. Both quaternions are unit vectors in
\mathbb{R}^4, so their
dot product is the
cosine of the angle between them:
\cos\Omega = q_0 \cdot q_1 = w_0 w_1 + x_0 x_1 + y_0 y_1 + z_0 z_1.
Step 5 — the SLERP formula. The constant-speed point on the arc is the
sine-weighted combination
\operatorname{slerp}(q_0, q_1, t) = \frac{\sin\big((1-t)\,\Omega\big)}{\sin\Omega}\,q_0 + \frac{\sin\big(t\,\Omega\big)}{\sin\Omega}\,q_1.
Check the endpoints. At t=0: the first weight is
\sin\Omega/\sin\Omega = 1 and the second is
\sin 0/\sin\Omega = 0, giving exactly q_0. At
t=1: the weights flip, giving exactly q_1. In
between, because the weights are sines of angles linear in t, the
result traces the arc at a steady angular rate — constant speed, by construction. (The two scalar
weights are exactly the coefficients that decompose the arc point in the
\{q_0, q_1\} plane; they are the spherical analogue of
(1-t) and t, and as
\Omega \to 0 they reduce to them.)
Step 6 — take the short way (the double-cover sign flip). Here is the one quaternion
gotcha. A quaternion and its negative represent the same rotation:
q and -q rotate identically (the unit quaternions
double-cover the rotations). So q_1 and
-q_1 are the same destination but sit on opposite sides of the
sphere. If q_0 \cdot q_1 < 0 the arc to q_1 goes the
long way round (more than 90° through orientation space); flipping the
sign,
\text{if } q_0 \cdot q_1 < 0: \quad q_1 \leftarrow -q_1,
picks the nearer representative so SLERP takes the genuinely shortest path. Skip this and your camera
occasionally takes the scenic 270° route to a pose 90°
away — a classic engine bug.
For unit quaternions q_0, q_1 and t \in [0,1],
spherical linear interpolation is
\operatorname{slerp}(q_0, q_1, t) = \frac{\sin\big((1-t)\Omega\big)}{\sin\Omega}\,q_0 + \frac{\sin\big(t\Omega\big)}{\sin\Omega}\,q_1.
-
It walks the shortest great-circle arc on the 4-D unit sphere from
q_0 to q_1.
-
The arc angle comes from the dot product: \cos\Omega = q_0 \cdot q_1.
-
Endpoints are exact: \operatorname{slerp}(\cdot,\cdot,0) = q_0 and
\operatorname{slerp}(\cdot,\cdot,1) = q_1.
-
It moves at constant angular velocity — equal t steps are
equal angle steps.
-
Double-cover sign flip: if q_0 \cdot q_1 < 0, replace
q_1 by -q_1 (same rotation, nearer point) to take
the short way.
NLERP and SLERP reach the same endpoints and trace the same arc — the difference is purely the
timing along it.
-
SLERP moves at constant angular speed: rock-steady, the right choice for a
camera pan, a turret tracking a target, or any motion the eye reads as "smooth and even".
The cost is two \sin evaluations (and a divide) per sample.
-
NLERP is a lerp plus one normalisation — much cheaper, and commutative and
associative (handy for blending several poses at once). Its angular speed is uneven (faster in the
middle), but for short blends, or many skinned-mesh bones where the wobble is imperceptible
and the budget is tight, that error is invisible and the speed wins.
Rule of thumb engines actually follow: NLERP for cheap, high-volume animation blends with small angles;
SLERP when the path is long or the constant rate is visible (cinematic camera moves). A common hybrid
even SLERPs only when q_0 \cdot q_1 shows a wide angle and NLERPs otherwise.
It's tempting to think "lerp the four quaternion numbers, then renormalise" (that's NLERP) is
SLERP — after all, both start exactly at q_0, end exactly at
q_1, and, as above, trace the very same great-circle arc. The catch is
speed, not path. Plain lerp draws a straight chord through the inside of the
4-D sphere; snapping that chord back onto the sphere by dividing by its length bunches points up near
the ends and stretches them apart in the middle, because the chord passes closer to the sphere's surface
near t=0,1 than at t=0.5. So a rotation that
should sweep at one constant rate visibly speeds up through the middle of the turn — a
wobble that's easy to miss in a code review (both formulas "work" and land on the right endpoints) and
easy to spot on screen the moment a camera or a limb has to swing through a wide angle at an even pace.
True SLERP's \sin-weighted formula, not a lerp-and-renormalise shortcut, is
what actually holds the angular speed constant end to end.