From sphere to quadratic, line by line
A sphere with centre C and radius r is
every point exactly r away from C.
Squaring the distance (to dodge a square root) gives the implicit equation:
\lVert P - C\rVert^2 = r^2.
Step 1 — substitute the ray. A point is on the sphere when it satisfies
this; a point is on the ray when it equals O + tD. Asking for both
at once means plugging the ray in for P:
\lVert (O + tD) - C\rVert^2 = r^2.
Step 2 — name the offset. Let M = O - C, the vector
from the sphere's centre to the ray's origin. The bracket becomes
tD + M:
\lVert tD + M\rVert^2 = r^2.
Step 3 — expand the squared length with the dot product. For any vector,
\lVert v\rVert^2 = v\cdot v, and the
dot product
distributes just like ordinary multiplication. So
\lVert tD + M\rVert^2 = (tD + M)\cdot(tD + M) expands to:
(D\cdot D)\,t^2 + 2\,(D\cdot M)\,t + (M\cdot M) = r^2.
Step 4 — move r^2 over and read off a quadratic in
t. Everything except t is a known
number, so this is at^2 + bt + c = 0 in disguise:
\underbrace{(D\cdot D)}_{a}\,t^2 + \underbrace{2\,(D\cdot M)}_{b}\,t + \underbrace{(\lVert M\rVert^2 - r^2)}_{c} = 0.
(If the direction is a unit vector then a = D\cdot D = 1, tidying it
further.) Solve it with the quadratic formula:
t = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}.
Step 5 — let the discriminant decide whether you hit. The term under the root,
\Delta = b^2 - 4ac, is the entire hit test, and its sign reads off
geometrically:
\Delta < 0\ (\text{miss}), \qquad \Delta = 0\ (\text{tangent — a graze}), \qquad \Delta > 0\ (\text{two hits}).
A negative discriminant means no real t — the ray sails past the
ball. Zero means the ray just kisses the surface at one point. Positive means it punches
clean through: the smaller root is where it enters, the larger where it
exits.
Step 6 — take the nearest hit in front of you. Of the (up to two) roots, we
want the closest surface the camera can actually see: the smallest
t > 0. A root with t \le 0 is
behind the origin (or the origin sits inside the ball) and is rejected. Plug that
t back into the ray to get the hit point itself:
P_{\text{hit}} = O + t\,D.
For a ray P(t) = O + tD and a sphere
\lVert P - C\rVert^2 = r^2, write
M = O - C. Then:
-
Substitute → quadratic — the hit parameters solve
(D\cdot D)t^2 + 2(D\cdot M)t + (\lVert M\rVert^2 - r^2) = 0.
-
Discriminant = hit test — with
\Delta = b^2 - 4ac:
\Delta < 0 miss,
\Delta = 0 tangent,
\Delta > 0 two hits (enter / exit).
-
Nearest visible hit — take the smallest root with
t > 0; discard roots with t \le 0.
-
Hit point — the surface point is
P_{\text{hit}} = O + t\,D.
The quadratic is bulletproof, but there's a slicker route that skips the algebra when the
direction is a unit vector. Drop the centre C onto the ray by
projecting: the parameter of the closest approach is
t_{ca} = (C - O)\cdot D,
the foot of the perpendicular from C to the ray. The squared
distance from C to the ray at that point is, by Pythagoras,
d^2 = \lVert C - O\rVert^2 - t_{ca}^2.
If d^2 > r^2 the ray misses entirely — done, no square root, no
formula. Otherwise the chord half-length is
h = \sqrt{r^2 - d^2}, and the two hits sit symmetrically about the
closest approach at t = t_{ca} \pm h. It is the very same answer
the discriminant gives — \Delta > 0 is exactly
d^2 < r^2 — just reorganised so the cheap rejection test happens
first. Engines love this: most rays miss most spheres, and the early-out saves the square
root on the common case.