diff --git a/changelog.d/1243-starfield-sky.md b/changelog.d/1243-starfield-sky.md index 7d4557672..d3ecbaf95 100644 --- a/changelog.d/1243-starfield-sky.md +++ b/changelog.d/1243-starfield-sky.md @@ -2,4 +2,4 @@ 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). + the Milky Way out, and the new assets weigh 296 kB (gekh, #1243). diff --git a/scripts/build-milky-way.mjs b/scripts/build-milky-way.mjs index 5e5d3d253..e0703f882 100644 --- a/scripts/build-milky-way.mjs +++ b/scripts/build-milky-way.mjs @@ -23,7 +23,8 @@ * sit where today's sky has nothing and are far brighter than any star. * 4. Blur with a three-pass box approximation of a Gaussian, σ = BLUR_DEG on * the sky: rows wrap in longitude and widen by 1/cos(latitude) so the - * blur stays round towards the poles. + * blur stays round towards the poles. Faint glow blends to a second, + * FAINT_BLUR_DEG blur, so it carries no clumps of unresolved stars. * 5. Resample into six FACE_SIZE² cube faces in the standard WebGL face * orientation (the renderer uploads them without flipY), with the ICRF * axes as the cube axes — +X to the March equinox, +Z to the north @@ -43,6 +44,12 @@ import { fileURLToPath } from 'node:url'; export const FACE_SIZE = 1024; export const BLUR_DEG = 0.4; +// Faint glow takes a much wider blur: there the panorama is grain of +// unresolved stars, which a 0.4° blur leaves as clumps. Bright parts keep the +// fine blur and their dust lanes; FAINT_RANGE (fraction of black-to-white) +// is where the two blend. +export const FAINT_BLUR_DEG = 2; +const FAINT_RANGE = [0.005, 0.08]; const WORK_WIDTH = 4096; const OPEN_RADIUS = 3; const WEBP_QUALITY = 96; @@ -300,6 +307,26 @@ export function softBlack(x, knee = SOFT_KNEE) { return x <= 0 ? 0 : (x * x) / (x + knee); } +export function smoothstep(edge0, edge1, x) { + const t = Math.min(Math.max((x - edge0) / (edge1 - edge0), 0), 1); + return t * t * (3 - 2 * t); +} + +/** + * Per pixel, keep `fine` where `weight(luminance of coarse)` is 1 and take + * `coarse` where it is 0, blending in between. The weight follows the coarse + * copy, so it is smooth itself and draws no seams along dark lanes. Writes + * into `fine`. + */ +function blendByBrightness(fine, coarse, weight) { + const [r] = fine; + for (let i = 0; i < r.length; i++) { + const w = weight((coarse[0][i] + coarse[1][i] + coarse[2][i]) / 3); + for (let c = 0; c < 3; c++) + fine[c][i] = coarse[c][i] + (fine[c][i] - coarse[c][i]) * w; + } +} + /** Value at `fraction` of the sorted luminance. */ function luminancePercentile(planes, fraction) { const [r, g, b] = planes; @@ -342,9 +369,19 @@ async function main() { // After the opening: it sinks the starry sky to the dark gaps between // stars, and a fill taken before it would stay brighter than the sky. for (const planet of PLANETS) paintOut(planes, width, height, planet); + const wide = planes.map((plane) => Float32Array.from(plane)); for (const plane of planes) blurPlane(plane, width, height, sigmaPx); + for (const plane of wide) + blurPlane(plane, width, height, (FAINT_BLUR_DEG / 180) * height); const black = luminancePercentile(planes, BLACK_PERCENTILE); const white = luminancePercentile(planes, WHITE_PERCENTILE); + blendByBrightness(planes, wide, (luminance) => + smoothstep( + FAINT_RANGE[0], + FAINT_RANGE[1], + (luminance - black) / (white - black), + ), + ); let total = 0; for (const face of CUBE_FACES) { const pixels = Buffer.alloc(FACE_SIZE * FACE_SIZE * 3); diff --git a/src/data/local_data/sky/README.md b/src/data/local_data/sky/README.md index 175542da5..e20b1fb77 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 | 85 KB | +| `milkyway_{px,nx,py,ny,pz,nz}.webp` | Milky Way glow, six 1024² cube faces, no stars | 62 KB | ## Stars — Yale Bright Star Catalogue @@ -44,7 +44,8 @@ Centre. **Changes made** (CC BY 4.0 asks for them to be stated): resampled to 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, +blurred (σ = 0.4°, blending to σ = 2° in the faint glow so unresolved +stars leave no clumps), reprojected from galactic coordinates to ICRF cube faces, 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: diff --git a/src/data/local_data/sky/milkyway_nx.webp b/src/data/local_data/sky/milkyway_nx.webp index 9c75115c5..fb14fb996 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 a85f3d47e..fb0a48760 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 154e0454f..002e483dc 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 2bbb49174..e14e3aded 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 779353c64..194331c55 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 50f0793a6..06624bf21 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 5c666e87d..75e0ad063 100644 --- a/src/tooling/skyAssets.test.mjs +++ b/src/tooling/skyAssets.test.mjs @@ -9,6 +9,7 @@ import { cubeFaceDirection, directionToPlateCarree, icrfToGalactic, + smoothstep, softBlack, } from '../../scripts/build-milky-way.mjs'; import { raDecToUnitVector } from '../sky/stars.js'; @@ -121,3 +122,11 @@ test('the black level eases in instead of cutting the faint sky into contours', assert.ok(softBlack(0.003) < 0.0003); close(softBlack(1), 1 - 0.03, 1e-3); }); + +test('the faint-glow blend weight eases between its edges', () => { + assert.equal(smoothstep(0.1, 0.2, 0), 0); + assert.equal(smoothstep(0.1, 0.2, 0.1), 0); + close(smoothstep(0.1, 0.2, 0.15), 0.5, 1e-9); + assert.equal(smoothstep(0.1, 0.2, 0.2), 1); + assert.equal(smoothstep(0.1, 0.2, 9), 1); +});