diff --git a/crates/resolved-cli/tests/cli.rs b/crates/resolved-cli/tests/cli.rs index b28aec4..9fb66d1 100644 --- a/crates/resolved-cli/tests/cli.rs +++ b/crates/resolved-cli/tests/cli.rs @@ -66,9 +66,10 @@ fn example_scene_on_gpu_and_cpu() { let [r, _, b] = gpu.get_pixel(960, 540).0; assert!(r > 500 && r > 2 * b, "{r} {b}"); // Above the limb is empty space, which reads the 64 DN bias; below it, Earth's - // dayside. + // dayside, lit and blue. assert_eq!(gpu.get_pixel(0, 0).0, [64; 3]); - assert!(gpu.get_pixel(0, 1079).0.iter().all(|&d| d > 300)); + let [r, g, b] = gpu.get_pixel(0, 1079).0; + assert!(r > 64 && g > r && b > g, "{r} {g} {b}"); let differing = gpu .pixels() .zip(cpu.pixels()) diff --git a/crates/resolved/src/bodies.rs b/crates/resolved/src/bodies.rs index 0e934b7..4c6be67 100644 --- a/crates/resolved/src/bodies.rs +++ b/crates/resolved/src/bodies.rs @@ -24,12 +24,45 @@ pub struct Appearance { } impl Default for Appearance { - /// Flat spectra: 0.3 for Earth (its Bond albedo) and 0.12 for the Moon (its - /// average visible normal albedo). + /// A blue-tilted stand-in for Earth until albedo maps arrive (M2), and a flat 0.12 + /// for the Moon (its average visible normal albedo). + /// + /// Earth's spectrum is shaped like cloud-free ocean seen through the atmosphere, + /// where scattered sunlight makes the blue end several times brighter than the + /// red: 0.32 at 350 nm, 0.21 at 500 nm, 0.07 at 650-700 nm, then back up to 0.20 + /// in the near infrared, where clouds and land reflect strongly. In the example + /// camera that is an albedo of 0.09, 0.15 and 0.26 in red, green and blue. Earth + /// gets its color through the same per-channel folding as everything else; its + /// overall brightness is lower than the real, partly cloudy Earth's (about 0.3). fn default() -> Self { + let nm = [ + 350.0, 450.0, 500.0, 550.0, 600.0, 650.0, 700.0, 750.0, 850.0, 2800.0, + ]; + let earth = [0.32, 0.29, 0.21, 0.13, 0.09, 0.07, 0.07, 0.10, 0.20, 0.20]; Self { - earth_albedo: Spectrum::constant(0.3), + earth_albedo: Spectrum::sampled(nm.map(|x| x * 1e-9).to_vec(), earth.to_vec()) + .expect("fixed samples"), moon_albedo: Spectrum::constant(0.12), } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::camera::CameraConfig; + use crate::radiometry::Radiometry; + + #[test] + fn default_earth_is_blue_in_the_example_camera() { + let r = Radiometry::new(&CameraConfig::example()); + let [red, green, blue] = r.effective_albedo(&Appearance::default().earth_albedo)[..] else { + panic!("three channels") + }; + // Measured: red 0.086, green 0.152, blue 0.258. + assert!( + blue > green && green > red && blue > 2.0 * red, + "{red} {green} {blue}" + ); + } +} diff --git a/crates/resolved/tests/sensor.rs b/crates/resolved/tests/sensor.rs index be45e2a..e0fe6eb 100644 --- a/crates/resolved/tests/sensor.rs +++ b/crates/resolved/tests/sensor.rs @@ -10,7 +10,7 @@ use resolved::gpu::targets::FrameTargets; use resolved::gpu::GpuContext; use resolved::nalgebra::{UnitQuaternion, Vector2, Vector3}; use resolved::radiometry::{Radiometry, AU}; -use resolved::{CameraConfig, Pose, Scene}; +use resolved::{CameraConfig, Pose, Scene, Spectrum}; /// Runs the sensor pass over `radiance` (example camera, rows from the top) and /// returns the DN image. @@ -143,7 +143,12 @@ fn sunlit_earth_through_the_whole_chain() { Vector3::z(), ) .unwrap(); - let body_radiometry = BodyRadiometry::new(&radiometry, &Appearance::default()).unwrap(); + // A flat gray 0.3 Earth, as in docs/optics.md's worked example. + let gray = Appearance { + earth_albedo: Spectrum::constant(0.3), + ..Appearance::default() + }; + let body_radiometry = BodyRadiometry::new(&radiometry, &gray).unwrap(); let bodies = BodiesUniforms::new( &scene, &camera, diff --git a/docs/optics.md b/docs/optics.md index 691fb0e..c4574c3 100644 --- a/docs/optics.md +++ b/docs/optics.md @@ -418,8 +418,16 @@ disk, which is the feature horizon-based navigation measures. ### Shading -Earth and the Moon are Lambertian, with one albedo each, set by -`Appearance`: 0.3 for Earth and 0.12 for the Moon, flat across wavelength. +Earth and the Moon are Lambertian, each with one reflectance spectrum over +the whole body, set by `Appearance`. The Moon's is a flat 0.12. Earth's +default is a stand-in shaped like cloud-free ocean seen from space: about +0.3 in the blue falling to 0.07 in the red, then back up in the near +infrared. Most of that blue is sunlight scattered by the atmosphere above +the ocean, the same scattering that makes the sky blue; the ocean itself is +dark. In the example camera it gives channel albedos of 0.09, 0.15 and 0.26 +(red, green, blue), so Earth renders blue. Its overall brightness is below +the real Earth's average of about 0.3, which includes bright clouds. + Each point's radiance is rho cos(i) S_k / (pi d^2), with i the angle between the surface normal and the Sun. The night side has i past 90 degrees and is black. The Sun's distance d is taken from each body's center, since the