Skip to content
CheRongtianPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

BlackHole

English | 简体中文

A real-time black-hole visualization for macOS. Metal performs backward ray tracing, physical accretion-disk emission, and numerical diagnostics. OpenGL renders the three-dimensional reference grid, applies FXAA, and composites the final image.

The project retains a legacy Schwarzschild regression path and provides a Kerr path based on Boyer-Lindquist coordinates, Hamiltonian null geodesics, a local camera tetrad, spin-dependent ISCO, circular-orbit emitters, and an exact emitter-to-observer frequency ratio. The accretion disk supports Simple thin-disk and relativistic Novikov-Thorne/Page-Thorne flux models, both feeding the same Planck/CIE/HDR display pipeline.

Current Features

  • Schwarzschild and Kerr metric paths with RK4 backward null-geodesic tracing
  • Positive and negative dimensionless Kerr spin a_star
  • Boyer-Lindquist/world mapping, equatorial disk intersection, Kerr horizon and ISCO
  • Schwarzschild local-frame Doppler factor and gravitational redshift
  • Exact Kerr frequency shift from the invariant k_mu u^mu contraction
  • Simple thin-disk and Novikov-Thorne/Page-Thorne local emission
  • Planck blackbody spectrum, CIE 1931 XYZ, linear sRGB, exposure, and luminance-preserving Reinhard tone mapping
  • Capture-order protection, bounded local RK4 half-step retry, Hamiltonian/Carter diagnostics, and explicit ray outcomes
  • A gravitationally lensed sphere, 3D wireframe grid, eight-frame TAA, and material-aware FXAA
  • Fixed-scale debug views for the main physical emission stages

Requirements

  • A Metal-capable Mac
  • Xcode or Xcode Command Line Tools with the Metal toolchain
  • Homebrew and a C++17 compiler
brew install cmake glfw glew glm

Metal setup checks:

xcodebuild -showComponent metalToolchain -json
xcrun --find metal
xcrun -sdk macosx metal --version

Running

Fast preview

cd /Users/cherongtian/Desktop/Projects/BlackHole
chmod +x run.sh
./run.sh kerr 0.9 2.0 final nt

This launches a fast Kerr/Novikov-Thorne preview. step-scale=2.0 doubles the baseline Kerr RK4 step and halves the effective maximum step count. The nominal integration range stays approximately constant while strong-field edge accuracy is lower than at 1.0.

Normal-accuracy rendering:

./run.sh kerr 0.9 1.0 final nt

The current no-argument default is:

metric=kerr, a_star=0.9, step_scale=1.0, view=final, emission=nt

run.sh invokes clang++ directly, writes ./BlackHole in the project root, and launches it. It creates no CMake build directory. Each invocation recompiles; ./BlackHole reopens the most recently compiled configuration without rebuilding.

Full syntax and parameters

./run.sh [schwarzschild | kerr [spin [step-scale [view [simple|nt]]]]]

Arguments are positional, so earlier arguments must be present when setting a later one.

Argument Values Default Meaning
metric schwarzschild, kerr kerr Metric and geodesic path.
spin floating point 0.9 Kerr a_star=a/M. Schwarzschild forces 0; the implementation limits it to [-0.998,0.998].
step-scale positive float 1.0 Kerr global RK4 step multiplier, effectively limited to [0.01,4.0]. Schwarzschild keeps its legacy fixed step.
view table below final Final result or a diagnostic stage.
emission simple, nt Kerr: nt; Schwarzschild: simple Local disk-flux model.
Step scale Intended use Tradeoff
2.0 Fast interactive preview About half the global steps; reduced strong-field edge accuracy.
1.0 Normal rendering Current reference setting.
0.5 Convergence diagnosis Half-size step and about twice the maximum steps.
0.25 Stricter convergence diagnosis Quarter-size step and about four times the maximum steps.

Near-horizon segment scaling and bounded rejected-step retries remain active at every global scale.

Common configurations

# Current default
./run.sh

# Fast and normal Kerr/NT
./run.sh kerr 0.9 2.0 final nt
./run.sh kerr 0.9 1.0 final nt

# Moderate positive and negative spin
./run.sh kerr 0.5 1.0 final nt
./run.sh kerr -0.5 1.0 final nt

