A mirror does one thing, perfectly: it takes an incoming ray and sends it back out at the
same angle, on the other side of the surface. A raytracer needs that bounce as a formula —
given the direction a ray is travelling and the
surface normal
it strikes, what direction does it leave in? The whole answer is one line,
\vec{R} = \vec{D} - 2(\vec{D}\cdot\vec{N})\,\vec{N},
where \vec{D} is the incoming direction,
\vec{N} is the unit surface normal, and
\vec{R} is the reflected direction. Let's not assert it — let's
build it, and watch the angle come out equal on its own.
Deriving the bounce, line by line
Step 1 — split the incoming ray into two parts. Any vector hitting the
surface can be written as the part that points along the normal (straight into the
surface) plus the part that lies in the surface (sliding sideways):
\vec{D} = \vec{D}_{\parallel} + \vec{D}_{\perp},
where \vec{D}_{\parallel} runs along
\vec{N} and \vec{D}_{\perp} runs across
it (perpendicular to \vec{N}, i.e. tangent to the surface).
Step 2 — measure the normal part with a dot product. Because
\vec{N} is a unit vector, the
projection
of \vec{D} onto it is just the
dot product
\vec{D}\cdot\vec{N} times \vec{N}:
\vec{D}_{\parallel} = (\vec{D}\cdot\vec{N})\,\vec{N}.
Step 3 — the surface part is whatever's left over. Rearranging Step 1,
\vec{D}_{\perp} = \vec{D} - \vec{D}_{\parallel} = \vec{D} - (\vec{D}\cdot\vec{N})\,\vec{N}.
Step 4 — reflect by flipping only the normal part. Here is the entire idea
of a mirror. A bounce reverses the component going into the wall while leaving the
sideways slide untouched — the ray skids along the surface the same way, but its "into the
wall" motion becomes "out of the wall". So negate
\vec{D}_{\parallel} and keep
\vec{D}_{\perp}:
\vec{R} = \vec{D}_{\perp} - \vec{D}_{\parallel}.
Step 5 — substitute the two pieces back in. Putting Step 2 and Step 3 into
Step 4,
\vec{R} = \big[\vec{D} - (\vec{D}\cdot\vec{N})\,\vec{N}\big] - (\vec{D}\cdot\vec{N})\,\vec{N}.
Step 6 — collect the two identical terms. Both carry the factor
(\vec{D}\cdot\vec{N})\,\vec{N}, so they add to twice it:
\vec{R} = \vec{D} - 2(\vec{D}\cdot\vec{N})\,\vec{N}.
That's the reflection formula — three multiplies and a subtract per axis, and it's the
beating heart of every mirror in every raytraced frame.
Step 7 — check the angle really is preserved
A bounce should obey "angle in equals angle out". First, reflection keeps the ray's length:
squaring \vec{R} and expanding the dot product,
\vec{R}\cdot\vec{R} = \vec{D}\cdot\vec{D} - 4(\vec{D}\cdot\vec{N})(\vec{D}\cdot\vec{N}) + 4(\vec{D}\cdot\vec{N})^2\,(\vec{N}\cdot\vec{N}).
Since \vec{N} is a unit vector,
\vec{N}\cdot\vec{N} = 1, so the last two terms cancel and
\lVert\vec{R}\rVert = \lVert\vec{D}\rVert. Next, the angle each ray
makes with the normal is read off by dotting with \vec{N}:
\vec{R}\cdot\vec{N} = \vec{D}\cdot\vec{N} - 2(\vec{D}\cdot\vec{N})(\vec{N}\cdot\vec{N}) = \vec{D}\cdot\vec{N} - 2(\vec{D}\cdot\vec{N}) = -(\vec{D}\cdot\vec{N}).
Same magnitude, opposite sign: the outgoing ray leans away from the normal by exactly the
angle the incoming ray leaned toward it. Angle of incidence = angle of
reflection, proven, not assumed.
Let \vec{D} be an incoming direction striking a surface with
unit normal \vec{N}. The reflected direction is
\vec{R} = \vec{D} - 2(\vec{D}\cdot\vec{N})\,\vec{N},
and it has these properties:
-
It flips only the normal component of \vec{D}
(the part along \vec{N}) and leaves the tangential (in-surface)
part unchanged.
-
Angle of incidence = angle of reflection, since
\vec{R}\cdot\vec{N} = -(\vec{D}\cdot\vec{N}) and
\lVert\vec{R}\rVert = \lVert\vec{D}\rVert.
-
It needs a unit \vec{N}. The
\vec{N}\cdot\vec{N} = 1 step is what makes the formula exact;
an un-normalized normal scales the bounce wrongly.
The same bounce powers two things that look different on screen. A mirror
(a perfectly smooth surface) reflects the scene: the raytracer follows
\vec{R} off into the world and shows whatever it hits — this is
the recursive reflection ray you'll meet later.
A specular highlight — that bright glint on a glossy apple — is the same
reflection applied to the light rather than the scene. The surface looks brightest
where the reflected light direction points straight at your eye, so the glint sits exactly
where \vec{R} lines up with the view. Both are
\vec{R} = \vec{D} - 2(\vec{D}\cdot\vec{N})\,\vec{N} wearing
different costumes; we'll meet the highlight again in
the lighting equation.