Cover: a lensed accretion disk arcing over a black hole shadow
Real-Time Graphics  ·  General Relativity

Rendering the Unseeable

How to Build a Real-Time Black Hole Simulator — from Schwarzschild Geodesics to Shaders on a Graphics Card

1Introduction to Black Holes

A black hole is embarrassingly simple to define and infuriatingly hard to draw. The definition: a region of spacetime from which no light signal can escape to the rest of the universe. The difficulty: the reason light can't escape is not that gravity "pulls on photons" like a cannonball slowing in flight — it is that mass has bent the geometry through which light travels, so that every straight line near the hole leads inward. A simulator that merely applies a fake bend to a pretty picture is lying about the mechanism. A simulator that solves the geometry correctly is doing numerical general relativity sixty times a second, per pixel, on a chip designed to draw triangles.

The idea predates Einstein by more than two centuries. In 1783 John Michell and independently in 1796 Pierre-Simon Laplace calculated the radius of a body whose Newtonian escape velocity reaches the speed of light. The modern concept arrived in 1916, months after Einstein published general relativity, when Karl Schwarzschild — serving on the Russian front in WWI — found the first exact solution to Einstein's field equations for a spherical, non-rotating mass. The term "black hole" itself only caught on after 1967, when journalist Ann Ewing popularized it in print (coined in a 1964 lecture by John Wheeler, who later regretted the hype).

1.1 The numbers you will actually render

Everything in a non-rotating simulator scales from one number, the Schwarzschild radius:

$$r_s = \frac{2GM}{c^2} \approx 2.95\ \mathrm{km}\times\frac{M}{M_\odot}$$
QuantityValue (Schwarzschild)Why a renderer cares
Event horizon$r = r_s$Hard black disk of captured rays; also the gravitational time-dilation singularity of the coordinates.
Photon sphere$r = 1.5\,r_s$Unstable circular light orbits. Source of the razor-thin photon ring — infinitely many images of the same sky stacked at one radius.
Apparent shadow$r_\text{shadow} = \frac{\sqrt{27}}{2}\,r_s \approx 2.6\,r_s$The shadow looks 3.5× wider in area than the horizon. A common amateur error is to draw a black ball of radius $r_s$ — the correct silhouette is bigger.
ISCO$r = 3\,r_s$Innermost stable circular orbit. Hot inner edge of any believable thin accretion disk lives here or just outside.
Photon capture cone$b_\text{crit} = \frac{\sqrt{27}}{2}\,r_s$Impact parameter threshold. Rays below it spiral in; rays above it escape after bending.

For scale: the galactic-center black hole, Sgr A*, has $r_s \approx 6$ million km — about 4% of Earth's orbit radius — while the pixel-sharp M87* imaged by the Event Horizon Telescope has an event horizon wider than our solar system. In FlyBlackHole, the companion game built for this paper, we set $r_s = 100$ m so a human-scale spaceship can interact with it; every equation below is unit-free, so the same code renders both.

Photon capture and deflection diagram with real integrated geodesics
Figure 1. Photon trajectories integrated from the exact null-geodesic equation (Chapter 3). Rays with impact parameter $b < \sqrt{27}/2\,r_s$ (red) are captured; rays just above it (blue) can loop the hole one or more times before escaping. The stacking of near-critical rays is what produces the photon ring.

1.2 What you are actually looking at

A convincing black hole "object" is a stack of visual phenomena, none of which is the hole itself:

Rotating (Kerr) holes, briefly

Real black holes spin, and the Kerr metric adds frame-dragging: the photon region is no longer a sphere, the shadow becomes a D-shape, the ISCO moves inward (to $0.5\,r_s$ for a near-extremal hole), and the disk can extend much closer — making the lensing far more extreme and asymmetric. This paper focuses on Schwarzschild, where one scalar ($r_s$) replaces spin and the physics still produces the iconic look. Chapter 6 notes where Kerr breaks each technique.

2Black Holes on Screen: A Depiction History

Timeline of black hole depictions from 1967 to 2024
Figure 2. A century and a half of "draw me a black hole." Grey: artistic invention. Colored: depictions with real physics behind them.

