mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
fix(sky): keep the Sun and Moon, settle outside the ICRF tables
- Pass the custom sky box to the Viewer instead of `skyBox: false`, which also stopped CesiumWidget from creating the Sun and the Moon. Guarded by scripts/qa-sky-sun-moon.mjs. - Outside the IAU 2006 tables (before 1975, after ~2050) the preload resolves without data; remember that span and stay on TEME instead of preloading and rendering every frame. - The anime style no longer cel-shades the far plane, where four bands turned the faint Milky Way into blotches. - Paint the planets out after the star-removing opening and with the ring median, so no brighter plateau is left; faces rebuilt. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
6549344946
commit
fb81e155b7
15 files changed
+243
-28
No files matched your search
+16
-13
@@ -15,12 +15,12 @@
|
||||
*
|
||||
* Transform (deterministic):
|
||||
* 1. Resample to WORK_WIDTH×WORK_WIDTH/2 and convert sRGB to linear light.
|
||||
* 2. Paint out the planets the 2008–2010 exposures caught (PLANETS): they
|
||||
* sit where today's sky has nothing and are far brighter than any star.
|
||||
* 3. Remove stars with a morphological opening (minimum, then maximum, over
|
||||
* 2. Remove stars with a morphological opening (minimum, then maximum, over
|
||||
* a (2·OPEN_RADIUS+1)² window): anything smaller than the window — every
|
||||
* star — drops to the surrounding sky; the renderer draws catalogue stars
|
||||
* itself, sharp and in the right place.
|
||||
* 3. Paint out the planets the 2008–2010 exposures caught (PLANETS): they
|
||||
* 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.
|
||||
@@ -234,16 +234,18 @@ function galacticVector(l, b) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a disc of the sky with the mean of the ring just outside it, per
|
||||
* channel. The blur that follows hides the seam.
|
||||
* Replace a disc of the sky with the median of the ring just outside it, per
|
||||
* channel. A median, not a mean: the planet's diffraction spikes reach far
|
||||
* past the disc and would lift a mean well above the sky. The blur that
|
||||
* follows hides the seam.
|
||||
*/
|
||||
function paintOut(planes, width, height, { l, b, radiusDeg }) {
|
||||
const centre = galacticVector(l, b);
|
||||
const inner = Math.cos((radiusDeg * Math.PI) / 180);
|
||||
const outer = Math.cos(((radiusDeg + 0.5) * Math.PI) / 180);
|
||||
const outer = Math.cos(((radiusDeg + 1) * Math.PI) / 180);
|
||||
const disc = [];
|
||||
const ring = [];
|
||||
const span = Math.ceil(((radiusDeg + 0.5) / 180) * height);
|
||||
const span = Math.ceil(((radiusDeg + 1) / 180) * height);
|
||||
const row = Math.round((0.5 - b / 180) * height);
|
||||
for (let y = Math.max(0, row - span); y < Math.min(height, row + span); y++) {
|
||||
const latitude = 90 - ((y + 0.5) * 180) / height;
|
||||
@@ -256,10 +258,9 @@ function paintOut(planes, width, height, { l, b, radiusDeg }) {
|
||||
}
|
||||
}
|
||||
for (const plane of planes) {
|
||||
let sum = 0;
|
||||
for (const index of ring) sum += plane[index];
|
||||
const mean = sum / ring.length;
|
||||
for (const index of disc) plane[index] = mean;
|
||||
const values = Float32Array.from(ring, (index) => plane[index]).sort();
|
||||
const median = values[values.length >> 1];
|
||||
for (const index of disc) plane[index] = median;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,12 +324,14 @@ async function main() {
|
||||
const { default: sharp } = await import('sharp');
|
||||
const { width, height, planes } = await decodePanorama(sharp, file);
|
||||
const sigmaPx = (BLUR_DEG / 180) * height;
|
||||
for (const planet of PLANETS) paintOut(planes, width, height, planet);
|
||||
for (const plane of planes) {
|
||||
extremumPlane(plane, width, height, OPEN_RADIUS, Math.min);
|
||||
extremumPlane(plane, width, height, OPEN_RADIUS, Math.max);
|
||||
blurPlane(plane, width, height, sigmaPx);
|
||||
}
|
||||
// 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);
|
||||
for (const plane of planes) blurPlane(plane, width, height, sigmaPx);
|
||||
const black = luminancePercentile(planes, BLACK_PERCENTILE);
|
||||
const white = luminancePercentile(planes, WHITE_PERCENTILE);
|
||||
let total = 0;
|
||||
|
||||
@@ -1067,5 +1067,6 @@
|
||||
"scripts/build-milky-way.mjs",
|
||||
"src/sky/stars.test.mjs",
|
||||
"src/sky/starrySky.test.mjs",
|
||||
"src/tooling/skyAssets.test.mjs"
|
||||
"src/tooling/skyAssets.test.mjs",
|
||||
"src/tooling/skyAssets.paintOut.test.mjs"
|
||||
]
|
||||
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* qa-sky-sun-moon — the custom sky must keep Cesium's Sun and Moon.
|
||||
*
|
||||
* CesiumWidget creates `scene.sun` and `scene.moon` only when the `skyBox`
|
||||
* option is not `false`, so replacing the sky box the wrong way silently
|
||||
* removes the Sun disc and its bloom. Points the camera at the Sun from
|
||||
* 22 000 km and fails unless both bodies exist; saves a screenshot.
|
||||
*
|
||||
* Usage: node scripts/qa-sky-sun-moon.mjs [--url http://localhost:4317] [--out qa-shots/sky-sun.png]
|
||||
*/
|
||||
import puppeteer from 'puppeteer';
|
||||
import { mkdirSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const argv = process.argv;
|
||||
const option = (name, fallback) =>
|
||||
argv.includes(name) ? argv[argv.indexOf(name) + 1] : fallback;
|
||||
const url = option('--url', 'http://localhost:4317');
|
||||
const out = option('--out', 'qa-shots/sky-sun.png');
|
||||
mkdirSync(path.dirname(out), { recursive: true });
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
headless: 'new',
|
||||
args: ['--no-sandbox', '--ignore-gpu-blocklist'],
|
||||
});
|
||||
try {
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1280, height: 800 });
|
||||
await page.evaluateOnNewDocument(() =>
|
||||
localStorage.setItem('gev:first-run-mission:v1', 'suppressed'),
|
||||
);
|
||||
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForFunction(() => window.__godsEyeView?.viewer?.scene, {
|
||||
timeout: 90_000,
|
||||
});
|
||||
await new Promise((r) => setTimeout(r, 10_000));
|
||||
const info = await page.evaluate(async () => {
|
||||
// The page's own Cesium instance, as Vite served it.
|
||||
const entry = performance
|
||||
.getEntriesByType('resource')
|
||||
.map((e) => e.name)
|
||||
.find((n) => /\.vite\/deps\/cesium\.js/.test(n));
|
||||
const C = await import(entry);
|
||||
const { viewer } = window.__godsEyeView;
|
||||
const scene = viewer.scene;
|
||||
scene.camera.cancelFlight();
|
||||
const time = viewer.clock.currentTime;
|
||||
const sun = C.Cartesian3.normalize(
|
||||
C.Matrix3.multiplyByVector(
|
||||
C.Transforms.computeTemeToPseudoFixedMatrix(time),
|
||||
C.Simon1994PlanetaryPositions.computeSunPositionInEarthInertialFrame(
|
||||
time,
|
||||
),
|
||||
new C.Cartesian3(),
|
||||
),
|
||||
new C.Cartesian3(),
|
||||
);
|
||||
// Off to the side of the Earth, looking straight at the Sun.
|
||||
const side = C.Cartesian3.normalize(
|
||||
C.Cartesian3.cross(sun, C.Cartesian3.UNIT_Z, new C.Cartesian3()),
|
||||
new C.Cartesian3(),
|
||||
);
|
||||
const destination = C.Cartesian3.add(
|
||||
C.Cartesian3.multiplyByScalar(side, 2.2e7, new C.Cartesian3()),
|
||||
C.Cartesian3.multiplyByScalar(sun, -0.6e7, new C.Cartesian3()),
|
||||
new C.Cartesian3(),
|
||||
);
|
||||
const up = C.Cartesian3.normalize(
|
||||
C.Cartesian3.cross(
|
||||
C.Cartesian3.cross(sun, C.Cartesian3.UNIT_Z, new C.Cartesian3()),
|
||||
sun,
|
||||
new C.Cartesian3(),
|
||||
),
|
||||
new C.Cartesian3(),
|
||||
);
|
||||
scene.camera.setView({ destination, orientation: { direction: sun, up } });
|
||||
scene.requestRender();
|
||||
return {
|
||||
sun: Boolean(scene.sun),
|
||||
moon: Boolean(scene.moon),
|
||||
skyBox: scene.skyBox?.constructor?.name,
|
||||
};
|
||||
});
|
||||
await new Promise((r) => setTimeout(r, 4_000));
|
||||
await page.evaluate(() => window.__godsEyeView.viewer.scene.requestRender());
|
||||
await new Promise((r) => setTimeout(r, 1_500));
|
||||
await page.screenshot({ path: out });
|
||||
console.log(JSON.stringify({ url, ...info, screenshot: out }));
|
||||
if (!info.sun || !info.moon) {
|
||||
console.error('FAIL: scene.sun and scene.moon must both exist');
|
||||
process.exitCode = 1;
|
||||
}
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
Reference in new issue
Block a user