Task 2 of 5
Before anything can bounce, a ray has to find a surface. The sibling module Ray-Marched Metaballs walks its rays forward in safe hops, because a signed distance field is all it has to go on. A sphere gives you something better: an exact answer, one square root long.
Write the ray as o + t·d with d a unit vector,
put e = o − centre, and asking "where does the ray meet the surface" is asking
when |e + t·d|² = r². Because d is unit, the t²
coefficient is exactly 1 and the whole thing collapses to
b = e · d
c = e · e - r * r
t = -b ± Math.sqrt(b * b - c)
Three cases, and all three matter. A negative discriminant means the ray misses. Two
roots mean it goes in one side and out the other — the one you can see is the
nearer, −b − √(b² − c). And a root at or behind t = 0 is in the
ray's past: the sphere is behind the camera, and it must not be reported as a hit. Reject
anything up to t = 0.001 rather than just the negatives — the next task fires
rays that start on a surface, and in float32 such a ray finds that surface again
at a t a hair above zero.
sphereT so it returns the distance to the
nearest visible hit, or -1 when there isn't one.b = e·d and c = e·e − r * r for e = o − centre-1 when the discriminant b * b - c is not positive-b - Math.sqrt(disc) — and -1 instead when that root is at or behind the ray's origin (t <= 0.001)Three early exits and one answer:
const disc = b * b - c;
if (disc <= 0) {
return -1; // the ray misses this sphere entirely
}
const t = -b - Math.sqrt(disc);
if (t <= 0.001) {
return -1; // the hit is behind us (or right on top of us)
}
return t;The next task fires rays that start on a surface. In float32 the
starting point is never quite on it, and a root at t = 0.000001 is that
same surface hitting itself. The small epsilon is what stops a bounce from being
immediately swallowed by the thing it bounced off.
Nearer is brighter. You should get a pale sphere sitting on a floor that fades with distance, and the flat background colour everywhere the ray found nothing.
rtTrace, Vulkan's ray query) is still one thread,
one ray, one nearest hit. Analytic primitives never went away either: spheres, boxes and
capsules still get intersected in closed form because closed form is faster than a mesh.
This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.