Skip to content

The optics

magmacrunchmedia edited this page Aug 28, 2026 · 2 revisions

The optics

Every law the engine implements, the convention it is written in, and the test that pins it. Where a formula is a simplification, this page says so plainly. The point of the engine is that you can tell the difference.

Source: source/linalg.c, source/polar.c, source/spectrum.c, source/geometry.c.


Reflection

hv3_reflect(d, n)  =  d − 2(d·n)n

d arrives (unit, pointing into the surface), n is the surface normal on d's side. Length is preserved exactly.

Pinned by: 45° in gives 45° out; normal incidence returns along itself; the result stays unit. Also, at the scene level, the mirror-image property: looking through a perfect mirror at an object equals looking directly at that object's mirrored position, which catches a sign error the single-surface tests would not.


Refraction, by Snell's law

hv3_refract(d, n, eta, &out)      eta = n_from / n_to

k    = 1 − eta²(1 − cos²θi)          cosθi = −n·d
out  = eta·d − (eta·(−cosθi) + √k)·n

Returns 0 when k < 0: the transmitted angle would need sin > 1, which is total internal reflection by definition, and the caller must reflect instead.

Pinned by: 30° into n=1.5 glass emerging at 19.4712° (x-component exactly 1/3); normal incidence passing straight through at any eta; a ray at 41° inside n=1.5 glass escaping at the Snell angle while one at 43° is trapped, bracketing the 41.8103° critical angle from both sides.


The Fresnel equations

The real pair, not Schlick's approximation. holo_fresnel() returns power reflectances; holo_fresnel_amp() returns amplitudes plus the machinery polarization needs.

Rs = |(n₁cosθi − n₂cosθt) / (n₁cosθi + n₂cosθt)|²
Rp = |(n₁cosθt − n₂cosθi) / (n₁cosθt + n₂cosθi)|²

holo_fresnel_amp() additionally returns the transmission amplitudes ts, tp, and the power-projection factor

f = (n₂cosθt) / (n₁cosθi)          so that   f·ts² = 1 − rs²

which is what makes the transmitted Mueller matrix conserve energy without a fudge factor.

Past the critical angle the function returns 1 (TIR) and reports the phase difference the reflection imposes:

δ = δp − δs = 2·[ atan(g / (n²cosθi)) − atan(g / cosθi) ]
      n = n₂/n₁ < 1,   g = √(sin²θi − n²)

Pinned by: 4% at normal incidence on n=1.5; a vanishing p-component at Brewster's angle atan(1.5) = 56.31°, where the reflection is completely polarized (degree of polarization exactly 1); reciprocity across the interface at the paired Snell angle; Rs = Rp = 1 past the critical angle; R + T = 1 per polarization; and δ = 36.9° for glass-to-air at 45°, the number a Fresnel rhomb is cut to exploit.

Sources are unpolarized, so where the engine needs a single scalar it uses (Rs + Rp)/2. The two are kept separate everywhere else because polarization needs them apart.


Polarization: Stokes and Mueller

Every source in hologram is unpolarized, so a camera path never needs the full 4×4 Mueller product, only its first row. Each spectral ray therefore carries a HoloSRow (the accumulated detector row, four floats) plus a reference frame: the unit transverse vector its Q axis is measured against.

srow' = srow · M

When a path reaches a source of intensity S, the camera sees S · srow.i.

The operations

holo_srow_rotate(s, c2, s2) Frame rotation, given as the double angle (cos 2θ, sin 2θ).
holo_srow_mueller(s, a, b, c, d) The interface template [a b 0 0; b a 0 0; 0 0 c d; 0 0 −d c]. Fresnel reflection and transmission, retarders and mirrors are all this shape in their own basis.
holo_srow_polarizer(s) Ideal linear polarizer along the frame.
holo_frame_rot(frame, target, dir, &c2, &s2) The double angle rotating one frame onto another about dir, from two dot products, with no atan in the hot path.

How a surface uses them

The s vector of the plane of incidence, ŝ = normalize(d × n), is the basis both Fresnel branches speak. The walk rotates the ray's frame onto ŝ, applies the Mueller matrix built from the Fresnel amplitudes, and carries ŝ forward as the new frame. At normal incidence there is no plane of incidence and no rotation to perform, so the frame is carried unchanged.

Reflection uses a = ½(rs²+rp²), b = ½(rs²−rp²), c = rs·rp; transmission uses the same shape scaled by f. Under TIR, a = 1 with c = cos δ and d = sin δ, the retardance above, which is how linear light entering a Fresnel rhomb comes out elliptical.

Pinned by: Malus's law at five angles (0°, 30°, 45°, 60°, 90°, giving ½cos²θ); the three-polarizer paradox (crossed = 0, a 45° third between them = exactly 0.125); Brewster giving degree of polarization 1; energy conservation per polarization; quarter- and half-wave plates between crossed polarizers transmitting ½sin²(δ/2); and TIR's 36.9° retardance.

Simplification, stated plainly

Grating orders are weighted by polarization-neutral scalars. A real grating's efficiency is strongly polarization-dependent, and computing it rigorously is a solver's job (see gratinglab) rather than a renderer's.


Spectral rendering

Sampling

HOLO_WAVELENGTHS = 12 samples, evenly spaced 0.42 to 0.68 µm. Fixed, not stochastic: the CPU and GPU must trace identical rays for the oracle diff to mean anything, and a game that renders the same still frame twice should not shimmer.

