wyj 04611e3e5a Feat: Overlap movie output and cut per-frame fast-mode work
- Gather the union of every movie frame's all-sky tiles once and read them in bounded batches, replacing the per-frame prefetch scan and log.
- Fuse the fast supersampled-buffer clear into the FFTW pack pass so a resolved frame starts clean with no serial memset.
- Split parallel HDR->RGB8 tone mapping from PNG encoding.
- Add a bounded single-producer/single-writer movie output queue used by both observer movies and multi-frame imported lens maps, with writer timing and error propagation.
- Add --png-compression-level and --movie-output-workers shared|reserve-one.
- Add --fast-fftw-plan estimate|measure|wisdom|wisdom-update with strict wisdom identity sidecars.
- Add staged movie timing, regression tests, and docs.
2026-10-03 19:32:35 -04:00
2026-08-25 22:45:43 -04:00

GR 4D ray tracing

English | 简体中文

An offline spacetime renderer focused on physical accuracy. The project aims to turn time-dependent numerical-relativity simulations, including binary black-hole mergers, into 4K movies by tracing light rays backward through the four-dimensional spacetime.

Schwarzschild black-hole lensing of the 2MASS Galactic-center star field

The 2MASS Galactic-center star field seen through Schwarzschild spacetime, from a camera at radius 100 M. This reference image uses the legacy Reinhard tone map (--tone-map reinhard). Click the image for the full 4K render; rendering command below.

Stars are individual catalog point sources with direction, temperature, and amplitude. The renderer maps them into the camera image, accounting for multiple images, gravitational lensing magnification, and frequency shifts, then accumulates their sub-pixel point-spread functions (PSFs) into an HDR image. Movie cameras are described by worldline and tetrad tracks.

Single images support independent coordinate position and look direction, coordinate velocity, and camera roll in both backends. For example, --observer-position 1.75 0 0 --observer-velocity -0.5 0 0 --look-ra-deg 0 --look-dec-deg 0 specifies an inward-moving Schwarzschild camera looking outward from inside the horizon. See single-frame camera parameters and complete commands.

Current status

The current implementation supports analytic Minkowski, Schwarzschild, and moving Alcubierre warp-bubble spacetimes, single images and observer-track image sequences, adaptive lens meshes, and reusable lens-map files. It is written primarily in C with OpenMP CPU parallelism; an optional HIP backend accelerates PSF accumulation.

HIP retains parallel CPU catalog mapping and uses bounded, completion-protected event uploads. See HIP configuration and bounded performance checks.

The Nmesh numerical-spacetime backend and BBH rendering are still planned. The current scope is black-hole capture and distant stellar backgrounds; local matter emission, accretion disks, and plasma are outside this stage. See the design document for the architecture and development roadmap.

Build

The default build requires a C11 compiler with OpenMP support, GNU Make, and libpng development files. From the repository root:

make -j

This builds build/Release/minkowski_sky, build/Release/schwarzschild_sky, and build/Release/alcubierre_sky. For individual backends, Debug builds, optional HDR/FITS output, HIP support, and regression checks, see build.md.

Prepare the stellar catalog

The renderer accepts any stellar catalog converted to the supported CSV format: four columns containing ICRS right ascension and declination in degrees, temperature in kelvin, and amplitude, in that order. Use --catalog PATH to load a single CSV.

2MASS is the recommended survey catalog, with download and processing scripts provided by this project. Instructions are in assets/2mass/README.md. The full download takes approximately three to four days, so contact me for a compressed archive first if possible. Place the processed tile files under assets/2mass/processed/all_sky/ for the commands below.

The included assets/sky_grid_5deg.csv is a synthetic stellar grid for geometry and regression tests. It can be used directly with --catalog assets/sky_grid_5deg.csv, without downloading survey data; see the test-grid example below.

Rendering examples

The examples below illustrate a few choices of sky field, spacetime, and camera settings. After building and preparing the catalog, adapt these commands to your own field of view, observer position, and rendering settings. See usage.md for the available controls and workflows.

Example: Summer Triangle in flat spacetime

This example renders a field around the Summer Triangle:

