Atmosphere

This commit is contained in:
connor committed 2026-10-17 22:02:21 -04:00
1 parent a3f62301f4
commit 4b1198a351
21 files changed
+2253 -204

No files matched your search

+147 -12
View File
@@ -1012,8 +1012,9 @@ lit) right. It misses some things that are visible in real images:
old basalt lava, the "seas" seen with the naked eye) and its bright
highlands also differ in albedo by about a factor of two, which from
lighting L1 the Moon's map shows.
- Earth's atmosphere softens the limb and adds a thin bright band around it
(not modeled yet). Here Earth's edge is a sharp line.
- Earth's atmosphere softens the limb and adds a thin glowing band around it.
At lighting L0 Earth's edge is a sharp line; from L1 the atmosphere is drawn
("Earth's atmosphere").
### Earth's surface
@@ -1057,12 +1058,8 @@ Two details matter for brightness:
those values. A coastline seen up close blends correctly from sea to land.
- Blue Marble is surface reflectance with no atmosphere, and open ocean is
dark, a few percent. From orbit the ocean looks bright blue because the air
above it scatters sunlight toward the camera: Rayleigh scattering, which
grows as 1 / wavelength^4, which is also why the sky is blue. Until the
atmosphere is modeled, a stand-in adds a reflectance of that shape,
0.07 at 450 nm falling to 0.017 at 650 nm (`Appearance::earth_haze`). It
brightens and blues the whole lit disk evenly, where the real effect grows
toward the limb, where the line of sight crosses more air.
above it scatters sunlight toward the camera. The renderer adds that air
itself, next.
The texture's red, green and blue are reflectances in the same three bands a
glTF base color uses (see the target model section), folded into the camera's
@@ -1073,6 +1070,143 @@ calibrated reflectance product itself, so its values are close but not exact
reflectances. A calibrated alternative (MODIS's albedo product) is a possible
later refinement.
### Earth's atmosphere
Code: `atmosphere.rs`, `shaders/atmosphere.wgsl`, `shaders/bodies.wgsl`.
At lighting L1 Earth is seen through its atmosphere
(`Appearance::earth_atmosphere`, on by default). Air does three things to
light passing through it:
- **Molecules scatter it**, much more at short wavelengths: Rayleigh
scattering grows as 1 / wavelength^4, so blue light at 450 nm scatters 3.6
times as much as red at 620 nm. Straight up from the ground, air scatters
about 10 % of green light (an optical depth of 0.11 at 550 nm) and 21 % of
blue (0.24 at 450 nm). That is why the sky is blue and the setting Sun red.
- **Aerosols** (dust, sea salt, smoke droplets) scatter colors far more
evenly than air does, so they make haze white rather than blue, and mostly
forward, within a few tens of degrees of the light's direction. They sit
low: half of them in the lowest 0.8 km. Straight up they scatter or absorb
about as much light as the air does, an optical depth of 0.12 at 550 nm on
average over Earth, and far more over deserts and fires.
- **Ozone absorbs** light in a broad band centered near 600 nm (the Chappuis
band). From space it matters mainly at the limb, where the line of sight
crosses the ozone layer edge on, and tints the glow there.
What leaves the ground is reduced by the light scattered or absorbed out of
it on the way to the camera: the **transmittance**, between 0 and 1. And the
air along the line of sight sends sunlight it scatters toward the camera: the
**path radiance**, which adds to whatever is behind it. Both grow with the
amount of air the line of sight crosses, so the ground looks hazier and bluer
toward Earth's edge, where the line of sight is long and slanting. Past the
edge the line of sight crosses only air, which glows: the **limb**. There the
air is thin enough that the glow is in proportion to the density at the
lowest point of the line of sight, so it fades by a factor of e (2.7) for
every 8 km of height.
The ground itself is lit by the sunlight that reaches it, which the air has
also dimmed (more at a low Sun, and more in blue, so ground near sunset is lit
redder), and by **skylight**: the light the whole sky scatters down, about a
tenth of the Sun's in blue with the Sun overhead and less in red. Past the
terminator the sky still lights the ground for a while: twilight.
Light can also scatter more than once, from air to air or off the ground and
back. With the Sun high that adds a few tens of percent to the sky's
brightness.
Adding up the scattered light along every line of sight for every pixel would
be far too slow. Instead the renderer precomputes tables once, at setup, with
the method of Bruneton and Neyret (2008), in the form of Bruneton's 2017
implementation and with its atmosphere: what a point at a given height sees
looking in a given direction, with the Sun at a given angle, is a function of
four numbers (height, view angle from the zenith, Sun angle from the zenith,
and the angle between the view and the Sun), so it fits a table. Three tables:
- the scattered light (4D, stored as a 3D texture of 256 x 128 x 16 texels,
Rayleigh and aerosols separately);
- the transmittance from any point to the top of the atmosphere (256 x 64),
from which the transmittance between any two points follows;
- the skylight on the ground (64 x 16).
Each pixel that sees Earth then reads them about a dozen times: the
transmittance to the ground and the light scattered along the way (the light
scattered along the whole line of sight, less what lies beyond the ground),
and the sunlight and skylight on the ground there.
Light scattered more than once is approximated as Hillaire (2020) does:
assume the light arriving at a point after one scattering comes equally from
every direction and is the same at nearby points. Then all the higher orders
together follow from a small table per height and Sun angle, instead of being
computed order by order.
The air is Bruneton's, with more aerosols: Rayleigh scattering falling with
an 8 km scale height; aerosols with a 1.2 km scale height, an optical depth of
0.12 at 550 nm (the global annual mean of Kinne et al.'s 2013 climatology)
falling as wavelength^-1.3, 90 % of it scattering, with Bruneton's forward
phase function (asymmetry 0.8); and 300 Dobson units of ozone between 10 and
40 km. Bruneton's aerosol depth of 0.0053 is air cleaner than almost anywhere
on Earth: with it the ground looked bluer and sharper toward the limb than it
should. The top of the atmosphere is set
100 km up. Earth is the sphere of its equatorial radius, in axes stretched
along the polar axis, where its ellipsoid is that sphere (heights are off by
at most 0.3 % near the poles).
The tables are computed every 20 nm and folded into the camera's channels
with sunlight and each channel's QE, like every other spectrum
("Folding spectra into per-channel constants"). The scattered light folds
exactly. Transmittance does not: a channel whose band spans very different
transmittances gets their average weighted by sunlight, which is wrong when
the light passing through is a different color from sunlight. It matters most
for blue light along long, grazing paths. Setup takes 0.25 s on the 24-core
desktop CPU these numbers come from, and 1.1 s on four of its cores.
Tests: the tables against direct integrals, in `atmosphere.rs` (the amount of
air from the ground straight up within 1e-4 of the scale height, Rayleigh
scattered light within 3.4e-4, aerosols within 0.31 %); the shaders against
the tables, on the GPU and lavapipe (`tests/gpu/atmosphere.rs`): the ground
seen at 60 degrees from its zenith and the limb at 40 and 50 km, within
2.5e-4. The limb fades by 3.38 over those 10 km, against 3.49 for the density
alone; the air between takes the rest.
Checked against a real satellite: GOES-16's full disk of 2024-07-01 at
15:00 UTC, in its blue (470 nm) and red (640 nm) bands, over 640,000 cloud-free
ocean pixels (NOAA's cloud mask), away from the Sun's glint. Over ocean,
which is dark, most of the light the satellite measures comes from the air,
and GOES sees the sea from straight above out to its limb: the same slanting
paths through the air as a camera in low orbit looking toward the horizon.
The tables, evaluated at each pixel's angles with Blue Marble's ocean color
and channels shaped like GOES's bands, against the measured reflectance
(the median in each band of viewing angle from the zenith):
| Viewing angle | GOES blue | Model | Clean air | GOES red | Model | Clean air |
|---|---|---|---|---|---|---|
| 20-40 degrees | 0.097 | 0.090 | 0.077 | 0.029 | 0.030 | 0.023 |
| 55-65 | 0.146 | 0.145 | 0.123 | 0.044 | 0.051 | 0.037 |
| 70-75 | 0.205 | 0.191 | 0.169 | 0.076 | 0.071 | 0.056 |
| 78-81 | 0.249 | 0.239 | 0.219 | 0.093 | 0.095 | 0.078 |
| 81-84 | 0.267 | 0.266 | 0.246 | 0.104 | 0.110 | 0.092 |
The model is within 7 % in blue (low: Blue Marble's ocean is darker in blue
than real open ocean) and 15 % in red out to 84 degrees, and the haze grows
toward the limb as measured. With Bruneton's clean air ("Clean air") it is 8
to 29 % too dark. NOAA's own retrieval put the aerosols' optical depth over
these pixels at 0.13 (median), close to the 0.12 the model assumes.
On the RTX 3060 the atmosphere adds 0.8 to 1.0 ms to the bodies pass at
1920 x 1080 with camera C1's 2 x 2 supersampling, and 14 to 27 ms on lavapipe.
Limitations:
- **The camera must be above the atmosphere**, over 100 km up; below that a
frame is refused.
- **Sunlight reaching the target** through the atmosphere, and the Sun and
Moon seen through it at the limb, are not dimmed or reddened yet.
- **Clouds** are not drawn: Blue Marble is a cloud-free mosaic.
- **The Moon's shadow** in a solar eclipse dims the air along the line of
sight as it dims the ground point behind it.
- **Light from Earth onto targets** (Earthshine) includes the atmosphere
over the ground, but not the glow beyond Earth's edge.
### The Moon's surface
Code: `moon.rs`, `shaders/bodies.wgsl`; data from `scripts/fetch-moon.py`. From
@@ -1531,7 +1665,8 @@ arrives from Earth and the Moon on a surface facing any direction:
through each cell's center finds the point it meets on the body, and that
point is shaded exactly as the camera's view of the body is shaded:
reflectance from the Blue Marble image (or the stand-in spectrum), lit by
the cosine of the Sun's angle there, dark on the night side. Each texture
the cosine of the Sun's angle there, dark on the night side, and at
lighting L1 seen through the atmosphere. Each texture
read averages the patch of ground the cell covers, about 1000 square
kilometers straight down from 500 km and more toward Earth's edge, so a
cell sees the mean color of that patch rather than one point. A line
@@ -1588,8 +1723,7 @@ Limitations:
and its error is largest for strongly colored surfaces.
- **Lambertian bodies.** Earth and the Moon reflect as Lambertian surfaces
here, as they do in the camera's view, with the Moon's map scaled by its
phase curve as seen from the target. Earth has no clouds or atmosphere yet
beyond the stand-in haze.
phase curve as seen from the target. Earth has no clouds yet.
## Where the Sun and Moon appear
@@ -1762,5 +1896,6 @@ is a run of the script with `--gaia 16`, at twice the size.
- Non-Lambertian materials (BRDFs), glints, and the full Moon's edge as bright
as its center
- Clouds on Earth
- Earth's atmosphere: scattering, the bright limb
- Sunlight through Earth's atmosphere onto targets, and the Sun and Moon seen
through it; the atmosphere seen from inside it (a camera below 100 km)
- Motion blur, rolling shutter, stray light