263 lines
13 KiB
Markdown
263 lines
13 KiB
Markdown
# 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.
|
|
|
|
### Adaptive image mesh refinement
|
|
|
|
Adaptive refinement is disabled by default (`--refine-max-level 0`), so the
|
|
existing coarse-mesh renders remain unchanged. When enabled, its defaults are
|
|
an absolute direction error of `1e-3` degrees, relative error `0.1`, minimum
|
|
long edge `0.5` pixels, minimum area `0.25` pixel-squared, and a provisional
|
|
minimum discrete-Jacobian magnitude of `1e-3`. Each value can be overridden
|
|
independently:
|
|
|
|
```text
|
|
--refine-max-level N
|
|
--refine-angle-abs-deg D
|
|
--refine-angle-rel R
|
|
--refine-jacobian-min J
|
|
--refine-min-edge-pixels P
|
|
--refine-min-area-pixels2 A
|
|
```
|
|
|
|
`N` caps the triangle refinement level. Let `e` be the angle between the
|
|
traced longest-edge midpoint direction and the normalized endpoint
|
|
interpolation, and let `s` be the angle between those two endpoint **camera
|
|
directions**. Both are evaluated internally in radians; the absolute CLI
|
|
threshold `D` is specified in degrees and converted before comparison. `s`
|
|
is the angular geometric size of the image triangle's test edge, not a
|
|
source-sky/lens-map length. A locally escaped triangle is split only when
|
|
**both** `e > D_rad` (the converted `--refine-angle-abs-deg D`) and
|
|
`e / max(s, 1e-15) > --refine-angle-rel`. `P` and `A`
|
|
prevent selecting a leaf already at or below the requested image-plane
|
|
long-edge and area scales.
|
|
Triangles whose three vertices disagree between capture and escape are split
|
|
independently of the direction-error thresholds, allowing the mesh to follow a
|
|
shadow boundary.
|
|
|
|
Independently of the midpoint geometry test, an all-escaped triangle also
|
|
computes the discrete lens Jacobian
|
|
`J = Omega_source / Omega_image`. Both signed solid angles use
|
|
`2 atan2(dot(a, cross(b,c)), 1 + dot(a,b) + dot(b,c) + dot(c,a))`, with the
|
|
ordered camera directions for `Omega_image` and their traced infinity
|
|
directions for `Omega_source`. A J-driven split requires **both** a shared
|
|
image edge whose incident triangles have opposite nonzero signs of `J` and
|
|
`min(abs(J_left), abs(J_right)) < --refine-jacobian-min`. It then requests
|
|
that shared edge on both leaves. Thus `|J|` bounds the fold selection instead
|
|
of widening it as a standalone critical-curve band. A negative sign is
|
|
physical parity and is retained. The `1e-3` default is deliberately
|
|
provisional and should be tuned with the small Schwarzschild refinement
|
|
diagnostic before being treated as a production threshold.
|
|
|
|
For a short Schwarzschild diagnostic that permits at most one actual split
|
|
generation, for example:
|
|
|
|
```sh
|
|
./build/schwarzschild_sky --catalog assets/sky_grid_5deg.csv \
|
|
--width 48 --height 48 --coarse-cell-pixels 24 --fov-deg 40 \
|
|
--refine-max-level 1 --refine-angle-abs-deg 0.001 \
|
|
--refine-angle-rel 0.001 --refine-jacobian-min 0.001 \
|
|
--refine-min-edge-pixels 1 \
|
|
--refine-min-area-pixels2 1 --draw-mesh \
|
|
--output output/imgs/schwarzschild_refinement.png
|
|
```
|
|
|
|
For movies, each refinement generation completes the full newest-to-oldest
|
|
time-slab sweep before any probe becomes a mesh vertex. Newly added vertices
|
|
are therefore traced only by the next generation; the renderer never returns
|
|
to a slab that has already been released. At the start of every generation,
|
|
newly inserted vertices and geometry-only longest-edge probes for its new
|
|
leaves are collected together, so both ray sets use the same parallel
|
|
`RayPool` pass.
|
|
|
|
`--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-worker local heartbeats (8, 16, 32, ... completed triangles per worker),
|
|
and image-write boundaries. The worker heartbeats use neither global progress
|
|
accounting nor cross-worker synchronization.
|
|
Verbose ray-trace output reports the initial mesh trace and refinement stages
|
|
for single frames; movie mode additionally reports each generation's sample
|
|
count and each time slab's activation and terminal-ray summary.
|
|
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.
|