Added Airy and PSF via FFT. Decided to make it a C2 feature
This commit is contained in:
1 parent
fbd7ce4147
commit
5e2769faa8
25 files changed
+2319
-117
No files matched your search
+88
-6
@@ -727,7 +727,9 @@ take 80 bytes per pixel of GPU memory: 166 MB at 1920 x 1080 and 660 MB at
|
||||
|
||||
## Blur: the point spread function
|
||||
|
||||
Code: `camera.rs` (`Psf`); `gpu/blur.rs`, `shaders/blur.wgsl`.
|
||||
Code: `camera.rs` (`Psf`); `gpu/blur.rs`, `shaders/blur.wgsl`; from camera
|
||||
C2, `psf.rs`, `gpu/convolve.rs`, `gpu/fft.rs`, `shaders/convolve.wgsl`,
|
||||
`shaders/fft.wgsl`.
|
||||
|
||||
No camera focuses a point to a point. Light from one point of the scene
|
||||
lands on the detector as a small spot, and the shape of that spot is the
|
||||
@@ -816,9 +818,9 @@ small-pixel averages, and the same error remains at a smaller scale:
|
||||
|
||||
(sigma 0.4 px, points at 16 and edges at 129 positions within a pixel; the
|
||||
worst edges lie on a pixel boundary.) C1 renders 2 x 2 by default;
|
||||
`RendererOptions::supersampling` sets 1 to 4. Point sources, stars in
|
||||
particular, need better than that, and will be drawn with the blurred spot
|
||||
computed exactly over each pixel instead.
|
||||
`RendererOptions::supersampling` sets 1 to 4. Point sources need better
|
||||
than that, so stars are drawn with the blurred spot computed exactly over
|
||||
each pixel instead (see Stars).
|
||||
|
||||
A 2D Gaussian is the product of a 1D Gaussian across and one down, and
|
||||
averaging an n x n block is likewise an average across and one down. So the
|
||||
@@ -860,6 +862,83 @@ as an error. On the CPU through lavapipe, 2 x 2 adds 9 to 13 ms to
|
||||
1920 x 1080 frames of 116 to 680 ms, and 3 x 3 at 3840 x 2160 runs out of
|
||||
memory.
|
||||
|
||||
### Diffraction and the PSF's wings (camera C2)
|
||||
|
||||
A Gaussian falls to nothing within a few sigma. A real camera's spot does
|
||||
not: diffraction's rings fade only as the cube of the distance, and dust and
|
||||
rough lens surfaces scatter a little light much farther. Next to most of a
|
||||
scene that faint halo, the PSF's wings, is invisible. Next to the Sun it is
|
||||
not. In the sun-glare example scene each pixel of the Sun's disk collects
|
||||
about 5e8 electrons in its 0.4 ms exposure, 26,000 full wells, over about
|
||||
5,700 pixels. Diffraction at f/4 spreads a fraction of about
|
||||
λN / (π^3 r^3) of a point's light into each pixel r pixels away (λN, the
|
||||
wavelength times the f-number, is 0.39 px for green light in the example
|
||||
camera), so 500 px from the Sun each pixel still gets about 300 electrons
|
||||
from it, and 1000 px away about 40: a glow over the whole frame that raises
|
||||
the background, can hide a faint target, and pulls a limb fit toward the
|
||||
bright side.
|
||||
|
||||
Camera tier C2 models this. `Psf::Airy { obstruction }` is diffraction by
|
||||
the camera's aperture: the Airy pattern, set by the f-number, with an
|
||||
optional central obstruction such as a reflecting telescope's secondary
|
||||
mirror (which moves light from the core into the rings). The pattern scales
|
||||
with the wavelength, so a channel's spot is the pattern averaged over the
|
||||
channel's band, each wavelength weighted by the photons sunlight gives it
|
||||
there; across an 80 nm band the outer rings blur into a smooth halo.
|
||||
`Psf::Measured { image, pixel_scale }` takes a spot measured in a lab or in
|
||||
flight, the same over the whole field, one image per channel or one for
|
||||
all. Measured: a 2 nm wide channel's first dark ring falls within 1 % of
|
||||
1.22 λN, where the textbook puts it.
|
||||
|
||||
A kernel reaching hundreds of pixels is far too slow to apply pixel by
|
||||
pixel. C2 instead uses the convolution theorem: transform the image into
|
||||
its frequencies with a fast Fourier transform (FFT), multiply by the
|
||||
kernel's frequencies, computed once at setup, and transform back. The cost
|
||||
no longer depends on the kernel's size, only on the image's. The transform
|
||||
runs on the GPU in compute shaders, sized to powers of two up to 4096 on a
|
||||
side, with two channels sharing one complex transform, so three channels
|
||||
take four transforms per frame. C2 applies every PSF this way, Gaussians
|
||||
too, and draws stars into the focal plane before the transform, so they get
|
||||
the same wings; each star's light is shared among its four nearest rendered
|
||||
pixels, which keeps its center exact but can widen its spot by up to 0.25 px
|
||||
RMS at 2 x 2.
|
||||
|
||||
As at C1, the transform acts on the scene rendered finer than the pixels,
|
||||
before they average it. The kernel is the PSF integrated over a rendered
|
||||
pixel twice, once for the pixel the light leaves and once for the one it
|
||||
lands in, so a Gaussian at C2 differs slightly from C1's, which samples the
|
||||
Gaussian at the rendered pixels' centers (by up to 0.34 % of the brightest
|
||||
pixel, measured on the Moon). The transform treats the image as repeating,
|
||||
so the renderer draws a margin around it as wide as the kernel reaches, and
|
||||
the grid holds the image and that margin. The reach is therefore set by the
|
||||
grid: at least `RendererOptions::psf_reach` (16 px by default), and as far
|
||||
as the smallest grid that holds it allows. Light the kernel would carry
|
||||
farther is added back to the spot instead, so none is lost, but the glow
|
||||
stops at the reach, and with the Sun in the frame its edge shows in a
|
||||
brightened preview.
|
||||
|
||||
| Image, rendered pixels per pixel | Grid | Reach | RTX 3060 | lavapipe |
|
||||
|---|---|---|---|---|
|
||||
| 1920 x 1080, 2 x 2 | 4096 x 4096 | 64 px | 30 ms | 560 ms |
|
||||
| 1920 x 1080, 1 x 1 | 2048 x 2048 | 64 px | 5.8 ms | 213 ms |
|
||||
| 1920 x 1080, 1 x 1, reach 1000 | 4096 x 4096 | 1088 px | 29 ms | 556 ms |
|
||||
| 3840 x 2160, 1 x 1 | 4096 x 4096 | 128 px | 30 ms | 623 ms |
|
||||
|
||||
(The convolution pass alone, in the sun-glare scene at lighting L1. The
|
||||
whole frame took 35 ms at 1920 x 1080 and 2 x 2, against 5.9 ms at C1.) A
|
||||
grid of 4096 x 4096 takes 640 MB on the device. 3840 x 2160 at 2 x 2 would
|
||||
need a grid 8192 across and is refused; 1 x 1 fits.
|
||||
|
||||
The transforms work in 32-bit floating point, which keeps about seven
|
||||
significant digits, and their rounding error spreads over the whole grid in
|
||||
proportion to its brightest values. With the Sun in the frame that leaves a
|
||||
floor of about 10 electrons RMS, up to 165, across the frame (measured in
|
||||
the sun-glare scene with a noiseless detector, where C1 reads exactly 0),
|
||||
above the example detector's 5 electrons of read noise. A sunlit target at
|
||||
half the full well leaves a floor near 1e-3 electrons, far below any noise.
|
||||
Computing the brightest sources' wings separately, outside the transform,
|
||||
would remove the Sun's floor; that is not done yet.
|
||||
|
||||
## Earth, Moon and Sun
|
||||
|
||||
Code: `bodies.rs`, `gpu/bodies.rs`, `shaders/bodies.wgsl`.
|
||||
@@ -1638,7 +1717,9 @@ since all of a star's light that enters lands in its spot. A star is not
|
||||
rendered and then blurred: a point of light rendered into a pixel loses
|
||||
where in the pixel it fell (see the blur section). Instead its electrons are
|
||||
spread over the pixels around it as the Gaussian PSF integrated over each
|
||||
pixel's area, which is exact for any position within a pixel. The detector
|
||||
pixel's area, which is exact for any position within a pixel. (Camera C2
|
||||
draws stars into the focal plane before its convolution instead, so they
|
||||
take the Airy or measured PSF too; see the PSF's wings.) The detector
|
||||
adds them to the light from everything else before the full well, so a
|
||||
bright star saturates like any other light. A star is hidden wherever
|
||||
anything nearer is drawn, Earth, the Moon or a target: stars are at
|
||||
@@ -1672,7 +1753,8 @@ is a run of the script with `--gaia 16`, at twice the size.
|
||||
## Not modeled yet
|
||||
|
||||
- Earthshine on the Moon's night side
|
||||
- The diffraction pattern's rings, and a lens's measured point spread function
|
||||
- A point spread function that changes across the field, and the PSF's wings
|
||||
past the reach of camera C2's grid
|
||||
- Non-Lambertian materials (BRDFs), glints, and the full Moon's edge as bright
|
||||
as its center
|
||||
- Clouds on Earth
|
||||
|
||||
Reference in new issue
Block a user