What happens to an image when light bends?
Near a black hole, tracing straight rays is no longer enough to explain the view. This project explores light trajectories in the Schwarzschild model of a non-rotating black hole and builds a renderer around those trajectories.
It contains two distinct rendering paths: an offline C++ geodesic ray tracer and a separate approximate real-time GLSL visualization. They make different accuracy and responsiveness tradeoffs.
An explicit state and a fourth-order integrator.
The offline tracer stores spacetime coordinates t, r, θ, φ and their derivatives. It evaluates Schwarzschild-coordinate geodesic equations and advances the state with fourth-order Runge–Kutta integration. Rays begin at a camera and are traced until they approach the horizon, leave the scene, or exhaust a step budget.
The source uses normalized mass M = 1, horizon radius 2M, a fixed integration step of 0.05, and a maximum of 2,000 integration steps per ray. This is a numerical graphics experiment; the portfolio makes no claim of validated astrophysical accuracy.
Turning trajectories into an image.
The offline renderer detects crossings of an equatorial disk, accumulates procedural disk emission, and applies a redshift/beaming-inspired brightness adjustment. Escaping rays sample a procedural background. Tone mapping and gamma correction turn the accumulated values into a PPM image.
The case-study hero is converted from the repository’s saved output/render.ppm. It is authentic output from the project, not a replacement generated for this portfolio.
A separate interactive approximation.
The OpenGL/GLUT application draws a full-screen fragment shader with cursor-controlled camera rotation. The GLSL path bends ray directions using an inverse-square-inspired acceleration, adds a procedural disk and star field, and tone maps the result. It does not run the offline Schwarzschild RK4 solver for each pixel.
The application also spawns point particles with a separate CPU RK4 update. Those trajectories are overlaid on the shader view. This combination is useful for exploring graphics and integration, but it is not a coupled accretion-disk or fluid simulation.
Where numerical methods meet rendering constraints.
The core engineering problem is making evolving ray states produce a coherent image while keeping termination, disk intersections, coordinate transforms, and color accumulation understandable. The code separates vectors, noise, image writing, and ray integration.
The real-time branch trades the offline solver for a shorter shader loop. The offline path retains explicit numerical state and emits a portable image artifact. No speedup, frame-rate benchmark, or convergence study is claimed.
The boundaries of the model.
The Schwarzschild model assumes a non-rotating black hole. The disk is a procedural visual model rather than a physical fluid calculation. Fixed steps, finite ray budgets, near-horizon behavior, and coordinate singularities can introduce numerical error.
The repository does not provide an independent conservation or convergence validation. Its current Makefile also gathers both C++ entry points for the default target, so the explicit offline compilation below avoids combining two main functions. The interactive target uses macOS GLUT and OpenGL frameworks.
Build the offline renderer.
Use a C++17 compiler and build from source rather than relying on the committed binaries. Run from the repository root so the output path and shader paths resolve.
For the macOS interactive version, make realtime followed by ./realtime_black_hole uses the framework flags supplied by the repository. Other platforms need an appropriate OpenGL/GLUT build configuration.
git clone https://github.com/kvitkas/Black-Hole-Simulation.git
cd Black-Hole-Simulation
mkdir -p output
c++ -std=c++17 -O3 src/main.cpp src/RayTracer.cpp -o black_hole_renderer
./black_hole_renderer