Observer: parameterize Schwarzschild camera view

This commit is contained in:
wyj committed 2026-08-27 04:12:17 -04:00
1 parent 457c0728b4
commit 83dc05fd52
5 files changed
+96 -19

No files matched your search

+6 -2
View File
@@ -88,8 +88,12 @@ mkdir -p output/imgs
`SPACETIME=minkowski` (the default) and `SPACETIME=schwarzschild` select source `SPACETIME=minkowski` (the default) and `SPACETIME=schwarzschild` select source
files at compile time, so each executable contains exactly one metric provider. files at compile time, so each executable contains exactly one metric provider.
The Schwarzschild demonstration uses mass `M=1`, a static camera at Cartesian The Schwarzschild demonstration uses mass `M=1` and places a static camera at
Kerr--Schild position `(30, 0, 0)`, directed at the hole, escapes at `r=256`, 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 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 rendering thresholds are Phase-1 demonstration values, not settled production
refinement or integration settings. refinement or integration settings.
+7 -2
View File
@@ -19,6 +19,7 @@ typedef struct {
int coarse_cell_pixels; int coarse_cell_pixels;
int draw_mesh; int draw_mesh;
double horizontal_fov_deg, look_ra_deg, look_dec_deg, exposure; double horizontal_fov_deg, look_ra_deg, look_dec_deg, exposure;
double observer_radius;
double observer_inward_speed; double observer_inward_speed;
PointSpreadFunction psf; PointSpreadFunction psf;
const char *catalog_path; const char *catalog_path;
@@ -102,6 +103,7 @@ static int parse_args(int argc, char **argv, Settings *s,
.look_ra_deg = 270.0, .look_ra_deg = 270.0,
.look_dec_deg = 0.0, .look_dec_deg = 0.0,
.exposure = 1e-3, .exposure = 1e-3,
.observer_radius = 30.0,
.psf = {2.7, 4.5}, .psf = {2.7, 4.5},
.catalog_path = "assets/sky_grid_5deg.csv", .catalog_path = "assets/sky_grid_5deg.csv",
.output_path = "output/imgs/minkowski_sky.ppm", .output_path = "output/imgs/minkowski_sky.ppm",
@@ -137,6 +139,8 @@ static int parse_args(int argc, char **argv, Settings *s,
!parse_positive(argv[++i], &s->exposure)) { !parse_positive(argv[++i], &s->exposure)) {
} else if (!strcmp(argv[i], "--observer-inward-speed") && i + 1 < argc && } else if (!strcmp(argv[i], "--observer-inward-speed") && i + 1 < argc &&
!parse_speed(argv[++i], &s->observer_inward_speed)) { !parse_speed(argv[++i], &s->observer_inward_speed)) {
} else if (!strcmp(argv[i], "--observer-radius") && i + 1 < argc &&
!parse_positive(argv[++i], &s->observer_radius)) {
} else if (!strcmp(argv[i], "--psf-fwhm-pixels") && i + 1 < argc && } else if (!strcmp(argv[i], "--psf-fwhm-pixels") && i + 1 < argc &&
!parse_positive(argv[++i], &s->psf.fwhm_pixels)) { !parse_positive(argv[++i], &s->psf.fwhm_pixels)) {
} else if (!strcmp(argv[i], "--psf-moffat-beta") && i + 1 < argc && } else if (!strcmp(argv[i], "--psf-moffat-beta") && i + 1 < argc &&
@@ -184,7 +188,8 @@ static GeodesicTraceConfig trace_config(void) {
static int default_observer(const Settings *s, ObserverState *observer) { static int default_observer(const Settings *s, ObserverState *observer) {
#ifdef SPACETIME_SCHWARZSCHILD #ifdef SPACETIME_SCHWARZSCHILD
return observer_inward_schwarzschild_ks(1.0, 30.0, return observer_inward_schwarzschild_ks_look_at(
1.0, s->observer_radius, s->look_ra_deg, s->look_dec_deg,
s->observer_inward_speed, observer); s->observer_inward_speed, observer);
#else #else
*observer = observer_fixed_at_origin_look_at(s->look_ra_deg, s->look_dec_deg); *observer = observer_fixed_at_origin_look_at(s->look_ra_deg, s->look_dec_deg);
@@ -352,7 +357,7 @@ int main(int argc, char **argv) {
fprintf(stderr, fprintf(stderr,
"Usage: %s [--catalog PATH | --all-sky-catalog DIR] [--output PATH] [--width N] [--height " "Usage: %s [--catalog PATH | --all-sky-catalog DIR] [--output PATH] [--width N] [--height "
"N] [--fov-deg D] [--look-ra-deg D] [--look-dec-deg D] " "N] [--fov-deg D] [--look-ra-deg D] [--look-dec-deg D] "
"[--exposure E] [--observer-inward-speed V] " "[--exposure E] [--observer-radius R] [--observer-inward-speed V] "
"[--psf-fwhm-pixels N] [--psf-moffat-beta N] " "[--psf-fwhm-pixels N] [--psf-moffat-beta N] "
"[--coarse-cell-pixels N] [--draw-mesh] [--write-catalog PATH] " "[--coarse-cell-pixels N] [--draw-mesh] [--write-catalog PATH] "
"[--catalog-load-workers N] " "[--catalog-load-workers N] "
+57 -11
View File
@@ -30,29 +30,69 @@ ObserverState observer_fixed_at_origin_look_at(double ra_deg, double dec_deg) {
{0.0, right[0], right[1], right[2]}}}; {0.0, right[0], right[1], right[2]}}};
} }
int observer_static_schwarzschild_ks(double mass, double radius, static int schwarzschild_look_direction(double ra_deg, double dec_deg,
double direction[3], double up[3],
double right[3]) {
if (!isfinite(ra_deg) || !isfinite(dec_deg) || ra_deg < 0.0 ||
ra_deg >= 360.0 || dec_deg < -90.0 || dec_deg > 90.0)
return -1;
const double ra = ra_deg * pi / 180.0;
const double dec = dec_deg * pi / 180.0;
const double cos_ra = cos(ra), sin_ra = sin(ra);
const double cos_dec = cos(dec), sin_dec = sin(dec);
direction[0] = cos_dec * cos_ra;
direction[1] = sin_dec;
direction[2] = cos_dec * sin_ra;
up[0] = -sin_dec * cos_ra;
up[1] = cos_dec;
up[2] = -sin_dec * sin_ra;
right[0] = -sin_ra;
right[1] = 0.0;
right[2] = cos_ra;
return 0;
}
int observer_static_schwarzschild_ks_look_at(double mass, double radius,
double look_ra_deg,
double look_dec_deg,
ObserverState *out) { ObserverState *out) {
if (out == NULL || mass <= 0.0 || radius <= 2.0 * mass) double direction[3], up[3], right[3];
if (out == NULL || mass <= 0.0 || radius <= 2.0 * mass ||
schwarzschild_look_direction(look_ra_deg, look_dec_deg, direction, up,
right))
return -1; return -1;
const double f = 2.0 * mass / radius; const double f = 2.0 * mass / radius;
const double normalization = sqrt(1.0 - f); const double normalization = sqrt(1.0 - f);
*out = (ObserverState){ *out = (ObserverState){
.coordinate_time = 0.0, .coordinate_time = 0.0,
.coordinate_position = {radius, 0.0, 0.0}, .coordinate_position = {-radius * direction[0], -radius * direction[1],
/* e_(0) is the static four-velocity. e_(1) points inward; its time -radius * direction[2]},
* component makes the tetrad orthonormal in the KS metric. */ /* e_(0) is the static four-velocity. e_(1) points inward, toward the
* origin; its time component makes the tetrad orthonormal in the KS
* metric. */
.tetrad = {{1.0 / normalization, 0.0, 0.0, 0.0}, .tetrad = {{1.0 / normalization, 0.0, 0.0, 0.0},
{-f / normalization, -normalization, 0.0, 0.0}, {-f / normalization, normalization * direction[0],
{0.0, 0.0, 1.0, 0.0}, normalization * direction[1], normalization * direction[2]},
{0.0, 0.0, 0.0, 1.0}}}; {0.0, up[0], up[1], up[2]},
{0.0, right[0], right[1], right[2]}}};
return 0; return 0;
} }
int observer_inward_schwarzschild_ks(double mass, double radius, int observer_static_schwarzschild_ks(double mass, double radius,
double inward_speed, ObserverState *out) { ObserverState *out) {
return observer_static_schwarzschild_ks_look_at(mass, radius, 180.0, 0.0,
out);
}
int observer_inward_schwarzschild_ks_look_at(double mass, double radius,
double look_ra_deg,
double look_dec_deg,
double inward_speed,
ObserverState *out) {
ObserverState static_observer; ObserverState static_observer;
if (inward_speed < 0.0 || inward_speed >= 1.0 || if (inward_speed < 0.0 || inward_speed >= 1.0 ||
observer_static_schwarzschild_ks(mass, radius, &static_observer)) observer_static_schwarzschild_ks_look_at(
mass, radius, look_ra_deg, look_dec_deg, &static_observer))
return -1; return -1;
const double gamma = 1.0 / sqrt(1.0 - inward_speed * inward_speed); const double gamma = 1.0 / sqrt(1.0 - inward_speed * inward_speed);
@@ -65,3 +105,9 @@ int observer_inward_schwarzschild_ks(double mass, double radius,
} }
return 0; return 0;
} }
int observer_inward_schwarzschild_ks(double mass, double radius,
double inward_speed, ObserverState *out) {
return observer_inward_schwarzschild_ks_look_at(mass, radius, 180.0, 0.0,
inward_speed, out);
}
+15 -4
View File
@@ -12,12 +12,23 @@ ObserverState observer_fixed_at_origin(void);
/* Point the fixed inertial observer at an ICRS-style RA/Dec direction. /* Point the fixed inertial observer at an ICRS-style RA/Dec direction.
* The local spatial axes remain (forward, celestial north, increasing RA). */ * The local spatial axes remain (forward, celestial north, increasing RA). */
ObserverState observer_fixed_at_origin_look_at(double ra_deg, double dec_deg); ObserverState observer_fixed_at_origin_look_at(double ra_deg, double dec_deg);
/* Static camera at (radius, 0, 0) in Cartesian Kerr--Schild coordinates, /* Static camera at Cartesian Kerr--Schild position -radius * look_direction,
* directed toward the Schwarzschild black hole at the origin. */ * directed toward the Schwarzschild black hole at the origin. look_direction
* uses the same ICRS-style RA/Dec convention as the flat-space camera. */
int observer_static_schwarzschild_ks_look_at(double mass, double radius,
double look_ra_deg,
double look_dec_deg,
ObserverState *out);
/* Compatibility shortcut for the +X camera directed toward the origin. */
int observer_static_schwarzschild_ks(double mass, double radius, int observer_static_schwarzschild_ks(double mass, double radius,
ObserverState *out); ObserverState *out);
/* Camera at (radius, 0, 0), moving inward at local speed inward_speed with /* As above, with a local radial inward boost relative to the static observer. */
* respect to the static Schwarzschild observer. */ int observer_inward_schwarzschild_ks_look_at(double mass, double radius,
double look_ra_deg,
double look_dec_deg,
double inward_speed,
ObserverState *out);
/* Compatibility shortcut for the +X camera directed toward the origin. */
int observer_inward_schwarzschild_ks(double mass, double radius, int observer_inward_schwarzschild_ks(double mass, double radius,
double inward_speed, ObserverState *out); double inward_speed, ObserverState *out);
+11
View File
@@ -7,6 +7,7 @@ int main(void) {
SpacetimeSource spacetime = {0}; SpacetimeSource spacetime = {0};
MetricData metric; MetricData metric;
ObserverState observer; ObserverState observer;
ObserverState oriented_observer;
ObserverState inward_observer; ObserverState inward_observer;
const GeodesicTraceConfig trace = {.coordinate_time_step = 0.1, const GeodesicTraceConfig trace = {.coordinate_time_step = 0.1,
.max_steps = 4096, .max_steps = 4096,
@@ -17,6 +18,16 @@ int main(void) {
!isfinite(metric.alpha) || !isfinite(metric.gamma[0][0]) || !isfinite(metric.alpha) || !isfinite(metric.gamma[0][0]) ||
!isfinite(metric.K[0][0]) || !isfinite(metric.K[0][0]) ||
observer_static_schwarzschild_ks(1.0, 30.0, &observer) || observer_static_schwarzschild_ks(1.0, 30.0, &observer) ||
observer_static_schwarzschild_ks_look_at(1.0, 40.0, 270.0, 30.0,
&oriented_observer) ||
fabs(oriented_observer.coordinate_position[0]) > 1e-12 ||
fabs(oriented_observer.coordinate_position[1] + 20.0) > 1e-12 ||
fabs(oriented_observer.coordinate_position[2] - 20.0 * sqrt(3.0)) >
1e-12 ||
fabs(oriented_observer.tetrad[1][1]) > 1e-12 ||
fabs(oriented_observer.tetrad[1][2] - 0.5 * sqrt(0.95)) > 1e-12 ||
fabs(oriented_observer.tetrad[1][3] + sqrt(0.95) * sqrt(3.0) / 2.0) >
1e-12 ||
observer_inward_schwarzschild_ks(1.0, 30.0, 0.5, observer_inward_schwarzschild_ks(1.0, 30.0, 0.5,
&inward_observer) || &inward_observer) ||
fabs(inward_observer.tetrad[0][0] - fabs(inward_observer.tetrad[0][0] -