Files
resolved/readme.md
T
2026-10-14 17:23:25 -04:00

129 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# resolved
Renders what a spacecraft camera sees: Earth, Moon, Sun and a target
spacecraft loaded from a mesh. The inputs are an epoch, the observer and target
states and attitudes, and a camera model. Images are radiometric: the renderer
works in radiance and converts to electrons and then DN through the camera's
optics and detector, so an image should match what a real camera with the same
parameters records.
It is built for closed-loop simulation, where each step renders one frame and
latency is the number that matters. The goal is high-fidelity 1–2 MP RGB in
about 10 ms end to end on a mid-range GPU (the reference is an RTX 3060);
100 ms is the ceiling, not the aim. Lighting fidelity and camera fidelity are
selected separately, and each tier is benchmarked, so a run can trade
fidelity for speed knowing what each step costs.
Rendering goes through wgpu. On a machine without a GPU, the same shaders run
on the CPU through Mesa's lavapipe (`vulkan-swrast` on Arch,
`mesa-vulkan-drivers` on Debian and Ubuntu).
## Usage
```rust
use resolved::nalgebra::{UnitQuaternion, Vector3};
use resolved::radiometry::AU;
use resolved::{CameraConfig, Mesh, Pose, Renderer, Scene, Target};
# fn main() -> Result<(), resolved::Error> {
// Setup, once: device, shaders, spectra folded into the camera's channels.
let mut renderer = Renderer::new(CameraConfig::example(), Default::default())?;
// A model, uploaded once. Usually `Mesh::load(path, &options)` on an STL or glTF.
let stl = "solid panel
facet normal 0 0 0
outer loop
vertex 0 -1 -1
vertex 0 1 -1
vertex 0 0 1
endloop
endfacet
endsolid panel";
let panel = renderer.load_model(&Mesh::from_stl(&mut std::io::Cursor::new(stl), "panel", &Default::default())?)?;
// Each step: inertial positions in meters, Earth at the origin.
let target = Vector3::new(7.0e6, 0.0, 0.0);
let scene = Scene {
earth_attitude: UnitQuaternion::identity(),
sun: Vector3::new(AU, 0.0, 0.0),
moon: Vector3::new(0.0, 3.844e8, 0.0),
targets: vec![Target { model: panel, pose: Pose::new(target, UnitQuaternion::identity()) }],
};
let observer = Pose::looking_at(target + Vector3::new(20.0, 0.0, 0.0), target, Vector3::z()).unwrap();
let pending = renderer.submit(&scene, &observer)?;
// ... the simulation's own work for this step overlaps the GPU's here ...
let frame = pending.wait()?;
// 12-bit DN, channels r, g, b interleaved. The panel faces the Sun and the camera.
assert!(frame.dn_at(960, 540, 1) > 1000);
# Ok(())
# }
```
## Features
- `astrokit`: build a `Scene` from an epoch with
[astrokit](https://git.rcjohnstone.com/connor/astrokit)'s ephemerides (JPL
DE440s for the Sun and Moon, ITRF93 for Earth's orientation), through
`ephemeris::Ephemeris`. astrokit is not on crates.io, so this expects a
checkout at `../astrokit` next to this repository, and its kernels from its
`scripts/fetch-kernels.sh` (or `ASTROKIT_KERNELS` pointing at them).
## Status
M1 in progress: the library renders Earth, Moon, Sun and targets to DN
through `Renderer`; the CLI and benchmarks are still to come. The work is
tracked as Gitea milestones, each split into one issue per change:
| Milestone | Scope |
|---|---|
| M1 | Scene API, headless wgpu, analytic Earth/Moon/Sun, glTF and STL targets, Lambert shading with eclipse, pinhole camera, radiance to DN, CLI, benchmark baseline |
| M2 | Self-shadowing, Earthshine and Moonshine, Earth and Moon albedo maps, Sun limb darkening |
| M3 | Lens distortion, PSF, detector noise, saturation and blooming, stars |
| M4 | BRDF and spectral materials, atmospheric scattering, clouds, lunar terrain |
| M5 | Portable ray tracing, path tracing, label outputs for ML |
| M6 | Validation against lunar, Earth and Mitsuba references; motion blur, rolling shutter, stray light; multi-band and 4k |
## Conventions
- SI units throughout: meters, seconds, radians, watts.
- Scene state is f64 in an inertial frame (GCRF). The GPU only sees f32
coordinates relative to the camera.
## Layout
- `crates/resolved`: the library.
- `crates/resolved-cli`: the `resolved` command, for rendering without writing
a host program.
- `docs/optics.md`: the optics and radiometry the renderer models, explained
for readers without an optics background.
## Development
Point git at the committed hooks once per clone:
```sh
git config core.hooksPath hooks
```
`pre-commit` runs formatting, clippy and the library unit tests; `pre-push`
runs the full test suite, including doctests, and the documentation build.
The integration tests render on both a hardware GPU and lavapipe, so running
them needs a Vulkan driver for the GPU and lavapipe installed. Set
`WGPU_BACKEND`, `WGPU_ADAPTER_NAME` and the other standard wgpu environment
variables to steer adapter discovery.
To see what the GPU tests render, set `RESOLVED_TEST_IMAGES=1`. Each test then
writes a PNG per device to `target/tmp/test-images/`, scaled so a white
surface facing the Sun is full white:
```sh
RESOLVED_TEST_IMAGES=1 cargo test --test bodies --test mesh
```
## License
MIT