Pixel sampling

This commit is contained in:
connor committed 2026-10-15 16:28:43 -04:00
1 parent 782820261e
commit 4d070db85e
17 files changed
+918 -128

No files matched your search

+63 -20
View File
@@ -365,34 +365,77 @@ bodies and the other for the target.
The bodies use ray casting, because their shapes have exact equations and
their edges are what limb-based navigation measures. The target uses
rasterization, because a spacecraft model has no equation, only its mesh.
Both write into the same depth image, so they sort against each other
correctly. Ray casting a mesh is also possible, by testing rays against
The meshes are drawn first, with their depth. The bodies pass then casts its
rays and compares depths at every pixel, so the two sort against each other
correctly: a target in front of Earth covers it, and a target behind the limb
is hidden. Ray casting a mesh is also possible, by testing rays against
its triangles. A ray tracer, planned, does that, because following rays
gives exact shadows and light bouncing between surfaces. Rasterization can
only approximate those, for example with the shadow maps lighting L1 uses
(below).
### One sample per pixel
### Pixel area
Both techniques decide each pixel's value from one point, the pixel's
center. A real pixel collects light over its whole area, about 5.5 um
square on the detector. Where a pixel straddles an edge, say half on a
sunlit panel and half on dark space, the real pixel reads about half the
panel's value. The renderer reads either the full value or none, depending
on which side the center falls.
At camera tier C0, both techniques decide each pixel's value from one
point, the pixel's center. A real pixel collects light over its whole area,
about 5.5 um square on the detector. Where a pixel straddles an edge, say
half on a sunlit panel and half on dark space, the real pixel reads about
half the panel's value. At C0 the renderer reads either the full value or
none, depending on which side the center falls.
This shows in two ways. Edges are stair-stepped instead of graded. And
anything thinner than a pixel, like the booms of the example satellite seen
from 40 m, breaks into dashes, because only some pixel centers land on it.
For an algorithm that finds edges or measures brightness at sub-pixel
precision, the graded edge is the signal, so this matters for limb
navigation and pose estimation.
A regular pattern finer than the pixels, like the cells of a solar array,
beats against the pixel grid and shows as curved bands (moiré). For an
algorithm that finds edges or measures brightness at sub-pixel precision,
the graded edge is the signal, so this matters for limb navigation and
pose estimation.
The usual fix is supersampling: shading several points inside each pixel
and averaging. The GPU's multisampling hardware does this cheaply for
rasterized edges. Sampling the bodies' limbs needs it in the ray casting
shader. The lens blur of the point spread function, once modeled, also
spreads every edge over a few pixels.
Camera tier C1 samples each pixel at several points and averages them
(supersampling), in a way that costs little where nothing is crossing the
pixel:
- **Meshes** use the GPU's multisampling. The rasterizer tests each
triangle against 4 points in every pixel, and keeps a depth for each, but
runs the fragment shader once per pixel. A pixel half covered by a panel
then gets the panel's value in 2 of its 4 samples, and reads half. The
covered fraction comes in steps of a quarter, and the light is computed
at one point inside the covered part (the centroid of the covered
samples), so a pixel on an edge is still shaded as one surface.
- **Bodies** are ray cast, so the bodies pass first decides whether a body's
edge could cross the pixel: whether the line of sight through its center
passes within one pixel's angle of the edge of Earth, the Moon or the Sun.
Most pixels do not, and cast one ray as at C0. A pixel on an edge casts a
grid of 4 x 4 rays spread over its area and averages them. A body only a
few pixels across needs a finer grid, because its brightness falls off
toward its edge and a coarse grid misjudges that: a 4 x 4 grid gets a
full Moon 0.45 pixels wide 45 % wrong in total light. So the grid has at
least 8 rays across the body's radius, up to 64 x 64 for one smaller than
a pixel, which keeps the total within 1 %. Only a handful of pixels pay
for that.
- **Both together.** Where a target and a body share a pixel, each of the
grid's rays takes the mesh sample nearest to it, and keeps the target if
that sample is nearer than the body along the ray. So a target seen just
past Earth's limb takes its share of the pixel, and Earth keeps the rest.
A grid of 4 x 4 is still a coarse estimate of an area. For an edge nearly
aligned with the pixel grid, its 4 columns of rays resolve the covered
fraction in quarters, so a pixel on the limb can be off by up to an eighth
of the difference across the edge (measured 0.10 on Earth's limb from low
orbit, against up to a half at C0). Patterns that stagger the rays fix
aligned edges but do worse at other angles; the regular grid had the
smallest error on average over all angles. The lens blur of the point
spread function, once modeled, spreads every edge over a few pixels.
C1 costs 0.4 to 0.5 ms more per 1920 x 1080 frame on an RTX 3060, and 1.2
to 1.4 ms at 3840 x 2160, measured on Landsat 8, ICESat-2 and the moonrise
scene. On the CPU through lavapipe it costs much more, 2.6 to 8 times the
mesh pass at C0 (ICESat-2's 752,000 triangles go from 74 to 580 ms per
frame), because lavapipe appears to run the fragment shader for every
sample rather than every pixel. The 4 samples of radiance and depth also
take 80 bytes per pixel of GPU memory: 166 MB at 1920 x 1080 and 660 MB at
3840 x 2160.
## Earth, Moon and Sun
@@ -840,8 +883,9 @@ Limitations:
gap narrower than about 2 texels between a blocker and the surface it
shades (a few mm on Landsat 8) lets light through, because of the acne
margins above.
- **One sample per pixel.** Shadows are sampled at the pixel center, like
everything else, until pixel-area sampling is added.
- **One shadow lookup per pixel.** Shadows are looked up once per pixel, even
at camera C1, so the edge of a shadow inside a pixel is not graded by its
4 samples; the penumbra's own width usually grades it.
- **Bodies.** Earth and the Moon do not use shadow maps. Their shadows on
the target are the eclipse factor below, applied to the whole target.
Light from Earth and the Moon (below) is not shadowed at all.
@@ -1070,7 +1114,6 @@ velocity as an input, which the scene does not carry yet.
## Not modeled yet
- Earthshine on the Moon's night side
- Pixel-area sampling (anti-aliasing): graded edges, thin parts
- Lens distortion
- Blur from diffraction and lens imperfections (point spread function)
- Noise: photon shot noise, read noise, dark current, pixel-to-pixel variation