mkdir -p output/imgs
./build/Release/minkowski_sky \
  --all-sky-catalog assets/2mass/processed/all_sky \
  --look-ra-deg 296 --look-dec-deg 27 --fov-deg 72 \
  --width 3840 --height 2160 --exposure 1e12 \
  --tone-map reinhard \
  --output output/imgs/summer_triangle.png

Summer Triangle in flat spacetime, exposure 1e12

4K reference image with exposure 1e12; it uses the legacy Reinhard tone map (--tone-map reinhard). Click to view at full resolution.

Angles are in degrees; --fov-deg is the horizontal field of view. Exposure is an adjustable display multiplier. Use --verbose for progress during long renders.

Example: Galactic-center field through Schwarzschild spacetime

This example uses a camera at radius 100 M to render the lensed Galactic-center field shown at the top of this page:

mkdir -p output/imgs
./build/Release/schwarzschild_sky \
  --all-sky-catalog assets/2mass/processed/all_sky \
  --width 3840 --height 2160 \
  --look-ra-deg 262.5 --look-dec-deg -30 --fov-deg 45 \
  --observer-radius 100 --exposure 1e13 \
  --coarse-cell-pixels 16 --refine-max-level 4 --refine-jacobian-min 0.2 \
  --psf-fwhm-pixels 2.7 --psf-moffat-beta 4.5 \
  --psf-relative-tail 1e-8 --psf-min-y 0 --max-cache-psf-flux 1e8 \
  --catalog-load-workers 4 \
  --tone-map reinhard \
  --output output/imgs/schwarzschild_galactic_center.png

This uses the updated reference image's rendering settings. The --max-cache-psf-flux 1e8 setting permits bright PSF wings to be clipped to the cache radius, but this run reported no wing clipping or direct fallbacks. The CPU/HIP benchmark record preserves the commands and terminal output, and records bit-identical CPU/HIP PNGs. The command above omits the optional HDR and lens-map exports.

This example infers a position facing the hole from its look direction and radius. Adaptive refinement should be configured for the desired image accuracy; it is disabled by default. Tone-mapped PNG/PPM output uses a per-channel soft-clip display transform by default (--tone-map softclip --tone-map-p 2); --tone-map reinhard restores the historical curve used by the reference images above. The linear HDR FITS output never applies it. An optional limited-response sensor bloom (--sensor-bloom-limit E --sensor-bloom-transfer e, default disabled) can be applied to the linear HDR before display; it never changes the FITS output. See tone mapping and display. Camera controls, movie sequences, lens-map reuse, PSF settings, and HDR output are described in usage.md. For fast single-frame previews, --fast-mode replaces per-event PSF splats with a supersampled delta deposit plus one global PSF convolution and downsample. Its nearest-deposit path is not qualified for temporally coherent movie output; see usage.md for the spatial and temporal accuracy limits. Both binaries provide a complete option list with --help.

Example: Looking outward just above the Schwarzschild horizon

This camera is static at (2.1, 0, 0) in Cartesian Kerr–Schild coordinates, just outside the horizon at r=2M (M=1). RA=0°, Dec=0° points along +X, radially outward. Its coordinate velocity is zero: this is a static observer, not a freely falling one. The scene uses the bundled synthetic stellar grid, so no survey download is needed.

mkdir -p output/imgs
./build/Release/schwarzschild_sky \
  --catalog assets/sky_grid_5deg.csv \
  --observer-position 2.1 0 0 \
  --observer-velocity 0 0 0 \
  --look-ra-deg 0 --look-dec-deg 0 \
  --camera-roll-deg 0 \
  --width 3840 --height 2160 --fov-deg 90 \
  --coarse-cell-pixels 16 --refine-max-level 3 \
  --exposure 0.01 \
  --tone-map reinhard \
  --output output/imgs/schwarzschild_near_horizon_outward_R2.1_0.01_refine3.png

Synthetic stellar sky seen outward by a static observer at r=2.1M

4K render with a 90° horizontal field of view, exposure 0.01, and maximum refinement level 3; it uses the legacy Reinhard tone map (--tone-map reinhard). Click to view at full resolution.

The distant sky occupies a bounded angular region around the outward direction, with repeated images crowded near its edge. Strong gravitational blueshift pushes both the 3000 K and 12000 K test stars toward blue-white.

Example: Synthetic test grid with mesh overlay