Dispersion

Cauchy's equation, anchored at the sodium D line so that n(λ_D) = ior exactly, for any B:

n(λ) = n_d + B · (1/λ² − 1/λ_D²)          λ_D = 0.5893 µm

B = 0 is achromatic glass. BK7 is roughly n_d = 1.5168, B = 0.0042; dense flints run several times that.

Pinned by: the D line returning the quoted index exactly; n_F > n_d > n_C; and the Abbe number (n_d − 1)/(n_F − n_C) computing to 64.4 for those BK7 coefficients, against a catalogue value of about 64.2.

Colour

Intensities are folded to linear sRGB through the Wyman–Sloan–Shirley piecewise-Gaussian fits to the CIE 1931 colour matching functions (JCGT 2013), then through the standard XYZ→sRGB (D65) matrix, then normalized per channel so that a flat spectrum lands on exact white. Hue structure comes from the eye; the white point is the engine's choice.

The CPU computes these weights and ships them to the shader in the uniform block. The shader must not re-derive them, because both sides folding the same floats is part of what the oracle certifies.

Simplification, stated plainly

Scene colours stay RGB. An albedo is read at a wavelength through three smooth bands that partition unity (holo_albedo_at), so neutral colours are exact at every wavelength and a gray scene renders identically through the RGB and spectral pipelines. A saturated red albedo will not survive the round trip to the exact same red; that path is not colorimetry. The physics being showcased (dispersion angles, order directions, retardance) does not pass through it at all.


Conic surfaces

A dish is a cap of a conic of revolution, in the parameters optical design quotes: apex, axis (unit, out of the bowl), vertex radius of curvature R, conic constant K, rim radius.

The sag equation cleared of its square root is quadratic along a ray, which is why the intersection is closed form:

x² + y² + (1+K)z² − 2Rz = 0          in the dish's frame, apex at the origin
K Surface
0 Sphere
−1 Paraboloid
−e², −1 < K < 0 Ellipsoid
< −1 Hyperboloid

The normal is the gradient, (x, y, (1+K)z − R), taken back into world axes. The cap is clipped at the rim's sag, which is what keeps an ellipsoid's far half and a hyperboloid's second sheet out of the scene. An axis-parallel ray on a paraboloid degenerates the quadratic to a line; that case is handled explicitly.

Pinned by: a paraboloid with R = 2 reflecting parallel rays at three different zone radii through the focus at exactly R/2, a test that catches a wrong normal as surely as a wrong sag; an ellipsoid (a = 2, e = 0.5, so R = 1.5, K = −0.25) imaging its near focus onto its far one at three angles; and both properties surviving an arbitrary rotation of the dish's frame.

Focusing, made visible

A backward tracer cannot show a caustic by tracing camera rays alone. What it can show is the focal property itself: give the sky a sun disk and stand at a paraboloid's focus, and every point of the dish reflects the eye ray into the sun, so the whole aperture blazes. That is exactly what looking into a real solar furnace from its focus does.


The grating equation

Implemented in the conical (off-plane) vector form, of which the classical in-plane mount is the special case.

Let ĝ be the groove direction (unit, in the surface), the normal, and q̂ = ĝ × n̂ the in-plane dispersion direction. For incident direction d:

α = d·q̂ + mλ/d_spacing        the dispersion component picks up the order
β = d·ĝ                        the groove component is CONSERVED
out = α·q̂ + β·ĝ + √(1 − α² − β²)·n̂

Returns 0 when 1 − α² − β² ≤ 0: the order is evanescent and does not propagate. m = 0 reduces to exact specular reflection, at any skew.

The conservation of β is the conical invariant, the reason an off-plane mount disperses along a cone rather than a plane, and the property the in-plane formula sin θm = sin θi + mλ/d hides by setting β = 0.

Pinned by: m = 0 equalling hv3_reflect component by component at a skew incidence; β conserved and the output unit for a diffracted order; Littrow, where at sin θ = λ/2d the m = −1 order retroreflects exactly, the alignment every grating lab uses; longer wavelengths diffracting further; and an order going evanescent past 90°. At the scene level, an energy audit: three orders propagate at 550 nm through a 1 µm grating, and the second joins the sum at 450 nm, exactly where the equation admits it.

Simplification, stated plainly

Order efficiencies are hand-set scalars per grating, not computed. Real efficiency depends on groove profile, blaze angle, coating and polarization, and getting it right means a rigorous solver (integral method, RCWA, C-method). The renderer needs the equation; the efficiencies are art direction.


What the engine does not model

Stated so nobody mistakes an omission for a claim:

  • Caustics and indirect light. No photon mapping, no bidirectional paths. Light reaches the camera along camera rays only; a glass ball casts no bright spot, and mostly-clear glass is given no hard shadow rather than a wrong black disc.
  • Interference between paths. Stokes vectors carry polarization, not optical path length. Thin-film colour, Newton's rings and speckle are out of reach; a waveplate's colour comes from its 1/λ retardance, which is a single-element effect.
  • Diffraction as a wave phenomenon. Gratings are handled by the grating equation, which tells you where orders go, rather than by solving Maxwell's equations. No Airy disc, no edge diffraction, no aperture-limited resolution.
  • Absorption with depth. Glass tint is applied per interface, not by Beer–Lambert through path length.
  • Curved refractive surfaces. Dishes are mirror or matte; there are no lenses with conic figure yet. Refraction happens at spheres (volumes) and rectangles (thin panes).