refactor(sky): cut what the sky does not need

- qa-anime-sky keeps only the Milky Way case; the satellites and keyless
  cases checked an anime shader this branch no longer changes.
- Drop qa-sky-sun-moon.mjs: the viewer-options unit test covers it.
- VisualEffects sets the sky's visibility directly: the viewer always
  carries a StarrySky and never outlives the effects.
- Drop the unused star intensity knob, the malformed-row tolerance for
  our own pack, the test-only Transforms parameter and a private-name test.
- DATA_SOURCES points to sky/README.md instead of repeating it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-20 13:45:36 +04:00
1 parent d94cc35c81
commit b00c7c12c8
10 files changed
+131 -515

No files matched your search

+11 -33
View File
@@ -4,30 +4,16 @@ import { governorRequestRender } from '../renderGovernor.js';
import { buildStarVertices, STAR_VERTEX_FLOATS } from './stars.js';
/**
* Space background: catalogue stars drawn as point sprites over a soft Milky
* Way cube map, replacing Cesium's default Tycho-2 sky box (six 1024² JPEGs
* stretched 3–6× on screen, which read as compression blocks around the
* globe).
*
* Narrow, version-tested engine adapter. It takes the `Scene#skyBox` slot:
* Cesium calls `update(frameState, useHdr)` once per frame and executes the
* returned command first in the environment pass, before the atmosphere, sun,
* moon and every globe or tile command — so the sky stays behind everything
* without depth testing. Only `execute()` and `derivedCommands` of that
* command are read. An engine upgrade must revalidate this adapter.
*
* Both layers share one ICRF→Earth-fixed rotation (IAU 2006 precession,
* nutation and Earth rotation at the scene clock), so stars and the Milky
* Way agree with each other and with the real sky over the globe. Until
* Cesium's ICRF tables load, the TEME approximation the default sky box uses
* fills in; the arrival renders one frame through the idle governor.
* Space background in the `Scene#skyBox` slot: catalogue stars as point
* sprites over a soft Milky Way cube map, both turned by one ICRF→fixed
* rotation at the scene clock (TEME until Cesium's tables load). Scene runs
* the command from `update()` first in the environment pass, so the sky
* stays behind everything without depth. Revalidate on engine upgrades.
*/
export const STARRY_SKY_CESIUM_VERSION = '1.138.0';
/** Opacity of the Milky Way glow over black; the stars carry the sky. */
export const MILKY_WAY_INTENSITY = 0.4;
/** Overall star brightness multiplier. */
export const STAR_INTENSITY = 1;
/** Clock change, in seconds, below which the sky rotation is reused. */
export const SKY_ROTATION_EPSILON_SECONDS = 1;
@@ -118,14 +104,13 @@ void main()
`;
const STAR_FS = `
uniform float u_intensity;
in vec4 v_color;
in float v_sigma;
in float v_radius;
void main()
{
vec2 offset = (gl_PointCoord - 0.5) * (2.0 * v_radius);
float alpha = v_color.a * u_intensity *
float alpha = v_color.a *
exp(-0.5 * dot(offset, offset) / (v_sigma * v_sigma));
if (alpha < 1.0 / 512.0) discard;
out_FragColor = vec4(czm_gammaCorrect(v_color.rgb) * alpha, alpha);
@@ -133,15 +118,12 @@ void main()
`;
/** Pick the ICRF→fixed rotation for `time`, or TEME while tables load. */
export function computeSkyRotation(
time,
result,
transforms = Cesium.Transforms,
) {
const icrf = transforms.computeIcrfToFixedMatrix(time, result);
export function computeSkyRotation(time, result) {
const { Transforms } = Cesium;
const icrf = Transforms.computeIcrfToFixedMatrix(time, result);
if (icrf) return { matrix: icrf, precise: true };
return {
matrix: transforms.computeTemeToPseudoFixedMatrix(time, result),
matrix: Transforms.computeTemeToPseudoFixedMatrix(time, result),
precise: false,
};
}
@@ -169,7 +151,6 @@ export class StarrySky {
this.milkyWayIntensity = MILKY_WAY_INTENSITY;
/** 0..1 multiplier a style sets to hide the glow (see visualEffects.js). */
this.milkyWayVisibility = 1;
this.starIntensity = STAR_INTENSITY;
this._requestRender = requestRender;
this._rotation = Cesium.Matrix3.clone(Cesium.Matrix3.IDENTITY);
this._rotationTime = undefined;
@@ -197,10 +178,7 @@ export class StarrySky {
u_milkyWay: () => this._milkyWayCubeMap,
u_intensity: () => this.milkyWayIntensity * this.milkyWayVisibility,
};
this._starUniforms = {
...uniforms,
u_intensity: () => this.starIntensity,
};
this._starUniforms = uniforms;
const commands = [];
this._commands = commands;
// The environment pass reads `derivedCommands` (log depth, HDR, picking)
+19 -21
View File
@@ -25,19 +25,25 @@ test('the ICRF rotation is preferred and TEME fills in while tables load', () =>
const time = Cesium.JulianDate.fromIso8601('2026-10-19T00:00:00Z');
const icrf = Cesium.Matrix3.fromRotationZ(0.3);
const teme = Cesium.Matrix3.fromRotationZ(0.2);
const precise = computeSkyRotation(time, new Cesium.Matrix3(), {
computeIcrfToFixedMatrix: (_, result) => Cesium.Matrix3.clone(icrf, result),
computeTemeToPseudoFixedMatrix: () => assert.fail('TEME not needed'),
});
assert.equal(precise.precise, true);
assert.ok(Cesium.Matrix3.equals(precise.matrix, icrf));
const approximate = computeSkyRotation(time, new Cesium.Matrix3(), {
computeIcrfToFixedMatrix: () => undefined,
computeTemeToPseudoFixedMatrix: (_, result) =>
Cesium.Matrix3.clone(teme, result),
});
assert.equal(approximate.precise, false);
assert.ok(Cesium.Matrix3.equals(approximate.matrix, teme));
const { Transforms } = Cesium;
const original = { ...Transforms };
try {
Transforms.computeIcrfToFixedMatrix = (_, result) =>
Cesium.Matrix3.clone(icrf, result);
Transforms.computeTemeToPseudoFixedMatrix = () =>
assert.fail('TEME not needed');
const precise = computeSkyRotation(time, new Cesium.Matrix3());
assert.equal(precise.precise, true);
assert.ok(Cesium.Matrix3.equals(precise.matrix, icrf));
Transforms.computeIcrfToFixedMatrix = () => undefined;
Transforms.computeTemeToPseudoFixedMatrix = (_, result) =>
Cesium.Matrix3.clone(teme, result);
const approximate = computeSkyRotation(time, new Cesium.Matrix3());
assert.equal(approximate.precise, false);
assert.ok(Cesium.Matrix3.equals(approximate.matrix, teme));
} finally {
Object.assign(Transforms, original);
}
});
test('the TEME fallback keeps the celestial pole on the Earth axis', () => {
@@ -127,11 +133,3 @@ test('the Milky Way glow scales with its visibility, for styles that hide it', (
assert.equal(sky._milkyWayUniforms.u_intensity(), 0);
sky.destroy();
});
test('the Milky Way stays out of the environment map that lights models', () => {
// Scene#render hands `skyBox._cubeMap` to image-based lighting, which
// expects Cesium's own TEME, flipY layout; this ICRF map would be rotated.
const sky = new StarrySky({ requestRender: () => {} });
assert.equal('_cubeMap' in sky, false);
sky.destroy();
});
+6 -27
View File
@@ -109,38 +109,17 @@ export function bvToRgb(bv) {
return rgb.map((channel) => channel / peak);
}
/**
* Decode a catalogue pack into interleaved vertex data, `STAR_VERTEX_FLOATS`
* per star. Malformed rows are skipped, never thrown on: a damaged pack must
* not take the globe down with it.
*/
export function buildStarVertices(pack) {
const stars = Array.isArray(pack?.stars) ? pack.stars : [];
const count = Math.floor(stars.length / 4);
/** Decode a catalogue pack into interleaved vertex data, `STAR_VERTEX_FLOATS` per star. */
export function buildStarVertices({ stars }) {
const count = stars.length / 4;
const vertices = new Float32Array(count * STAR_VERTEX_FLOATS);
let written = 0;
for (let i = 0; i < count; i++) {
const [raDeg, decDeg, vmag, bv] = stars.slice(i * 4, i * 4 + 4);
if (
!Number.isFinite(raDeg) ||
!Number.isFinite(decDeg) ||
!Number.isFinite(vmag)
)
continue;
const [x, y, z] = raDecToUnitVector(raDeg, decDeg);
const { sigmaPx, alpha } = starAppearance(vmag);
const [r, g, b] = bvToRgb(bv);
vertices.set(
[x, y, z, sigmaPx, r, g, b, alpha],
written * STAR_VERTEX_FLOATS,
[...raDecToUnitVector(raDeg, decDeg), sigmaPx, ...bvToRgb(bv), alpha],
i * STAR_VERTEX_FLOATS,
);
written += 1;
}
return {
count: written,
vertices:
written === count
? vertices
: vertices.slice(0, written * STAR_VERTEX_FLOATS),
};
return { count, vertices };
}
+2 -6
View File
@@ -82,9 +82,9 @@ test('B−V colours run from blue-white to orange with the peak channel at 1', (
assert.deepEqual(bvToRgb(9), bvToRgb(2));
});
test('vertex data is interleaved per star and skips malformed rows', () => {
test('vertex data is interleaved per star', () => {
const { count, vertices } = buildStarVertices({
stars: [0, 0, 1, 0.5, 'bad', 10, 2, 0, 90, 45, 3, null, 10, 20],
stars: [0, 0, 1, 0.5, 90, 45, 3, null],
});
assert.equal(count, 2);
assert.equal(vertices.length, 2 * STAR_VERTEX_FLOATS);
@@ -93,10 +93,6 @@ test('vertex data is interleaved per star and skips malformed rows', () => {
close(vertices[STAR_VERTEX_FLOATS + 2], Math.SQRT1_2, 1e-6);
close(vertices[3], starAppearance(1).sigmaPx, 1e-6);
close(vertices[7], starAppearance(1).alpha, 1e-6);
assert.deepEqual(buildStarVertices(null), {
count: 0,
vertices: new Float32Array(0),
});
});
test('the committed catalogue holds the Yale Bright Star Catalogue', () => {