xfy 65a7611868 fix(disk): kill concentric moiré + smooth lensed-image rings via AA
Two distinct sampling artifacts on the accretion disk, found with a
CPU pixel-mirror of the integrator:

1. Moiré on the disk. integrate_disk_segment placed its N sub-samples
   on the deterministic (i+0.5)/N grid. That lattice aliases against
   the 2-D pixel grid and the per-ray RK45 step lattice → a concentric
   banding. Fix: jitter the sub-sample position within stratum i using
   a hash of the pixel coordinate and the sample index. The jitter is
   pixel-seeded only (no time), so the noise is spatially stable (no
   per-frame flicker) and stays inside the stratum, preserving the
   quadrature order. Verified by CPU mirror: banding transitions drop
   sharply and the noise is unstructured.

2. Higher-order lensed-image rings read as an artifact. Rays near the
   critical impact parameter wrap around the hole; each wrap crosses
   the disk plane once, projecting to a ring (a physical Einstein-ring
   structure, the same one Gargantua shows). The integrator renders
   each wrap as a sharp discrete band, so at high `steps` the rings
   look like a bug. Fix: per-pixel stochastic supersampling — fire
   several jittered sub-rays per pixel and average them, which
   antialiases the band edges into a smooth gradient while keeping the
   underlying lensing physics.

The two fixes share a `pixel_seed` (var<private>, set from
@builtin(position)) so the jitter is deterministic per pixel.

Shader refactor: the single-ray march is extracted into march(dir);
fragment() now loops it MAX_AA_SAMPLES times (compile-time cap for
WGSL's static-loop-bound requirement, runtime count from a new
aa_samples uniform via early break — the fbm3/MAX_OCTAVES pattern).

Uniform layout: added aa_samples: u32 + two f32 pads to round the
struct to 16-byte alignment, mirrored byte-for-byte across the WGSL
struct and BlackHoleUniforms (ShaderType).

UI/params: new AaQuality enum (Off/Low/High = 1/2/4 samples) in the
Quality panel. Defaults are tiered per AGENTS.md: Off on web, Low (2×)
on desktop — the RK45 march is already ~10× Phase 1's cost, so desktop
stays at 2× with High (4×) selectable for a fully smooth look.

cargo test: 20 passed. cargo build --release: clean.
2026-07-16 16:02:41 +08:00
..