# GR 4D ray tracing — Phase 0 prototype `minkowski_sky` is a deliberately small, CPU-only, single-frame Phase 0 benchmark. It renders point sources from a sky catalog through an analytic backend. It is not a sky texture: each source remains a direction, temperature, and amplitude until its sub-pixel Gaussian PSF is splatted. Build and render the default 1280 x 720 image: ```sh make run ``` The program first creates `assets/sky_grid_5deg.csv` when it is missing. The synthetic catalog places stars every 2 degrees on the union of longitude and latitude lines spaced 10 degrees apart; the two poles are stored only once. The eight octants (four 90-degree longitude sectors in each hemisphere) alternate red `temperature_K = 3000` and blue `temperature_K = 12000`. Red stars use `amplitude = 1`; blue stars use `amplitude = 0.00141095580387`, which equalizes their CIE/linear-sRGB luminance under the renderer's blackbody integration. Longitude boundaries belong to the sector to their east and the equator to the northern hemisphere, so boundary stars have a deterministic color. The default output is PNG at `output/imgs/minkowski_sky.png`. ## Movie PNG sequence (Phase A) Movie mode consumes a canonical observer-track CSV rather than a fixed camera. Each row stores coordinate time, proper time, Cartesian position, and the full four-by-four tetrad (21 columns total). Generate the first reproducible Minkowski benchmark—two coordinate seconds at 30 fps, accelerating from rest to about `0.95c`—then render its numbered PNG frames: ```sh make clean && make mkdir -p output/imgs ./build/minkowski_sky --write-minkowski-accel-track output/minkowski_accel_2s.csv \ --duration 2 --fps 30 --proper-acceleration 1.52 ./build/minkowski_sky --observer-track output/minkowski_accel_2s.csv \ --frames-dir output/imgs --frames-prefix minkowski_accel \ --start-time 0 --duration 2 --fps 30 --exposure 1e-5 ``` This writes `minkowski_accel_000000.png` through `minkowski_accel_000060.png`. The renderer treats the CSV as its observer input; the acceleration generator is only a reproducible flat-spacetime test fixture. Movie mode collects all current frame-mesh vertices into a single SoA ray pool, activates rays as a newest-to-oldest coordinate-time scan reaches their observer event, and advances active rays to each slab boundary. The analytic backends use logical slabs with no metric I/O; nmesh slab loading is the next backend step. `--slab-duration` sets the coordinate-time width (default `64`) for this current fixed-mesh pass. The current synthetic test catalog uses global default exposure `1e-3`; the accelerated benchmark explicitly uses `1e-5` because its physical Doppler blue shift otherwise clips the later frames. PNG is the default output and the default build links `libpng`: ```sh make clean && make mkdir -p output/imgs ./build/minkowski_sky --output output/imgs/minkowski_sky.png ``` If `libpng` is unavailable, rebuild with `make clean && make ENABLE_PNG=0`. That intentionally selects the binary-PPM fallback, whose default path is `output/imgs/minkowski_sky.ppm`; pass a `.ppm` path for explicit output. Run the flat-spacetime geodesic regression with: ```sh make test ``` This also checks that the Kerr--Schild metric remains finite at `r=2M` and that the central ray from the default Schwarzschild camera is classified as captured. Build an independent analytic Schwarzschild executable in Cartesian ingoing Kerr--Schild coordinates (regular at the horizon), then render the test catalog to PNG: ```sh make clean && make SPACETIME=schwarzschild mkdir -p output/imgs ./build/schwarzschild_sky --catalog assets/sky_grid_5deg.csv \ --width 640 --height 360 --coarse-cell-pixels 8 --fov-deg 60 \ --output output/imgs/schwarzschild_test_catalog.png ``` `SPACETIME=minkowski` (the default) and `SPACETIME=schwarzschild` select source files at compile time, so each executable contains exactly one metric provider. The Schwarzschild demonstration uses mass `M=1` and places a static camera at coordinate radius `30` by default. `--look-ra-deg` and `--look-dec-deg` define the direction from the camera to the hole; the camera is placed at the opposite direction from the origin and its local forward axis points radially inward. Use `--observer-radius R` to select any `R > 2`; it is a coordinate radius in Cartesian Kerr--Schild coordinates. The backend escapes at `r=256` and declares capture at `r=1.5`, safely inside the horizon at `r=2`. Those rendering thresholds are Phase-1 demonstration values, not settled production refinement or integration settings. For a local radial boost relative to that static camera, pass `--observer-inward-speed V`, where `0 <= V < 1` is measured in the static observer's orthonormal frame and positive values point toward the hole. The default is `0`, preserving the static camera. For rays that asymptote to the future horizon in coordinate-time backward integration, the Schwarzschild demo also terminates at `log(alpha p^0) = 8`. This is the normalized-momentum horizon diagnostic already evolved by the integrator; it is disabled by default and does not replace the AH-calibrated spatial capture criterion planned for nmesh data. Useful options: ```sh ./build/minkowski_sky --width 1920 --height 1080 --fov-deg 30 \ --catalog assets/sky_grid_5deg.csv --output output/imgs/frame.png ./build/minkowski_sky --catalog assets/2mass/processed/2mass_psc_m31_0p5deg_stars.csv \ --look-ra-deg 10.6847083 --look-dec-deg 41.26875 --fov-deg 1.8 \ --exposure 1e15 --output output/imgs/2mass_m31.png ./build/minkowski_sky --catalog assets/2mass/processed/2mass_psc_m44_1p0deg_stars.csv \ --look-ra-deg 129.99165 --look-dec-deg 19.54139 --fov-deg 2.0 \ --exposure 1e15 --width 1920 --height 1920 --output output/imgs/2mass_m44.png ./build/minkowski_sky --write-catalog assets/sky_grid_5deg.csv ``` The camera is a fixed inertial observer at coordinate position `(0,0,0)`, with a tetrad whose forward direction is coordinate `-Z` and whose vertical direction is `+Y`. The frame first triangulates the image plane, then traces only its vertices backwards. Escaped endpoints form a triangulation on the source sky. For every locally invertible triangle, catalog stars inside its spherical source triangle are interpolated back to the image triangle and splatted as PSFs. Consequently multiple image triangles naturally create multiple images of the same star. The ray state evolves `(x^i, Pi_i, log(alpha p^0))` in coordinate time with RK4 using the 3+1 equations in Bohn et al. II.A, until the spacetime backend classifies the ray. `spacetime.c` is the only module containing the Minkowski metric or its infinity criterion; frame, observer, and integrator use only `SpacetimeSource` and `MetricData`. The initial regular mesh size is exposed as `--coarse-cell-pixels`; it is a Phase-0 sampling knob, not a settled production refinement threshold. `--look-ra-deg` and `--look-dec-deg` rotate that fixed tetrad so its forward axis is the corresponding catalog direction; their defaults reproduce the original `-Z` view. `--exposure` converts a catalog's physical flux normalization to the prototype HDR scale. The current synthetic catalog is calibrated for default exposure `1e-3`; a 2MASS blackbody normalization in steradians requires a much larger display exposure such as the example above. The optics path integrates each fitted Planck spectrum through CIE 1931 color-matching functions and converts the resulting radiance to linear sRGB; it does not use an empirical color-temperature RGB approximation. Point sources use a flux-normalized circular Moffat PSF by default (`--psf-fwhm-pixels 2.7 --psf-moffat-beta 4.5`). The FWHM matches the former 1.15-pixel Gaussian core while the Moffat wings remain continuous; both values are display/optics calibration parameters. The default renderer builds one immutable, process-wide 64-by-64 sub-pixel Moffat lookup kernel. Its weights are pixel-area integrals and are bilinearly interpolated between phase tables. The renderer reports its build time and cached/direct-fallback image counts. Pass `--psf-direct` to use the slower 8-point quadrature reference evaluator for regression comparisons; an image whose required HDR-tail support exceeds the cache radius selects that reference path automatically. The PSF-cache completion line is printed before tracing and catalog splatting begin. For long renders, pass `--verbose` to print catalog-prefetch state, splat and image-write boundaries without adding work to the splat hot path. Movie renders always print one summary per time slab; `--verbose` also prints the ray counts before each slab is loaded. For PSF validation only, `make psf-hdr-test` builds `build/minkowski_psf_hdr_test`, a separate binary with a `--hdr-output PATH` option. It writes the pre-tone-mapping RGB framebuffer as a three-plane, 32-bit float FITS image. Values remain linear HDR at the renderer's arbitrary scale; no tone mapping or per-frame normalization is applied. The ordinary binaries do not contain this option or writer. The FITS header describes a synthetic 8640-by-5760, 36-by-24 mm full-frame sensor with 4.1667 um pixels. Each render records its active centered crop and derives `FOCALLEN` from that render's width and horizontal `--fov-deg`; these camera fields support plate-solving workflows but do not calibrate flux. Pass `--draw-mesh` to alpha-composite image-plane triangle edges as one-pixel-wide 0.5 linear-gray diagnostic lines at 0.5 opacity. The line rasterizer uses coverage-based antialiasing.