xfy 17fbbf98e1 render: derive Default for BlackHoleMaterial
Clippy warned that the manual Default implementation for BlackHoleMaterial
could be derived (clippy::derivable_impls). We now derive Default directly
on the struct and remove the manual implementation block.
2026-07-14 15:09:38 +08:00

singularity-rs

A real-time, physically-motivated Schwarzschild black-hole renderer in Bevy 0.19. Each pixel geodesic-ray-traces curved spacetime, producing gravitational lensing, the Einstein ring, a Doppler-beamed accretion disk, a lensed starfield, and optional lensed planets, a spacetime-curvature (Flamm) grid, and a cubemap skybox. Runs on desktop and the web (WebGPU).

The visual target is Gargantua from Interstellar: a black shadow surrounded by a tilted, glowing accretion disk whose back side is lensed up and over the hole (and down underneath), with one side brighter from relativistic Doppler beaming.

Run

Desktop (Vulkan / Metal / D3D12):

cargo run --release

Web (WebGPU — Chrome, Edge, recent Firefox/Safari):

cargo install --locked trunk
rustup target add wasm32-unknown-unknown
trunk serve
# open http://127.0.0.1:8080

Release web build: trunk build --release. On a browser without WebGPU, the page shows a fallback message instead of a blank canvas.

Controls

  • Drag (mouse) — orbit the camera around the hole.
  • Scroll — zoom (changes distance / impact parameter).
  • Controls panel (top-left) — live-tune every parameter:
    • Camera — distance, yaw, pitch, FOV.
    • Accretion Disk — inner/outer radius, tilt, brightness, rotation speed.
    • Doppler — enable + strength of the relativistic beaming asymmetry.
    • Renderer — integrator step count (the main quality/perf lever).
    • Background — procedural star intensity, optional cubemap skybox intensity.
    • Grid — toggle the lensed Flamm-paraboloid curvature grid + density.

Orbit input is automatically disabled while the cursor is over the panel.

How it works

A single full-screen quad carries a custom Material2d whose fragment shader, for every pixel:

  1. Generates a primary ray from the camera basis + FOV.
  2. Integrates the ray through Schwarzschild spacetime with RK4, applying the discretized bending acceleration a = -1.5·Rs·h²/r⁵ · pos (h = angular momentum).
  3. At each step tests the bent segment against the accretion disk (equatorial plane), lensed planets (storage buffer), and the Flamm paraboloid grid surface — compositing hits front-to-back.
  4. Terminates on capture (r < Rs, the shadow emerges naturally at the critical impact parameter b_crit = 3√3/2·Rs ≈ 2.598) or escape (samples the procedural starfield / cubemap along the bent final direction).

The disk is tilted relative to the camera, so the back of the disk is lensed over the top and under the bottom of the shadow — the characteristic "halo." Doppler beaming brightens the approaching side.

The CPU-side physics (src/physics.rs) mirrors the integrator and is unit-tested: b < b_crit rays are captured, b > b_crit rays escape.

Performance

Defaults target ~60 FPS at full resolution on a discrete/integrated GPU (developed on Apple M4). On web, defaults drop to steps=200 for interactivity. If you need more FPS, lower Steps in the Controls panel.

Note: the render_scale parameter exists but is not wired to a real sub-resolution render target in Phase 1 (the full-screen quad always renders at window resolution). It is reserved for future work; Steps is the real performance lever today.

Project layout

src/
  main.rs            app entry, plugin wiring
  camera.rs          orbit controller (yaw/pitch/zoom) + WantsPointer
  params.rs          BlackHoleParams (tunable, mirrored to GPU each frame)
  physics.rs         CPU mirror of the geodesic integrator (unit-tested)
  ui.rs              egui Controls panel (collapsible sections)
  web.rs             wasm glue: WebGPU detection + fallback message
  scene/planets.rs   Planet component + storage-buffer upload
  render/            BlackHolePlugin, material, fullscreen quad
assets/shaders/      WGSL: ray gen, Schwarzschild RK4, disk, stars, planets, grid, skybox
docs/superpowers/    design spec + implementation plan

Status

Phase 1 (Schwarzschild, this codebase) — complete: shadow, tilted Doppler accretion disk with lensed Einstein halo, lensed starfield, lensed planets, lensed Flamm grid, optional cubemap skybox, live egui controls, desktop + web/WebGPU.

Phase 2 (Kerr / true Gargatua) — future work. Replace the Schwarzschild integrator with the Kerr metric (Boyer-Lindquist, adaptive RK4, spin parameter) for frame-dragging and ergosphere asymmetry. Same scene elements, camera, params, and UI carry over; only the integrator core swaps. See docs/superpowers/specs/2026-07-09-interstellar-blackhole-design.md.

Description
No description provided
Readme 649 KiB
Languages
Rust 71.7%
WGSL 27%
Shell 0.7%
HTML 0.6%