This example uses assets/sky_grid_5deg.csv to inspect lensing and adaptive mesh refinement in Schwarzschild spacetime. The main output is the clean tone-mapped image; --draw-mesh additionally writes the final image-plane triangles, so one command produces both output/imgs/schwarzschild_test_grid.png (no mesh) and output/imgs/schwarzschild_test_grid_mesh.png (mesh overlay).

mkdir -p output/imgs
./build/Release/schwarzschild_sky \
  --max-cache-psf-flux 1e8 \
  --catalog assets/sky_grid_5deg.csv \
  --refine-max-level 3 --refine-jacobian-min 0.2 \
  --width 3840 --height 2160 \
  --look-ra-deg 0.1 --look-dec-deg 0.1 --fov-deg 45 \
  --coarse-cell-pixels 32 \
  --observer-radius 100 --exposure 0.2 \
  --psf-fwhm-pixels 2.7 --psf-moffat-beta 4.5 --draw-mesh \
  --tone-map reinhard \
  --output output/imgs/schwarzschild_test_grid.png

Synthetic stellar grid lensed by a Schwarzschild black hole, with adaptive mesh overlay

4K test-grid reference image showing the _mesh.png overlay; it uses the legacy Reinhard tone map (--tone-map reinhard). Click to view at full resolution.

Freely falling Schwarzschild movie camera

scripts/schwarzschild_camera_track.py generates the canonical 21-column observer CSV in ingoing Cartesian Kerr–Schild coordinates (G=c=M=1, matching the renderer). It requires Python 3, NumPy and SciPy. Position is (x,y,z); velocity is coordinate (dx/dt,dy/dt,dz/dt). Look RA/Dec and roll construct the initial rest-frame forward/up/right legs with the same convention as the single-image camera. Without look angles, the camera initially points toward the origin. The orientation then follows Fermi–Walker transport; for free fall this is parallel transport, so it does not keep pointing at the black hole. --tetrad alternatively accepts 16 row-major components (e0t,e0x,...,e3z) of an orthonormal tetrad, with e0 matching velocity.

python3 scripts/schwarzschild_camera_track.py \
  --position 8 0 0 --velocity 0 0 0 \
  --look-ra-deg 0 --look-dec-deg 0 --roll-deg 0 \
  --fps 30 --duration 10 --output /tmp/freefall_camera.csv
make SPACETIME=schwarzschild all
mkdir -p output/freefall_frames
./build/Release/schwarzschild_sky \
  --observer-track /tmp/freefall_camera.csv --movie-track-samples \
  --frames-dir output/freefall_frames --catalog assets/sky_grid_5deg.csv \
  --width 640 --height 360 --fov-deg 60 --exposure 0.2
ffmpeg -framerate 30 -i output/freefall_frames/frame_%06d.png \
  -c:v libx264 -pix_fmt yuv420p output/freefall.mp4

Here --duration is elapsed proper time, and --fps is samples per unit proper time. CSV rows occur at tau=k/fps <= duration, including the initial event and an endpoint only if it lies on that cadence (10 at 30 fps gives 301 rows). tau starts at zero; --t0 sets the initial coordinate time. --movie-track-samples uses each row exactly once and ignores renderer --start-time, --duration, and --fps; encode the PNG sequence at the generator's fps. Without this flag the existing movie mode resamples at uniform coordinate time, which changes the proper-time cadence.

DOP853 jointly integrates the geodesic and all tetrad legs using analytic metric derivatives. Defaults: --rtol 1e-10 --atol 1e-12 --stop-radius 0.001. Integration crosses the horizon and stops at this numerical guard before the singularity, reporting its proper time and retaining only regular cadence samples. The guard is not the exact singularity; reduce it and tolerances to check convergence. The renderer's independent ray capture cutoff remains r=1.5M: rows inside it are valid trajectory data but the current renderer captures those rays immediately. The script reports maximum tetrad drift and rejects errors above 1e-6 rather than silently repairing the transported frame. Run the orbit, transport and CSV render regressions after building with python3 tests/test_schwarzschild_camera_track.py.

S
Description
No description provided
Readme
73 MiB
0 Stars 1 Watchers 0 Forks
Languages
C 74%
Python 15.7%
HIP 5.2%
Assembly 3.6%
Makefile 1.1%
Other 0.4%