Symbolic Rendering

Appendix A — The Contract

An explainer comparing numerical rendering with symbolic rendering,
in the spirit of Utah graphics meets Macsyma (today: Python + sympy).
Dr. L. Van Warren — Original Idea & Mathematical Development
Kimi K3 — Assistance with Mathematical Development
Single HTML5 file · all computation is local · the archive notebook is Appendix B
Requires an internet connection for mathematical typesetting (MathJax).
How to use this file. §1 states the contract — the claims and the non-claims. §2 is the Table of Named Theorems, all ten chapters in one list. §3 collates the regression chain. §4 collates the ledgers. §5 is the Health Battery: sixteen checks, live, with numbers — the smoke alarm for the whole series. §6 scopes path space honestly. §7 is the Errata & Confessions. §8–§9 close the book. The Python cell in the laboratory is the Contract Checker — a preview of Appendix B.
☰  contents
  1. The contract, stated
  2. Table of named theorems
  3. The regression chain
  4. The ledger, collated
  5. The health battery (live)
  6. Path space, scoped honestly
  7. Errata & confessions
  8. A brief history, concluded
  9. The road ends; further reading

§1 · The contract, stated

What this series claims. For the scenes it covers — spheres, a plane, point and disk lights, hard and soft shadows, one mirror bounce, texture by chart and by bitmap, bump by height gradient, finite pixels, finite shutters, finite pupils — the image is a mathematical object: a chambered, piecewise-algebraic, differentiable expression. Both pipelines compute it; the numeric pipeline estimates it by runtime branches and samples, the symbolic pipeline carries it as structure. Their agreement is structural (interior max ~10⁻¹⁵), their disagreements are named (flips on boundary curves; variance vs bias in estimator chapters), and every extension degenerates to its predecessor in a documented limit, machine-checked.

What it never claims. That symbolic evaluation is faster than engineered rasterization (it is not); that closed forms survive general scenes (they die at bounce 2, at bitmaps, at quartic shadow times); that a black difference canvas certifies correctness (it certifies formulation agreement — correctness is the theorems); that the symbolic pipeline replaces sampling (it prices sampling, and samples where the boundary algebra runs out).

The discipline, in one sentence. Derive the anchor, run the regression, report the error in both currencies, and let the page show its work.

§2 · Table of named theorems

Name & statementCh.
1.1The Silhouette Theorem. The sphere's image is exactly circular: ρ = eR/√(e²−R²).1
1.2The Closed Forms. ν = √Δ/(R√A); r·V̂ = 2μν − ŵ·V̂ — no new square roots.1
2.1Disable-by-Substitution. A light is removed by c₂→0, not by a branch; verified to 1e-12.2
2.2The Affinity Theorem. The image is exactly affine in each light's color: ∂²I/∂c² = 0.2
3.1The Shadow Lemma. Shadowed ⟺ Δ_s ≥ 0 ∧ b₂ < 0 ∧ a₂+b₂ > 0 — three signs, no square root.3
3.2The Two-Number Ledger. With branches, report interior max and flip census; a single max lies.3
4.1Reflection Is Substitution. The reflected ray is the hit-point expression composed into the ray formula.4
4.2The Mirror Flip. Reflection across z=0 is a change of variables; mirrored-scene = bounce path to 1e-15.4
4.3The Exact Strict Test. The self-hit root factors out (t′=0 exactly); strict t′>0 excludes it, no ε.4
5.1The Longitude Theorem. atan2(N_y, N_x) = atan2(y, x) exactly; the seam renders as the slit y=0, x<0.5
5.2The Pole Corollary. N_z depends on r² alone: constant-v curves are circles; the pole is the window's center.5
5.3Gamma Non-Commutativity. E(a+b) ≠ E(a)+E(b): lighting in gamma space is a measured error, not a style.5
5.4The Bitmap Is a Piecewise Function. Nearest = floor-arithmetic; bilinear = tent polynomials; both exact as objects.5
6.1The Checker Box Integral. The floor's coverage is (G(x₁)−G(x₀))(G(y₁)−G(y₀))/h² with G a triangle-wave integral — no sampling.6
6.2The Coverage Doctrine. Exact where boundaries are algebraic; sampled where they are not; the band is ~2% of the frame.6
7.1The On-Axis Anchor. μ̄ = 2d(√(d²+ρ²)−d)/ρ²; the point light is its ρ→0 limit.7
7.2The Conic Penumbra. The shadow's edge on the source is the tangent cone × the light plane — a conic, classified per point.7
8.1The Mirror Anchor. n→∞ collapses the lobe onto Chapter 4's mirror direction.8
8.2Exact Lambert Weights. w = Δφ(cosⁿ⁺¹θ_a − cosⁿ⁺¹θ_b)/(n+1): the stratum geometry in closed form.8
8.3The Roughness Ruler. σ_θ ≈ 1/√(2n): blur has units.8
9.1Bump Moves No Boundary. Chambers are geometry-only; the flip census is independent of β — zero by theorem.9
9.2The U-Curve. Central differences err by ε²h‴/6 + O(ε_mach/ε): a truncation slope and a roundoff cliff, both measured.9
9.3∂N′/∂β in Closed Form. (I − N′N′ᵀ)g/|M|, verified to 1e-9 over 2000 random trials.9
10.1The Switching-Time Quadratic. A linearly-moving sphere's visibility mask is a quadratic in t: exact temporal coverage.10
10.2The Bokeh Law. The defocus footprint is an exact scaled aperture: ρ = a·e·|f−z₀|/((e−z₀)(e−f)).10
10.3The Swept Silhouette. The motion outline is algebraic, degree ≤ 4.10
10.4The Quartic Shadow. The moving shadow's mask is degree 4 in t — the exact frontier's surveyed edge.10

§3 · The regression chain

Every extension must degenerate to its predecessor in a documented limit. The chain, all links machine-checked in their chapters:

ChapterLimitReturnsChecked
2 — two lightsc₂ → 0Ch. 1, one light1e-12 (Cell 5)
3 — two spheres, shadowsremove BCh. 2boot battery
4 — mirror bouncek_r → 0Ch. 3bounce view isolates the term
5 — texture, gammasolid τ, raw displayCh. 4 pipelinechart census (Cell 3)
6 — the box filterM = 1 / point filterCh. 5 point samplingexactly (same code path)
7 — area lightρ → 0 / K = 1Ch. 5 point lightmachine precision (Cell 6)
8 — glossy loben → ∞Ch. 4 mirroranchor sweep (Cell 6)
9 — bumpβ → 0Ch. 5~1e-16 (Cell 6)
10 — aperture, shuttera → 0, v → 0Ch. 5 still pinhole~1e-12 (Cell 6)

An unbroken chain from one sphere and one light to a five-dimensional product integral — each link a theorem-shaped promise that the new machinery is a conservative extension of the old.

§4 · The ledger, collated

Ch.AddedPaidConfessed
1The image as expression; silhouette theoremderivation seconds; swellfloat-ordering noise is not zero
2RGB, two lights, Blinntriple swell (cse-clawed)Blinn's ungated highlight
3Shadow Lemma; chambers; flipstotality tax (~2×)self-shadow knife edge excluded
4Reflection by composition; strict testcomposition swell (~3×)ε-hack footprint measured, not adopted
5Chart theorems; bitmap; gamma ordertranscendental chartbitmap escapes CAD; ledger ≠ correctness
6Exact floor integral; coverageband machinerycoverage kink at the clamp; classifier is a heuristic
7Source integral; conic; two currenciesK² both sidesmidpoint/strata banding; seed vs determinism
8Lobe integral; exact weightsK² × source loophorizon truncation; starvation at high n; units
9Exact gradient; two-axis winnone (the rest stop)closed-form fields only; pole fallback; bump is a fiction
10Pupil + shutter; switching times; bokeh law5-D quadraturemidpoint shading tax; quartic shadows stratified

§5 · The health battery

Every boot-time verification from the ten chapters, re-run now, on this page, with its measured number. This is the smoke alarm, not the proof — the proofs live in the chapters' §3 sections; the 200,000-sample verifications live in the Colab cells.

standing by…
chcheckmeasuredverdict

§A·5b · The laboratory: the Contract Checker (Appendix B preview)

One cell, five headline theorems, re-verified independently in Python. Appendix B will carry the complete archive — every check from every chapter as one notebook. Three run paths — 📋 Copy code, ⬇ notebook, or paste into colab.new.

🐍 The Contract Checker — five theorems, re-verified

· · paste into colab.new → Run
import numpy as np, math
rng = np.random.default_rng(2024)

# --- 1. The Shadow Lemma (Ch.3): sign logic == explicit roots --------------------
ok = 0; tot = 0
for _ in range(400):
    P = rng.normal(size=3)*2; L = rng.normal(size=3)*4 + [0, 0, 6]
    C = rng.normal(size=3)*1.5; Rb = rng.uniform(0.2, 1.5)
    seg = L - P; PmC = P - C
    a2 = seg @ seg; b2 = PmC @ seg; c2 = PmC @ PmC - Rb**2
    dsc = b2*b2 - a2*c2
    if c2 < 0 or a2 + 2*b2 + c2 < 0 or abs(dsc) < 1e-9: continue
    sq = math.sqrt(max(dsc, 0)); s1, s2 = (-b2 - sq)/a2, (-b2 + sq)/a2
    if min(abs(s1), abs(s1 - 1), abs(s2), abs(s2 - 1)) < 1e-7: continue
    roots = dsc >= 0 and ((0 < s1 < 1) or (0 < s2 < 1))
    signs = dsc >= 0 and b2 < 0 and a2 + b2 > 0
    tot += 1; ok += (roots == signs)
print(f"1. Shadow Lemma: {ok}/{tot} {'PASS' if ok == tot else 'FAIL'}")

# --- 2. The checker box integral (Ch.6): closed form vs quadrature ----------------
def G(v):
    m = math.floor(v); f = 1 - abs(1 - 2*(v - m))
    return m + (f*f if m % 2 == 0 else 1 - f*f)
worst = 0
for _ in range(200):
    x0, y0 = rng.uniform(-20, 20, 2); h = float(rng.uniform(0.01, 2))
    exact = (G(x0 + h) - G(x0))*(G(y0 + h) - G(y0))/h**2
    K = 48
    xs = x0 + (np.arange(K) + .5)/K*h; ys = y0 + (np.arange(K) + .5)/K*h
    quad = np.sign(np.sin(np.pi*xs)[:, None]*np.sin(np.pi*ys)[None, :]).mean()
    worst = max(worst, abs(exact - quad))
print(f"2. Checker integral: worst |delta| {worst:.2e} {'PASS' if worst < 2e-3 else 'FAIL'}")

# --- 3. The on-axis anchor (Ch.7): mu-bar closed form vs quadrature ----------------
d, rho = 6.0, 0.45
exact = 2*d*(math.sqrt(d*d + rho*rho) - d)/rho**2
K = 20000; rr = (np.arange(K) + .5)/K*rho
quad = np.mean(d/np.sqrt(d*d + rr*rr))
print(f"3. On-axis anchor: exact {exact:.8f} quad {quad:.8f} "
      f"{'PASS' if abs(exact - quad) < 1e-6 else 'FAIL'}")

# --- 4. Exact Lambert weights (Ch.8): vs quadrature --------------------------------
worst = 0; n = 16; thm = math.pi/3
for k in range(8):
    ta, tb = k/8*thm, (k + 1)/8*thm
    wE = (math.cos(ta)**(n + 1) - math.cos(tb)**(n + 1))/(n + 1)
    tt = np.linspace(ta, tb, 4000)
    wQ = np.trapezoid(np.cos(tt)**n*np.sin(tt), tt)
    worst = max(worst, abs(wE - wQ))
print(f"4. Lambert weights: worst |delta| {worst:.2e} {'PASS' if worst < 1e-8 else 'FAIL'}")

# --- 5. Switching-time coverage (Ch.10): exact interval vs scan --------------------
Cb = np.array([0.5, 0.5, 1.3]); vel = np.array([-0.55, -0.35, 0.35]); R2, e = 0.32, 4.0
worst = 0
for _ in range(100):
    x, y = rng.uniform(-1, 1, 2)
    D = np.array([x, y, -e]); Aq = D @ D
    w0 = np.array([0., 0., e]) - Cb
    b0 = w0 @ D; vd = -vel @ D
    al = vd*vd - Aq*(vel @ vel)
    be = -2*b0*vd + 2*Aq*(w0 @ vel)
    ga = b0*b0 - Aq*(w0 @ w0 - R2**2)
    dsc = be*be - 4*al*ga
    exact = 0.0
    if dsc > 0 and abs(al) > 1e-14:
        sq = math.sqrt(dsc)
        lo, hi = sorted(((-be - sq)/(2*al), (-be + sq)/(2*al)))
        if al < 0: exact = max(0.0, min(1.0, hi) - max(0.0, lo))
    ts = np.linspace(0, 1, 400)
    scan = np.mean([1 if (lambda tt: (w0 - tt*vel) @ D)**2 >= 0 and
                    (((w0 - tt*vel) @ D)**2 - Aq*((w0 - tt*vel) @ (w0 - tt*vel) - R2**2)) >= 0
                    else 0 for tt in ts] and
                   [(((w0 - tt*vel) @ D)**2 - Aq*((w0 - tt*vel) @ (w0 - tt*vel) - R2**2)) >= 0
                    for tt in ts])
    worst = max(worst, abs(exact - scan))
print(f"5. Switching times: worst |delta| {worst:.2e} {'PASS' if worst < 6e-3 else 'FAIL'}")
print("\nThe contract holds, independently re-verified. Appendix B carries the rest.")
Expect: five PASS lines. These are the load-bearing theorems of the series, re-verified away from the page they live on.

§6 · Path space, scoped honestly

What resists, named plainly: path recursion (the integrand's chamber structure recurses; Chapter 4's bounce-2 swell was the warning shot), participating media (integrals along rays through chambers — stacked on everything else), general BRDFs (tabulated or measured functions: the bitmap wall, worn by materials), and global illumination (the fixed point of all of the above). In those provinces the symbolic object's job is settled and final: ground truth you can hold, differentiate, and sample. The expression stops being the answer and becomes the thing answers are estimates of — which is what it was from Chapter 1, and the series was the long proof.

§7 · Errata & confessions

In the manner of the Press's other volumes, the record is amended in open court:

§8 · A brief history, concluded

The two traditions this series reunited ran in parallel for fifty years: Warnock–Gouraud–Phong–Blinn–Catmull–Whitted–Cook–Kajiya on one side, Macsyma–Reduce–Maple–Mathematica–sympy on the other. Their reunion was not a stunt. The renderer's decision tree turned out to be piecewise mathematics; the shadow ray turned out to be a Sturm theorem; the texture was a function composition; the antialiasing filter was an integral; the soft shadow, the glossy blur, the bokeh, and the motion trail were the same integral wearing different domains. Every chapter made one such identification and then measured the consequences. The deepest finding is also the plainest: the two pipelines were never alternatives. One computes the object; the other is the object. The ledger kept them honest about which was which.

§9 · The road ends; further reading

Set by the Press · the contract is kept by re-running it.
FINIS LIBRI — but the checks, being re-runnable, continue without us.
Section ids are stable (s1…s9, battery, lab, cell1) — cite the id when requesting revisions.