diff --git a/changelog.d/1243-starfield-sky.md b/changelog.d/1243-starfield-sky.md index 91096628f..7d4557672 100644 --- a/changelog.d/1243-starfield-sky.md +++ b/changelog.d/1243-starfield-sky.md @@ -1,5 +1,5 @@ - Replace the blocky JPEG sky around the globe with the 9,096 stars of the - Yale Bright Star Catalogue, drawn as sharp points where each star really is - at the scene clock, over a soft Milky Way built from ESO/S. Brunier's - panorama (CC BY 4.0). The Sun and Moon stay; the new assets weigh 482 KB - (gekh, #1243). + Yale Bright Star Catalogue, drawn as sharp points at their catalogue + positions for the scene clock, over a soft Milky Way built from ESO/S. + Brunier's panorama (CC BY 4.0). The Sun and Moon stay, the anime style fades + the Milky Way out, and the new assets weigh 319 kB (gekh, #1243). diff --git a/scripts/build-milky-way.mjs b/scripts/build-milky-way.mjs index 19b2ea1e9..5e5d3d253 100644 --- a/scripts/build-milky-way.mjs +++ b/scripts/build-milky-way.mjs @@ -29,9 +29,9 @@ * axes as the cube axes — +X to the March equinox, +Z to the north * celestial pole — rotating each direction into galactic coordinates * with the IAU J2000 matrix. - * 6. Map the darkest 25 % of the sky to black and the brightest 0.01 % to - * white, encode with gamma 1/2.2 and ±0.5 LSB dither against banding; - * WebP. + * 6. Map the median sky to black, easing in with a soft knee, and the + * brightest 0.01 % to white (on luminance, keeping each pixel's hue), + * encode with gamma 1/2.2 and ±0.5 LSB dither against banding; WebP. * * Usage: * node scripts/build-milky-way.mjs @@ -46,10 +46,12 @@ export const BLUR_DEG = 0.4; const WORK_WIDTH = 4096; const OPEN_RADIUS = 3; const WEBP_QUALITY = 96; -// The darkest quarter of the sky is the black level: away from the galactic -// plane the panorama carries a mottled glow of unresolved stars that would -// read as grey fog around the globe. -const BLACK_PERCENTILE = 0.25; +// The median sky is the black level: away from the galactic plane the +// panorama carries a mottled glow of unresolved stars and dust that reads as +// grey blotches around the globe. A hard cut there draws edges of its own, so +// the level eases in over SOFT_KNEE (a fraction of the black-to-white range). +const BLACK_PERCENTILE = 0.5; +const SOFT_KNEE = 0.03; const WHITE_PERCENTILE = 0.9999; const OUT_DIR = path.join( path.dirname(fileURLToPath(import.meta.url)), @@ -289,6 +291,15 @@ function dither(index, channel) { return (h >>> 0) / 4294967296 - 0.5; } +/** + * Black level with a soft knee: 0 at and below the level, then a quadratic + * ease that joins the straight line, so faint sky fades out instead of + * ending in a contour. + */ +export function softBlack(x, knee = SOFT_KNEE) { + return x <= 0 ? 0 : (x * x) / (x + knee); +} + /** Value at `fraction` of the sorted luminance. */ function luminancePercentile(planes, fraction) { const [r, g, b] = planes; @@ -346,12 +357,14 @@ async function main() { ); const [u, v] = directionToPlateCarree(...icrfToGalactic(direction)); const index = j * FACE_SIZE + i; + const rgb = [0, 1, 2].map((c) => + samplePlane(planes[c], width, height, u, v), + ); + const luminance = (rgb[0] + rgb[1] + rgb[2]) / 3; + const level = softBlack((luminance - black) / (white - black)); for (let c = 0; c < 3; c++) { - const linear = Math.max( - (samplePlane(planes[c], width, height, u, v) - black) / - (white - black), - 0, - ); + // Scale every channel by the luminance curve to keep the hue. + const linear = luminance > 0 ? (rgb[c] / luminance) * level : 0; const encoded = Math.min(linear, 1) ** (1 / 2.2) * 255; pixels[index * 3 + c] = Math.min( 255, diff --git a/src/data/local_data/sky/README.md b/src/data/local_data/sky/README.md index e224e7bb0..175542da5 100644 --- a/src/data/local_data/sky/README.md +++ b/src/data/local_data/sky/README.md @@ -8,7 +8,7 @@ whole-globe view and show compression blocks. | File | Content | Size | | ---- | ------- | ---- | | `bright_stars.json` | 9,096 stars: J2000 RA/Dec, V magnitude, B−V | 235 KB | -| `milkyway_{px,nx,py,ny,pz,nz}.webp` | Milky Way glow, six 1024² cube faces, no stars | 247 KB | +| `milkyway_{px,nx,py,ny,pz,nz}.webp` | Milky Way glow, six 1024² cube faces, no stars | 85 KB | ## Stars — Yale Bright Star Catalogue @@ -45,7 +45,8 @@ Centre. 4096×2048, stars removed with a 7×7 morphological opening, two planets (Jupiter, by position) painted over with the median of the surrounding sky, blurred (σ = 0.4°), reprojected from galactic coordinates to ICRF cube faces, -black level set at the darkest 25 % of the sky, gamma-encoded and saved as +black level set at the median sky with a soft knee (the faint glow away +from the galactic plane fades to black), gamma-encoded and saved as WebP. Build: `node scripts/build-milky-way.mjs `. diff --git a/src/data/local_data/sky/milkyway_nx.webp b/src/data/local_data/sky/milkyway_nx.webp index d74db8e52..9c75115c5 100644 Binary files a/src/data/local_data/sky/milkyway_nx.webp and b/src/data/local_data/sky/milkyway_nx.webp differ diff --git a/src/data/local_data/sky/milkyway_ny.webp b/src/data/local_data/sky/milkyway_ny.webp index bec3fb4d3..a85f3d47e 100644 Binary files a/src/data/local_data/sky/milkyway_ny.webp and b/src/data/local_data/sky/milkyway_ny.webp differ diff --git a/src/data/local_data/sky/milkyway_nz.webp b/src/data/local_data/sky/milkyway_nz.webp index de654a4f2..154e0454f 100644 Binary files a/src/data/local_data/sky/milkyway_nz.webp and b/src/data/local_data/sky/milkyway_nz.webp differ diff --git a/src/data/local_data/sky/milkyway_px.webp b/src/data/local_data/sky/milkyway_px.webp index 3c0aead6f..2bbb49174 100644 Binary files a/src/data/local_data/sky/milkyway_px.webp and b/src/data/local_data/sky/milkyway_px.webp differ diff --git a/src/data/local_data/sky/milkyway_py.webp b/src/data/local_data/sky/milkyway_py.webp index 444093007..779353c64 100644 Binary files a/src/data/local_data/sky/milkyway_py.webp and b/src/data/local_data/sky/milkyway_py.webp differ diff --git a/src/data/local_data/sky/milkyway_pz.webp b/src/data/local_data/sky/milkyway_pz.webp index 8fecc2d52..50f0793a6 100644 Binary files a/src/data/local_data/sky/milkyway_pz.webp and b/src/data/local_data/sky/milkyway_pz.webp differ diff --git a/src/tooling/skyAssets.test.mjs b/src/tooling/skyAssets.test.mjs index 697c8f13e..5c666e87d 100644 --- a/src/tooling/skyAssets.test.mjs +++ b/src/tooling/skyAssets.test.mjs @@ -9,6 +9,7 @@ import { cubeFaceDirection, directionToPlateCarree, icrfToGalactic, + softBlack, } from '../../scripts/build-milky-way.mjs'; import { raDecToUnitVector } from '../sky/stars.js'; @@ -105,3 +106,18 @@ test('the galactic rotation puts the Galactic Centre and pole where the IAU does close(u, 0.5 + (360 - 280.47) / 360, 2e-3); close(v, 0.5 + 32.89 / 180, 2e-3); }); + +test('the black level eases in instead of cutting the faint sky into contours', () => { + assert.equal(softBlack(-0.2), 0); + assert.equal(softBlack(0), 0); + // Continuous, rising, with no step at the black level. + let previous = 0; + for (let x = 0.001; x <= 1; x += 0.001) { + const y = softBlack(x); + assert.ok(y > previous && y - previous < 0.0011, `step at ${x.toFixed(3)}`); + previous = y; + } + // Starts flat, then joins the straight line just below it. + assert.ok(softBlack(0.003) < 0.0003); + close(softBlack(1), 1 - 0.03, 1e-3); +});