# Kerr a*=0 through the Hamiltonian path
./run.sh kerr 0.0 1.0 final simple

# Legacy Schwarzschild regression path
./run.sh schwarzschild 0 1.0 final simple

# Compare emission models under identical Kerr geometry
./run.sh kerr 0.9 1.0 final simple
./run.sh kerr 0.9 1.0 final nt

# Smaller-step convergence runs
./run.sh kerr 0.9 0.5 final nt
./run.sh kerr 0.9 0.25 final nt

Diagnostic views

view Output Fixed mapping or purpose
final Final image Complete physical emission/display pipeline.
geometry Kerr capture mask Captured rays black, every other outcome white.
t-emit Emitted temperature Grayscale 0–5500 K.
g-doppler Local Doppler diagnostic Grayscale 0.5–1.5; 1 is middle gray.
g-grav Gravity-shift diagnostic Grayscale 0.75–1.0; the Kerr value is the remainder of the auxiliary ZAMO split.
g-total Total frequency ratio Grayscale 0.5–1.5; 1 is middle gray.
t-obs Observed temperature Grayscale 0–8000 K.
linear-rgb Exposure-scaled linear RGB before tone mapping Linear [0,4] mapped to display, followed only by sRGB encoding.
simple-flux Simple local flux Fixed logarithmic grayscale; excludes shift, exposure, and tone mapping.
nt-flux NT local flux Same fixed logarithmic mapping as simple-flux.
# Geometry
./run.sh kerr 0.9 1.0 geometry nt

# Frequency shift stages
./run.sh kerr 0.9 1.0 g-doppler nt
./run.sh kerr 0.9 1.0 g-grav nt
./run.sh kerr 0.9 1.0 g-total nt

# Temperature and color stages
./run.sh kerr 0.9 1.0 t-emit nt
./run.sh kerr 0.9 1.0 t-obs nt
./run.sh kerr 0.9 1.0 linear-rgb nt

# Radial flux comparison
./run.sh kerr 0.9 1.0 simple-flux simple
./run.sh kerr 0.9 1.0 nt-flux nt

Save complete console output while keeping it visible:

./run.sh kerr 0.9 1.0 final nt 2>&1 | tee kerr-nt.log

The command completes when the application window closes.

Optional CMake build

The direct script is recommended for switching compile-time modes. CMake remains available for IDE integration:

cmake -S . -B build
cmake --build build --parallel
./build/BlackHole

These commands explicitly create build/. A plain CMake build uses the compile-time defaults in Scene.hpp unless the build configuration supplies corresponding definitions.

Controls

  • Left mouse drag: orbit around the black hole
  • Shift + left mouse drag: pan the target
  • Mouse wheel: zoom

Camera movement clears temporal history; a still view converges over eight temporal samples.

Coordinates and Units

The black hole is at the world origin. The disk occupies the world X-Z plane with normal +Y, corresponding to the Kerr Boyer-Lindquist equator theta=pi/2. Kerr +Z maps to world +Y.

r_s = 2GM/c^2
M_geo = GM/c^2 = r_s/2
a = a_star M_geo = a_star r_s/2.

Shader lengths use r_s. The disk outer edge is 4.5 r_s; the Schwarzschild/simple inner edge is 3 r_s. The Kerr inner edge follows the spin/orbit-dependent ISCO when enabled.

Geodesics

Backward rays start at the camera and integrate into the scene. Physical photons follow the same null path in the reverse direction, from an emitter to the camera.

Schwarzschild

ds^2 = -(1-r_s/r)c^2dt^2 +(1-r_s/r)^-1dr^2
       +r^2dtheta^2+r^2sin^2(theta)dphi^2.

The original Schwarzschild spatial state and RK4 geodesic remain the compatibility/regression path.

Kerr

The Kerr path uses Boyer-Lindquist coordinates and signature (-,+,+,+):

Sigma = r^2+a^2cos^2(theta)
Delta = r^2-2M_geo r+a^2
A = (r^2+a^2)^2-a^2Delta sin^2(theta)

g_tt = -(1-2M_geo r/Sigma)
g_tphi = -2M_geo a r sin^2(theta)/Sigma
g_rr = Sigma/Delta
g_thetatheta = Sigma
g_phiphi = A sin^2(theta)/Sigma.

