Shadows, Reflection, and Refraction

Shadows, Reflection, and Refraction

Secondary rays extend the same visibility infrastructure used for primary rays to shadows, reflection, and refraction. Their introduction also exposes self-intersection artifacts whenever the ray origin bias and valid interval are treated imprecisely.

After a hit you may cast:

  • a shadow ray toward a light
  • a reflection ray
  • a refraction ray

All of these ray types should reuse the same scene-intersection interface, because this common representation of visibility is the architectural advantage of the ray-tracing formulation.

Incoming ray spawning shadow reflection and refraction rays

Shadow, reflection, and refraction rays leave the hit with a small geometric bias.

Shadow rays

origin = p + n_geo * bias
dir    = normalize(light - origin)
t_max  = distance(light, origin) - bias
occluded = any_hit(origin, dir, t_min, t_max)

The offset should be applied along the geometric normal rather than a perturbed shading normal, since a shading normal may point into the mesh and displace the new ray beneath the surface. Because a shadow query asks only whether any obstruction exists, an any-hit traversal is sufficient and avoids the unnecessary work of a closest-hit search.

Reflection

wi = reflect(ray.direction, n)   // obey your vector convention
origin = p + n_geo * bias
color += weight * trace(origin, wi, depth + 1)

Mirrors are the Dirac case: one direction, no lobe sampling. Glossy reflection needs a sampled direction and a PDF. The bias rule does not change.

Refraction and Fresnel

Snell's law relates angle sines through the index ratio η = ior_in / ior_out. Inside a medium the ratio flips. If the transmitted sine would exceed one, total internal reflection occurs: no refraction ray exists, only reflection.

Fresnel reflectance distributes energy between reflection and transmission. Schlick's approximation is acceptable in a teaching tracer:

$$ \begin{aligned} R_0 &= \left(\frac{1-\eta}{1+\eta}\right)^2 \ F &= R_0 + (1-R_0)(1-\cos\theta)^5 \end{aligned} $$

Either cast both paths with weights, or randomly choose reflection with probability F and refraction with probability 1 - F and divide by that probability. Dishonest weights bias the image.

Bias and acne

Too little bias produces shadow acne and reflected self-hits. Too much bias opens light leaks at contact. Start near 1e-4 in unit-scale scenes and revise after close-ups. Transmission rays often bias opposite reflection rays.

Nested media

Two glass objects require a medium stack or at least a current IOR. Entering pushes a medium. Leaving restores the previous one. Without that bookkeeping, a ray through a window into a solid glass object uses the wrong η on exit and the interior collapses.

Colored glass may multiply throughput by a distance-dependent absorption factor inside the medium. Thin windows often skip absorption and model a single interface.

Soft shadows

A spherical light with many shadow rays is already Monte Carlo integration. One sample yields a hard edge. Many samples yield a noisy penumbra. The estimator identity is the same as in the sampling chapter. Stabilize hard shadows before pursuing soft ones.

Common errors

  • Offsetting along a smoothed normal that dives into the surface
  • Leaving t_min = 0 on secondary rays
  • Forgetting to swap IOR when the ray is inside
  • Ignoring total internal reflection
  • Applying Fresnel twice in lobe and recursion

When secondary rays misbehave, color the first bounce by ray type. The ridiculous image usually names the bug in one glance.

Energy bookkeeping

Reflection and refraction split energy. If both paths are traced, weights must sum in a way consistent with Fresnel. If a single path is chosen stochastically, divide by the selection probability. A common implementation error is to make the random choice and omit the division. The result is a darker dielectric that “looks okay” until compared with a reference.

Shadow terminator note

Low-polygon smooth shading can self-shadow near the terminator because geometric and shading normals disagree. Bias helps. So does not shadowing with a shading normal that points away from the light while the geometry still faces it. Be explicit about which normal each test uses.