Pixel sampling
This commit is contained in:
1 parent
782820261e
commit
4d070db85e
17 files changed
+918
-128
No files matched your search
+63
-20
@@ -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
|
||||
|
||||
Reference in new issue
Block a user