Null rays follow

H = 1/2 g^(mu nu)p_mu p_nu = 0
dq^mu/dlambda = partial H/partial p_mu
dp_mu/dlambda = -partial H/partial q^mu.

p_t and p_phi are conserved; RK4 evolves (r,theta,phi,p_r,p_theta). Camera rays are initialized in a local ZAMO tetrad:

alpha = sqrt(Sigma Delta/A)
omega = 2M_geo a r/A
n^mu = (1/alpha,0,0,omega/alpha).

The local null condition fixes the past-directed time momentum before conversion to canonical Boyer-Lindquist momentum. The outer horizon is

r_+ = M_geo+sqrt(M_geo^2-a^2).

A positive Delta guard captures rays before the Boyer-Lindquist coordinate singularity. A capture-crossing trial state is terminated before diagnostics or scene intersection use it.

Disk Motion and Frequency Shift

The static local Schwarzschild observer measures

beta = sqrt[r_s/(2(r-r_s))]
gamma = 1/sqrt(1-beta^2)
g_doppler = 1/[gamma(1-beta cos(theta))]
g_grav = sqrt(1-r_s/r)
g_total = g_grav g_doppler.

The camera-to-scene ray segment is reversed and projected into the local static orthonormal frame before the Doppler dot product. Photon direction and orbital beta therefore belong to the same local observer. Approaching material has g_doppler>1; receding material has g_doppler<1. METAL_DISK_ROTATION_DIRECTION=+1/-1 selects the orbital direction around world +Y.

For a Kerr equatorial circular emitter,

u_emit^mu = u^t(1,0,0,Omega)
u^t = [-g_tt-2g_tphi Omega-g_phiphi Omega^2]^-1/2.

With x=r/M_geo and orbit sign s:

Omega M_geo = s/(x^(3/2)+s a_star)
E = [x^(3/2)-2sqrt(x)+s a_star]
    /[x^(3/4)sqrt(x^(3/2)-3sqrt(x)+2s a_star)]
L/M_geo = s[x^2-2s a_star sqrt(x)+a_star^2]
          /[x^(3/4)sqrt(x^(3/2)-3sqrt(x)+2s a_star)].

The Kerr physical frequency ratio is the invariant contraction

g_total = nu_observed/nu_emitted
        = (k_mu u_observer^mu)/(k_mu u_emitter^mu),

using the consistent sign convention for the stored past-directed ray. It combines gravitational redshift, frame dragging, transverse Doppler, and line-of-sight Doppler. Kerr g-doppler and g-grav views are an auxiliary ZAMO split; the final path uses exact g_total directly.

Kerr ISCO

Z1 = 1+(1-a_star^2)^(1/3)[(1+a_star)^(1/3)+(1-a_star)^(1/3)]
Z2 = sqrt(3a_star^2+Z1^2)

r_ISCO/M_geo = 3+Z2-sqrt[(3-Z1)(3+Z1+2Z2)]  aligned
r_ISCO/M_geo = 3+Z2+sqrt[(3-Z1)(3+Z1+2Z2)]  anti-aligned.

Alignment follows the product of spin and disk rotation direction. Since M_geo=r_s/2, a_star=0 gives r_ISCO=3r_s. A disk with r_ISCO>=r_outer has no emitting area and produces a console warning.

Disk Emission

The Simple zero-torque disk uses

F(r) = [3GM dotM/(8pi r^3)][1-sqrt(r_in/r)]
T_emit = [F/sigma_SB]^(1/4).

The CPU precomputes the large SI scale in double precision; the GPU evaluates x^-3[1-sqrt(x_in/x)], with x=r/r_s. Flux and temperature vanish at and inside r_in.

The Novikov-Thorne/Page-Thorne model assumes a stationary, axisymmetric, geometrically thin, optically thick, radiatively efficient disk with zero torque at the ISCO. For M_geo=1:

