A real-time black hole visualizer that renders gravitational lensing with a GPU fragment shader and a physically motivated Schwarzschild null-geodesic integrator. The default preset is based on M87*, the supermassive black hole imaged by the Event Horizon Telescope: 6.5 billion solar masses, about 16.8 Mpc away, with an observed crescent diameter near 42 microarcseconds.
- M87* preset with physical scale readout and EHT-style radio-intensity palette
- Optional Sgr A* comparison preset
- GPU fragment shader ray tracing with a Schwarzschild bending model
- Lensed procedural starfield sampled from the escaped ray direction, not screen space
- Doppler-boosted accretion disk with gravitational redshift and photon-ring glow
- Physical camera trajectories: stable circular orbit outside the ISCO, or a radial plunge that can cross the event horizon
- Per-pixel ray tracing/geodesic marching from the camera frame through the black-hole spacetime approximation
- Observer velocity and special-relativistic aberration applied before tracing each camera ray
- Mounted-camera look: hold right mouse button and move the mouse
- Full on-screen HUD/control reference with live physical and render parameters
- CPU renderer fallback using the same starfield and disk-plane math as the GPU path
- Live FPS and active preset in the window title
| Action | Keys |
|---|---|
| Orbit radius | W inward · S outward · mouse wheel |
| Orbit azimuth | A / D |
| Orbit inclination | Q / Ctrl lower · E / Space higher |
| Fast orbital adjustment | Shift |
| Start physical plunge | F |
| Plunge rate | W faster · S slower · mouse wheel |
| Look (mouse) | Hold RMB; full 360 mounted-camera look |
| Look (keyboard) | Arrow keys; full 360 mounted-camera look |
| Orbit rate | O slower · L faster |
| Presets | 1 M87* · 2 Sgr A* |
| Field of view | Z narrower · X wider |
| Black hole mass | [ smaller · ] larger |
| Exposure | , darker · . brighter |
| Render quality | Y cycles upward |
| Star background | T toggle · B fewer · N more |
| Disk orientation | U / I tilt · J / K twist |
| Disk animation | C slower · V faster · P pause |
| Reset camera | R |
| Toggle HUD | H or F1 |
| Release mouse / quit | Esc (once releases mouse; again quits) |
- Windows 10 or later
- Visual Studio 2019+ or Build Tools (MSVC) with C++17
- CMake 3.16+
- A GPU/driver exposing modern OpenGL via
wglGetProcAddress
cmake -S . -B build
cmake --build build --config Release
ctest --test-dir build -C Release --output-on-failure
build\Release\BlackHole.exeCMakeLists.txt— build configuration for a Win32 executablesrc/app.hpp— shared types, globals, OpenGL function pointerssrc/app_state.cpp— global state definitionssrc/window.cpp— Win32 window, WGL context, input handlingsrc/shader.cpp— GLSL program and uniform locationssrc/render.cpp— CPU fallback renderer and texture uploadsrc/hud.cpp— OpenGL bitmap-font HUD/control overlaysrc/physics.cpp— reference presets, physical scale math, color palette, starfield/control helperssrc/camera.cpp— physical observer update, mounted-camera look, and trajectory controlssrc/main.cpp— main loop, uniform updates, FPS titletests/physics_tests.cpp— CTest coverage for physical scale and control math
The camera is not a free noclip camera. Its position is generated from a physical observer state. In stable-orbit mode the radius clamps at the innermost stable circular orbit (r = 6M, three Schwarzschild radii), while the event horizon remains at r = 2M. Press F to start a radial plunge: the observer then falls inward with an infall velocity and can cross the event horizon. R resets back to an external stable orbit. You can point the mounted camera with full 360-degree mouse/arrow look, including outward toward the stars during a plunge, but the camera body follows the chosen physical trajectory. The observer also carries orbital or infall velocity; each camera-space ray is aberrated into the static frame before geodesic tracing.
The fragment shader builds a per-pixel viewing ray from that camera basis (uFwd, uRight, uUp, uCamPos) and integrates a 2D null geodesic in the ray plane using the Schwarzschild orbit equation d²u/dφ² + u = 3Mu² with u = 1/r. The ray-plane basis and near-radial angular steps are stabilized so camera-aligned rays do not carve holes in the disk. Crossing the accretion-disk plane triggers thermal/radio-style emission shading with Doppler beaming, gravitational redshift, and a visible display color shift. A photon-ring glow accumulates near the photon sphere (r ≈ 3M).
If the ray escapes, the background is sampled from the outgoing ray direction, so stars and the galactic band bend around the black-hole shadow instead of sticking to the screen.
The orange/gold palette follows the visual language of EHT radio intensity images. It is not a literal visible-light color photograph; the EHT image is millimeter/radio emission from hot plasma rendered as an intensity map.
Edit defaults in src/app_state.cpp:
gMass— normalized render mass/visual scalegPhysicalCamera— stable observer radius, inclination, azimuth, orbit rate, and mounted-camera look offsetsgControls— exposure, star density, disk orientation, quality, HUD stategActivePreset— active physical reference objectgWidth,gHeight— initial resolution (window is resizable)
- The GPU path is used when shader functions resolve via
wglGetProcAddress - VSync is disabled in
src/window.cppfor uncapped frame rate - For very high resolutions, prefer the GPU path; CPU is a fallback
This is a visual simulator optimized for interactivity. It uses physically meaningful reference values, Schwarzschild light bending, disk-plane intersection, gravitational redshift, Doppler beaming, observer aberration, secondary disk-image accumulation, and a direction-space lensed star background. It is not a full Kerr GRMHD radiative-transfer renderer, so it does not model M87* spin, magnetic polarization, turbulent plasma evolution, telescope reconstruction blur, or frequency-dependent radiative transfer. The visible blue/red color shift is a display mapping derived from Doppler factors, not full spectral radiative transfer. The inside-horizon view is an interactive Schwarzschild/ray-tracing approximation with a singularity guard rather than a full coordinate-regular GR renderer.
Reference anchors:
- NSF/EHT M87* media kit: https://www.nsf.gov/news/media-toolkits/event-horizon-telescope
- EHT M87* shadow/mass paper: https://arxiv.org/abs/1906.11243
- NASA M87 image/color-intensity context: https://science.nasa.gov/asset/hubble/m87/
No license specified. Add a license file if you plan to distribute or open-source.