2.1 The vortex era (1960s–1990s)

Before computer graphics, black holes were drawn — and drawn wrong, because no one had ever seen one. Star Trek: The Motion Picture (1979) sends the Enterprise through a "black hole" that looks like a swirling paint mixer: bright, central, and full of things. The famous 1979 Encyclopedia Britannica illustration — a glowing donut seen at an angle with a bright center — was rendered by artist Brill with advice from NASA's Goddard center, and got the orientation of the lensing roughly right but the center wrong: a real black hole's center is the blackest thing in frame. For two decades, movies copied that illustration as fact. Event Horizon (1997) featured a hole that opens into literal hell; physically, the only thing past the horizon is more physics.

2.2 Contact (1997): the first informed rendering

Carl Sagan's novel described a traversable wormhole, and for the film the team at Digital Domain — with theoretical backing from Kip Thorne — built a ray-traced wormhole with a physically-derived lensing map: a ring of warped starlight, the external galaxy smeared into an arc. It was the first screen black-hole-adjacent object whose light paths came from equations rather than concept art. Notably, the artists found the real thing was less visually dramatic than invented vortexes, and Sagan himself asked them to add a "sparkly" rim so audiences would read it as special effects. The tug-of-war between physics and spectacle was born here.

2.3 Interstellar (2014): the special mention

No depiction deserves more attention than Interstellar's Gargantua, and it is a special case in every sense:

Black hole rendered without gravitational lensing
Figure 3. The wrong black hole: no lensing. A flat disk with a black dot — the "Frisbee" that every hobby engine produces and every physics viewer instantly rejects.
Black hole rendered with full geodesic lensing
Figure 4. The same camera with per-pixel geodesic integration: the far disk arcs over the shadow, a second image appears below, the starfield smears tangentially, and the shadow swells to $\sqrt{27}/2\,r_s$. Same assets — only the light paths differ.

2.4 Reality overtakes the movies (2019–present)

On April 10, 2019 the Event Horizon Telescope released the first direct image of a black hole — M87*, an orange smear with a dark center, essentially the Interstellar look at 20-pixel resolution, which is exactly what an interferometer with limited uv-coverage should produce. Sagittarius A* followed in 2022. Cinema's loop closed: audiences now recognize a black hole because it looks like a movie black hole, and the movies turned out to be roughly right. Meanwhile real-time GPU engines (and FlyBlackHole, Chapter 7) brought GR ray tracing to consumer graphics cards at 60 fps — computation that cost Interstellar hundreds of CPU-hours per frame.

3The Math: Light in Curved Spacetime

3.1 The Schwarzschild metric

General relativity says matter tells spacetime how to curve; spacetime tells light how to travel. For a spherical, uncharged, non-rotating mass, the line element is

$$ds^2 = -\left(1-\frac{r_s}{r}\right)c^2 dt^2 + \left(1-\frac{r_s}{r}\right)^{-1} dr^2 + r^2\left(d\theta^2 + \sin^2\theta\, d\phi^2\right)$$

Light follows null geodesics: paths where $ds^2 = 0$. A planetarium-scale simulator would integrate the full geodesic equation with Christoffel symbols, but for Schwarzschild three facts collapse the problem into something a GPU can digest: spherical symmetry (motion stays in a plane), conservation of energy $E$, and conservation of angular momentum $L$.

3.2 The orbit equation and the effective potential

Writing $u = 1/r$ and differentiating with respect to the azimuthal angle $\phi$, the radial motion of any light ray reduces to a single beautiful equation:

$$\frac{d^2u}{d\phi^2} + u = \frac{3}{2}\,r_s\,u^2$$
Compare with the Newtonian straight line $d^2u/d\phi^2 + u = 0$. The entire difference between a flat universe and a black hole is the term $\tfrac{3}{2}r_s u^2$.

This equation exposes the physics at a glance. The right-hand side — the GR correction — vanishes at large distance (straight lines recovered; the classic $1919$ eclipse deflection $\hat\alpha = 2r_s/b$ falls out of a first-order perturbation) and dominates near the hole. Solving for circular photon orbits ($u'' = 0$) gives $u = 2/(3r_s)$, i.e. the photon sphere at $r = 1.5\,r_s$ — and the fact that it is unstable (a nudge inward sends the photon spiraling in; a nudge outward sends it away) is why near-critical rays pile up into the photon ring.

Deflection angle versus impact parameter
Figure 5. Deflection angle vs. impact parameter. The weak-field formula (blue) works for the solar system; near $b = 1.5\,r_s$ the true angle diverges logarithmically — rays here can wind around the hole any number of times. This logarithm is why the photon ring is a stack of ever-fainter, ever-thinner sub-rings: each winding is one more image of the sky.

3.3 Why the shadow is 2.6 $r_s$ wide

The separatrix between capture and escape is found exactly. For a photon, the radial equation admits an effective potential with a peak at $r = 1.5\,r_s$ at height $(L/E)^2 = 27\,r_s^2/4$. A ray from far away with impact parameter $b = L/E$ is captured iff it can reach the peak:

$$b \;<\; b_\text{crit} = \frac{\sqrt{27}}{2}\,r_s \approx 2.598\,r_s$$

Since the apparent angle of a distant ray on the camera equals its impact parameter over distance, the silhouette on the sky is a disk of radius $\sqrt{27}/2\,r_s$ — 30% larger in radius than the horizon, 3.5× in area. A second way to see it, useful for rendering: stand anywhere, in vacuum, at any radius, and the black hole blocks a cone of directions of half-angle $\psi$ given by $\sin\psi = b_\text{crit}\sqrt{1-r_s/r}\;/\;r$. At the horizon, $\sin\psi = \frac{\sqrt{27}}{2}\cdot 0 \to$ the shadow covers… more than half the sky and grows as you fall, filling the entire sky at the singularity. Keep this formula — it is a free, exact "black disk mask" that costs one square root and pairs well with approximate lensing.

3.4 Time dilation and redshift

Clocks deeper in the potential tick slower by $d\tau/dt = \sqrt{1-r_s/r}$, and light climbing out is redshifted by $1+z = 1/\sqrt{1-r_s/r}$. For the simulator this means the inner disk's thermal spectrum visibly reddens and dims as it approaches the horizon — the inner edge doesn't end abruptly, it fades toward infrared. At $r = 1.01\,r_s$ the redshift factor is 10×: the disk's visible light arrives as microwaves. In practice you multiply the emitted radiance by $g^4$ (photon rate $g$ × energy $g$ × two arrival-rate factors, from Liouville's theorem) — see Figure 8's redshift discussion.

Time dilation and redshift versus radius
Figure 6. Both curves blow up at the horizon: to a distant observer, infalling light never quite finishes arriving, it just reddens into nothing. This is why the horizon is black and soft-edged in any honest render.

Free-fall time, for the HUD

A body dropped from rest at infinity reaches the singularity from the horizon in proper time $\tau = \tfrac{4}{3}\,r_s/c$ — 3.3 microseconds for a 10 $M_\odot$ hole, but ~7 hours across Sgr A*'s horizon radius… and for the 100 m $r_s$ in FlyBlackHole, about 0.4 milliseconds of ship-board time. Game physics licenses generous fudge factors here; the twin HUD clocks (ship vs. far universe, differing by $1/(1-r_s/r)$) are honest.

4The Accretion Disk: Making It Glow

4.1 Geometry and temperature

The black hole is invisible; everything you love is the disk. A thin accretion disk is modeled as Keplerian circular orbits between roughly $r_\text{in} \approx 3\,r_s$ (ISCO) and $r_\text{out} \sim 10\text{–}15\,r_s$ (a soft outer falloff). The canonical Novikov–Thorne temperature profile is well approximated by a power law:

$$T(r) = T_\text{in}\left(\frac{r}{r_\text{in}}\right)^{-3/4},\qquad T_\text{in} \approx (10^5\text{–}10^7\,\mathrm{K})\left(\frac{M}{M_\odot}\right)^{-1/4}$$

Stellar-mass disks peak in X-rays — invisible to your eyes and to your framebuffer. Cinematic and game disks (including FlyBlackHole's 12,000 K inner edge) cheat mass upward or temperature downward so the blackbody peaks in visible orange-white. A simple, gorgeous approximation: evaluate a blackbody or a hand-tuned color-ramp at $T(r)$, multiply by $T^4$ (Stefan–Boltzmann) for intensity, and you have a physically-shaped glow in ten lines of shader.

4.2 Doppler beaming — the asymmetric signature

Disk material at $r$ moves at $\beta = \sqrt{r_s/2r}$ (Keplerian, at $r = 3\,r_s$ that is half the speed of light). A moving blackbody is brighter toward its motion by the relativistic Doppler factor $\delta = 1/[\gamma(1-\boldsymbol\beta\cdot\hat n)]$, with observed intensity $\propto \delta^{3\text{–}4}$ and observed temperature $T_\text{obs} = \delta\,T_\text{emit}$.

Doppler beaming across the disk
Figure 7. At 0.5c orbital speed, the approaching limb is ~2.5–3× brighter than the receding limb before color is even considered. Any render with a perfectly symmetric disk is telling you the disk is static — i.e., not orbiting, i.e., falling in. Interstellar deliberately toned this down (Chapter 2); the EHT images lean all the way in.

The full per-pixel radiance model that a serious render evaluates along each ray is then: sample the ray's crossings of the disk plane; at each crossing evaluate $T(r)$, project the orbital velocity onto the ray direction, combine the Doppler factor $\delta$ with the gravitational redshift factor $\sqrt{1-r_s/r}$ into a net shift $g$, and accumulate $g^4\,B_\nu(g\,\nu)\,d\ell$ with the disk's emissivity. Two crossings per ray is usually plenty (direct far side + one lensed image); four covers rays that loop the photon sphere.

4.3 Turbulence and the noise problem

Real disks are magnetized plasma with magnetorotational turbulence — spotty, variable. Simulators fake this with domain-warped fBM noise advected at the Keplerian rate (so the pattern shears, as it physically must: inner rings lap outer rings, producing the characteristic spiral smearing). Advection phase must be continuous across frames or the disk boils; use $\phi_\text{noise} = \phi - \Omega(r)\,t$ in the noise argument rather than any per-frame seed.

5Why a Graphics Card Fights You

It is worth being blunt about the mismatch: GPUs are astonishing parallel machines built around an assumption that gravitational lensing violates in every fiber. The assumption is straight lines and fixed geometry.

5.1 Rasterization is the wrong model of light

The graphics pipeline (whether fixed-function of 1998 or the mesh-shader pipeline of today) works forward from geometry: vertices → triangles → fragments → framebuffer. That is the inverse of what a black hole scene needs. The scene has essentially no surfaces; it has a light-field distortion. The correct dataflow is backwards ray tracing — for each pixel, trace where its light came from — and even then, "where it came from" requires numerically integrating an ODE, not intersecting a primitive. Rasterizers can draw a black hole; they cannot be one.

5.2 The five concrete enemies

EnemyWhat it costs youStandard mitigation
1Variable step count Rays near the photon sphere need 5–20× more steps than distant rays. SIMT hardware runs warps in lockstep: one divergent near-critical pixel drags 31 neighbors to its step count. This — not raw FLOPS — is why naïve per-pixel geodesic renderers stutter at the shadow edge. Two-pass rendering (cheap pass + high-precision pass only in a screen-space annulus around the shadow); adaptive $h$ with a hard step cap; importance-based step limits.
232-bit float precision RK4 over $r\in[1,20]r_s$ with float32 accumulates visible jitter at the pixel level — a "shimmering shadow edge" that reads as a bug. Near-critical rays are chaotic: float32 changes which sub-ring a ray lands in. Unit-scale the scene to $r_s = 1$; use the integrated-potential form (below) rather than double summation; double-float (float2 hi/lo) tricks for the position accumulator; temporal filtering to hide residual noise.
3No frame-to-frame coherence A rolling camera changes every pixel's geodesic. Unlike ray tracing (where denoising reuses neighbors), lensing fields change fast near the hole. Reproject previous frame's radiance along its own geodesic ("GR reprojection") as a temporal prior; jittered sub-pixel sampling + TAA.
4The photon ring is sub-pixel Physically the ring is infinitely thin in the limit. At 1080p your integration precision, not the physics, determines its brightness — undersampled rings flicker; over-smoothed rings look fake. Render the ring analytically as a separate 2–4 px glow pass using the capture-cone angle (Section 3.3), let the integrator handle the rest.
5Bandwidth vs. LDR pipelines Doppler factors give real luminance ranges of 10–100:1 across a single disk; 8-bit channels clip either the bright limb or the dark horizon. HDR render targets (RGBA16F minimum), filmic tonemap as a late full-screen pass so physics never sees the tonemap.

5.3 What the card is good at

After the complaint, the surprise: a midrange GPU has exactly the right shape for this problem. A null geodesic is embarrassingly parallel across pixels — no two rays talk to each other — which is the one workload SIMT loves. Modern cards add tensor cores, mesh shaders, and — most relevantly — compute shaders, which decouple the pipeline from triangles entirely: a compute pass writing to an HDR texture is a ray tracer's clean slate. Hardware ray-tracing cores (BVH traversal) do not help directly: they accelerate intersections with static primitives, and a geodesic integrator's inner loop is arithmetic, not tree traversal — though the disk plane can be treated as a primitive once you know where the bent ray will cross it.

The arithmetic budget: one RK4 step of the geodesic equation is ~20 FLOPs. A 1080p frame (2M pixels) × 100 average steps × 20 FLOPs ≈ 4 GFLOP per frame — 240 GFLOP/s at 60 fps. A GTX 1650 has ~3 TFLOPS. The physics fits on a laptop GPU with 1% headroom if you write it well. The challenge is never the raw integration; it is enemies 1–5 above.

6Techniques: A Ladder from Fakes to Geodesics

Scatter plot of rendering techniques by cost and fidelity
Figure 9. The technique ladder. Everything below "lookup tables" cheats; everything at/above it solves the geodesic equation in some form. The sweet spot for real-time is the two teal points.

6.1 Tier 0 — Texture and screen-space fakes

A billboarded "black hole" PNG with a screen-space radial-distortion post shader (UV offsets pulling outward from screen center). Cost: <0.1 ms. Used extensively in games for wormholes and portals. It fails the honest-test instantly: the distortion is radially symmetric in screen space, so it breaks the moment the camera translates — the effect is glued to the viewport, not the world. Fine for a cutscene; a lie for a flight sim.

6.2 Tier 1 — The deflection-map / lookup-table trick

Because Schwarzschild lensing is static and spherically symmetric, the mapping from "observed direction" to "true sky direction" is a 2D function of only two numbers. Precompute it once (the reference treatment is Bruneton & Bertails-Descoubes's Real-time High-quality Black Hole Shader and its BTFRK tables), store as textures, and at render time each pixel becomes one table lookup plus one environment tap. Cost: ~0.2 ms at 4K. Fidelity: essentially exact for a static camera around a non-rotating hole — but a moving camera needs the 3D generalization (tables indexed by radius and view angle), and Kerr breaks the separability that made tables legal. This is the technique to recommend to a game team that wants "lensing, but it must not touch frame budget."

6.3 Tier 2 — Pseudo-geodesic ray marching (potential fields)

Trace rays as polylines that bend each step by an analytic gradient of a post-Newtonian potential: $\Delta\hat{d} \propto -\nabla\Phi_\text{eff}\,ds$. Cheap (10–30 steps), coherent, and tunable — but wrong in the strong field: it won't reproduce the $\sqrt{27}/2$ shadow, and multi-winding photon-ring rays don't emerge. Good for lensing of background galaxies at cosmological distances; not for the hole itself.

6.4 Tier 3 — Per-pixel geodesic integration (the real thing)

The core algorithm. For each pixel, launch a ray from the camera with direction $\hat d$ and integrate backwards along the null geodesic until termination (escape to sky, or capture radius). The form used in FlyBlackHole and many research demos avoids Christoffel symbols entirely by using the conserved quantities and writing acceleration in Cartesian form:

$$\frac{d^2\mathbf{x}}{d\lambda^2} = -\frac{3}{2}\,\frac{r_s\,h^2}{r^5}\,\mathbf{x},\qquad h = |\mathbf{x}\times\mathbf{v}|$$
with $h$ — the specific angular momentum — fixed from the launch state. This reproduces the exact Schwarzschild photon orbits (it is the orbit equation 3.2 written in vector form) and is ~15 lines of shader code.

Then: adaptive-step RK4 (halve $h$ when $r$ drops below a few $r_s$, since curvature scales as $r^{-5}$ — see Figure 10); at each step test for a crossing of the disk plane ($z$ sign change) and evaluate the disk radiance model of Chapter 4 at interpolated crossings; terminate when $r < r_s$ (capture: add nothing) or when $r > r_\text{far}$ with outward velocity (escape: sample the starfield cubemap).

Integration error versus step size for Euler, Verlet and RK4
Figure 10. Why Euler is not an option and RK4 is: at equal step counts RK4 is 2–3 orders of magnitude more accurate, and the error curve's slope means adaptive stepping (more steps only where $r^{-5}$ demands them) pays for itself. Verlet/leapfrog variants are popular in shaders for their symplecticity — they conserve $h$ and the shadow shape over long windings, at a middling accuracy that adaptivity fixes.

6.5 Tier 4 — Path-traced GR (film territory)

Multiple scattering, full radiative transfer through the disk medium, ion spectra, time-dependent emission — what academic groups render for comparison images with the EHT, and roughly what Interstellar's pictl renderer approximated. Minutes-to-hours per frame on a workstation. Include it here for honesty about the ceiling; nothing about it fits 60 fps except its data structures.

6.6 Where Kerr breaks the ladder

7Case Study: FlyBlackHole

The companion project to this paper, FlyBlackHole, is a first-person spaceship flight into a Schwarzschild black hole, built natively for Linux on Godot 4.7 / Vulkan. Every visual claim in this paper was verified against its renders.

FlyBlackHole render: lensed disk over the shadow with photon ring
Figure 11. A FlyBlackHole capture: shadow at the correct apparent radius, far side of the Keplerian disk arcing over and under it, tangentially smeared stars, Doppler-brightened approaching limb (left). Disk spans 3.2–14 $r_s$, inner edge ~12,000 K.
Design decisionChoiceRationale
IntegrationAdaptive-step RK4 per pixel, Cartesian form (6.4)Accuracy headroom for multi-winding rays; adaptivity tames the warp-divergence cost (enemy 1) near the shadow edge.
Units$r_s = 100$ m, ship-scale cameraHuman flight scale; all equations unit-free. Float32 safe because the scene is unit-scaled in $r_s$ (enemy 2).
Disk3.2–14 $r_s$, Keplerian $\Omega\propto r^{-3/2}$, blackbody ramp, Doppler + gravitational shift, fBM turbulence advected at $\Omega(r)$Chapter 4, at real-time cost.
StarfieldHDR cubemap sampled at the ray's asymptotic direction after escapeLensing of the sky is free — the sky is sampled wherever physics says the ray pointed.
Horizon logic$R$-reset disabled inside $r_s$; twin HUD clocks (ship proper time vs. far-universe time at $1/(1-r_s/r)$)The one-way door is the point of the experience; the clocks are the physics lesson.
Inside the horizonExternal universe rendered through the capture cone as a shrinking bright windowPhysically correct view of the outside for an infalling observer: everything beyond the cone is causally dark.
View while crossing the horizon
Figure 12. Mid-dive: as the ship nears $r_s$, the outer universe compresses toward a bright window overhead while shadow fills the lower hemisphere — the capture cone at work, matching the analytic $\sin\psi$ formula of 3.3.
Inside the horizon: universe in a shrinking window
Figure 13. Inside the horizon: everything that still exists outside occupies the window; its rim glows with photon-ring light that orbited the hole en route to the falling camera.
Wide exterior view of the black hole system
Figure 14. Cruising distance. The gravity overlay (isogravity shells and field lines) is debug geometry — the same field the integrator samples, drawn for teaching.

7.1 Measured behavior

On the development GPU (a midrange 4 TFLOP-class card) the geodesic pass runs at 60 fps at 1080p with adaptive stepping; frame cost is dominated not by steps but by divergence near the shadow annulus, matching Chapter 5's prediction — disabling lensing drops the pass to a plain cubemap blit (<0.05 ms), a ~20× swing. The most surprising casualty in early builds was float32 precision on the camera's world position at $r \gg r_s$ (coordinate magnitudes in the $10^4 r_s$ star field): the fix was rendering in camera-local geodesic coordinates with the hole's position as the only large value — worth writing on the wall of any engine integration: never integrate a geodesic in world coordinates.

8A Recipe You Can Build This Weekend

Everything you need for a physically honest, 60 fps black hole in any compute-capable renderer (Godot, Unity compute, Unreal compute, raw Vulkan/Metal/WebGPU):

// Per-pixel GR lensing — the whole algorithm (GLSL-ish, camera-local coords, rs = 1)
vec3 renderRay(vec3 dir) {
    vec3 x = vec3(0.0);           vec3 v = dir;          // start at camera
    vec3 h = cross(x - holePos, v);  float h2 = dot(h, h);
    vec3 acc = vec3(0.0);                          // accumulated disk radiance
    vec3 prev = x; float dt = 0.02;

    for (int i = 0; i < 300; i++) {
        x = integrateRK4(x, v, dt);               // d²x/dλ² = -1.5 * h2 * (x-p) / r⁵
        float r = length(x - holePos);

        if (r < 1.0) return acc;                // captured: black
        if (r > 60.0 && dot(v, x-holePos) > 0.0)
            return acc + sky(normalize(v));      // escaped: starfield

        if (prev.y * (x - holePos).y < 0.0)      // disk-plane crossing
            acc += sampleDisk(prev, x);           // T(r)·T⁴ ramp, δ³·⁵ beaming,
                                                  //   sqrt(1-1/r) redshift, noise
        dt = clamp(0.05 * r * r, 0.002, 0.5);     // adaptive: fine near hole
        prev = x;
    }
    return acc;
}

Pitfalls checklist (each of these cost a real project a day)

8.1 Suggested parameter set (validated)

ParameterValueParameterValue
$r_s$1.0 (unit-scaled)Disk inner / outer3.2 / 14 $r_s$
Escape radius40–60 $r_s$Inner-edge temperature~12,000 K (visual)
Max integration steps250–350Adaptive $dt$$0.05\,r^2$, clamped
Render targetRGBA16FCamera FOV60–75° (wider exaggerates lensing)

Further reading path: Doyle's 1984 thesis for the visual physics; the Weir & Oliver Interstellar paper on arXiv for a production implementation; Hsu et al.'s and Ringer's papers for lookup-table real-time methods; and any graduate text for the Kerr extension. Then open a compute shader and go make the shadow.

9References

  1. Darwin, C. G. (1959). "The gravitational deflection of light." Proc. Roy. Soc. A 254:393. — exact photon orbits and the capture cone.
  2. Bardeen, J. M. (1973). "Timelike and null geodesics in the Kerr metric." In Black Holes, Les Houches.
  3. Doyle, L. R. (1984). What is the gravitational lensing signature of a black hole? Senior thesis, CalTech. — the visual-physics ancestor of Interstellar.
  4. Weir, P., James, O., et al. (2015). "Designing Interstellar's black hole." arXiv:1502.03808; Class. Quantum Grav. 32. — production GR renderer.
  5. Himwich, T. et al. / EHT Collaboration (2019). "First M87 Event Horizon Telescope Results." ApJL 875:L1. — and Sgr A* results, 2022.
  6. Ressler, S. M. & Lamm, H. G. (2004). "Perspective views of relativistic radiation from a thin accretion disk." A&A 425:223. — disk rendering reference.
  7. Bruneton, E. & Bertails-Descoubes, F. (2023). "A Real-time High-quality Black Hole Shader." ebruneton.github.io/black_hole_shader — beam tracing with precomputed tables (the BTFRK method); live demo.
  8. Matthews, T. (2026). FlyBlackHole: native Linux/Vulkan GR flight simulator. Local repository: ~/FlyBlackHole.
  9. Matthews, T. (2026). How Gravity Can Be a Result of Time Dilation — companion paper on the metric foundations.
↑ Back to top