f_NT(x) = -Omega_,x/[x(E-Omega L)^2]
          integral(x_ISCO..x)[(E-Omega L)L_,x']dx'
F_NT = [dotM c^2/(4pi M_geo^2)]f_NT
T_emit = (F_NT/sigma_SB)^(1/4).

A 4096-sample nonuniform CPU lookup concentrates samples near the ISCO; Metal interpolates it at disk hits. Current display inputs are dotM=1.0e14 kg/s and exposure=2.5e-6. Accretion rate changes physical flux and temperature, while exposure only scales post-spectrum linear RGB.

Spectral and Display Pipeline

local flux -> T_emit -> g_total -> T_obs=g_total T_emit
-> Planck spectrum -> CIE 1931 XYZ -> linear sRGB
-> exposure -> luminance-preserving Reinhard -> sRGB encoding.
B_lambda(lambda,T) = [2hc^2/lambda^5]
                     /[exp(hc/(lambda k_B T))-1].

The shader samples 24 midpoint wavelengths from 380–780 nm, converts wavelength to metres for Planck evaluation, applies an analytic CIE 1931 color-matching approximation, integrates XYZ, and converts to linear sRGB.

Liouville transport gives

I_nu,observed(nu)=g_total^3 I_nu,emitted(nu/g_total).

An ideal shifted blackbody is consequently represented by T_obs=g_total T_emit. This includes the corresponding spectral intensity change, so the physical path applies no additional g_total^3 multiplier and no artificial red/blue tint.

Broad-band blackbody chromaticity varies smoothly. Hot approaching regions naturally approach white or blue-white, so frequency shifts often appear more strongly as brightness asymmetry than saturated hue bands. Lowering dotM cools and warms the disk; changing exposure alters display brightness without changing T_emit or g_total. Highly saturated cinematic red/blue separation requires a non-blackbody palette, narrow-band emission, or artificial mapping outside the present physical path.

Numerical Protection and Outcomes

Kerr trial steps are accepted when both diagnostic errors remain within the broad rejection thresholds:

normalized |H| <= 1e-2
normalized Carter drift <= 1e-2
maximum half-step retry depth = 3.

Rejected segments receive bounded local half-step retries. Exhausted retries produce numerical-failure, preventing invalid momentum from becoming an ordinary background ray.

Ray outcomes are mutually exclusive:

  • captured
  • disk-hit
  • object-hit
  • escaped
  • integration-range-exhausted
  • numerical-failure

escaped requires outward motion beyond a dynamic surface enclosing the camera, disk, and objects. integration-range-exhausted records rays that consume their affine integration range without another terminal outcome.

Rendering and Performance

Metal traces a 400 x 300 image and material mask. OpenGL composites it into an 800 x 600 window with the perspective grid and material-aware FXAA.

Kerr metric/derivative evaluation makes each RK4 step more expensive than the legacy Schwarzschild path. The NT table is precomputed once, while each disk-hit pixel evaluates 24 Planck/CIE samples. Initial diagnostics and eight-sample temporal accumulation can make the first still frames slower. Use step-scale=2.0 for camera work, 1.0 for final inspection, and 0.5/0.25 for convergence tests.

Configuration and Files

Main constants are in Scene.hpp, including render size, TAA samples, integration limit and step, disk bounds, ISCO usage, physical-effect switches, accretion rate, exposure, and disk rotation direction. run.sh supplies metric, spin, step scale, view, and emission model as compile-time definitions.

BlackHole.mm           Application entry point and render loop
Scene.hpp/.cpp         Camera, black hole, objects, and scene constants
Engine.hpp/.cpp        GLFW window, OpenGL compositor, grid, TAA/FXAA
MetalRayTracer.hpp/.mm Metal shader, geodesics, disk physics, spectra, diagnostics
CMakeLists.txt         Optional CMake configuration
run.sh                 Parameterized direct clang++ build-and-run script
GPUinfoFile/           GPU information utility sources
Draft/                 Earlier experiments

Model Boundaries

  • finite-resolution, single-precision GPU RK4 integration;
  • Boyer-Lindquist coordinates with a capture guard outside the coordinate singularity;
  • an infinitesimally thin equatorial disk;
  • local blackbody emission without atmosphere or color correction;
  • no returning radiation, self-irradiation, limb darkening, disk thickness, ISCO magnetic stress, plunging-region emission, polarization, lines, or scattering;
  • finite visible-band sampling and an analytic CIE approximation;
  • an OpenGL reference grid that is visually curved and is not part of relativistic ray tracing.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages