moon: real lunar map (CC BY 4.0) with correct near-side geometry - the old normal sign lit the far side, so the disc came out black; stars are now occluded by the disc, sun and moon discs drawn at 12x physical without changing surface brightness; build.py inlines the map as a data URI

This commit is contained in:
Richard Zink committed 2026-10-11 22:46:35 +02:00
1 parent afc779e27e
commit ee56f2a1c8
4 files changed
+215 -25

No files matched your search

+193 -20
View File
@@ -97,6 +97,17 @@ var FRAG_SRC = [
'uniform vec3 uCloudAlbedo;', // theme-tinted but brightness-normalised
'uniform float uAirDensity;', // slant optical depth per world unit
'uniform float uCloudHaze;', // clouds sit above most of the haze: much weaker
'uniform sampler2D uMoonTex;', // equirectangular near-side lunar map
'uniform float uMoonTexOn;', // 0 until the map is loaded (flat disc)
'uniform float uMoonR;', // disc angular radius in radians
'uniform float uMoonFrac;', // illuminated fraction (drives the halo)
'uniform float uMoonEarth;', // earthshine on the dark side
'uniform float uMoonLimb;', // limb darkening strength
'uniform float uMoonBright;', // per-pixel luminance of the disc
'uniform float uMoonHalo;',
'uniform float uMoonFlipU;',
'uniform float uMoonFlipV;',
'uniform float uSunR;', // sun disc angular radius (radians)
// 3D hash and value noise
'float hash3(vec3 p) {',
'p = fract(p * vec3(0.1031, 0.1030, 0.0973));',
@@ -224,6 +235,11 @@ var FRAG_SRC = [
'vec3 col = mix(uBg, uSky, skyT);',
'float nightFactor = 1.0 - smoothstep(-0.15, 0.05, uSunAlt);',
'float skyMask = smoothstep(-0.20, 0.40, rd.y);',
// Stars must not sit on the moon: the disc is opaque, so every
// star term is masked out inside (a little beyond) its rim.
// 0 inside the disc, 1 outside it - the smoothstep runs along the
// angular distance, so an extra "1.0 -" would blank the whole sky.
'float moonOcc = smoothstep(uMoonR * 0.92, uMoonR * 1.35, length(rd - uMoonDir));',
// night darkening of the gradient BEFORE star emission is added
'col *= (1.0 - nightFactor * 0.55);',
// ---- real star catalog (Yale BSC5 baked into a texture) ----
@@ -243,38 +259,68 @@ var FRAG_SRC = [
// stars only once the sky is actually dark: the dusk palette
// is rust/blood, and additive white stars on a red sky read
// as red stars
'col += st * starVis * skyMask * twk * 1.45;',
'col += st * starVis * skyMask * twk * 1.45 * moonOcc;',
'}',
// ---- Sun disc ------------------------------------------------
// angular radius ~0.27 deg -> chord length ~0.0047
'if (uSunAlt > -0.03) {',
'vec3 qs = rd - uSunDir;',
'float angS = length(qs);',
'float discS = 1.0 - smoothstep(0.0040, 0.0050, angS);',
'float haloS = exp(-angS * 55.0) * 0.30 + exp(-angS * 14.0) * 0.12;',
// The disc is drawn at uSunR (CONFIG.sun.angularRadius, 12x
// physical by default) but the surface brightness stays
// exactly what it was: the halo profile is normalised to the
// disc radius, so the sun is bigger, not brighter.
'float angN = angS * (0.00465 / uSunR);',
'float discS = 1.0 - smoothstep(uSunR * 0.86, uSunR, angS);',
'float haloS = exp(-angN * 55.0) * 0.30 + exp(-angN * 14.0) * 0.12;',
'vec3 sunCol = mix(uLight, vec3(1.0, 0.92, 0.72), 0.35);',
'col += sunCol * (discS * 1.15 + haloS) * (1.0 - nightFactor * 0.85);',
'}',
// ---- Moon disc with real terminator ---------------------------
// lit side = dot(surface normal, sun dir) -> crescent/gibbous
// falls out of geometry, phase tracks __currentMoonDir.
'if (uMoonDir.y > -0.01) {',
// ---- Moon disc: lunar map + geometric terminator --------------
// The near side always faces the observer, so the map is sampled
// in a selenographic frame built from the north celestial pole:
// north up, east to the right, exactly like a printed lunar map,
// and it rotates as the moon crosses the sky. The terminator is
// pure geometry (surface normal dot sun direction), so crescent,
// gibbous and the orientation towards the sun all fall out.
'if (uMoonDir.y > -0.02) {',
'float MR = uMoonR;',
'vec3 qm = rd - uMoonDir;',
'float angM = length(qm);',
'if (angM < 0.02) {',
'float z = sqrt(max(0.0, 0.0046 * 0.0046 - angM * angM));',
'vec3 nM = normalize(qm + uMoonDir * (z + 0.00001));',
'if (angM < MR) {',
'float rr = angM / MR;',
'float z = sqrt(max(0.0, 1.0 - rr * rr));',
// outward normal of the NEAR hemisphere: the offset part
// plus -uMoonDir (towards the observer), not +uMoonDir.
// With the wrong sign the lit side was the far side and
// the disc came out black at and near full moon.
'vec3 nM = normalize(qm / MR - uMoonDir * z);',
'vec3 upTry = uPole - uMoonDir * dot(uPole, uMoonDir);',
'vec3 fb = (abs(uMoonDir.y) < 0.9) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);',
'vec3 fb2 = fb - uMoonDir * dot(fb, uMoonDir);',
'vec3 uU = (dot(upTry, upTry) > 1e-6) ? normalize(upTry) : normalize(fb2);',
'vec3 uE = cross(uU, uMoonDir);',
'float lon = atan(dot(nM, uE), dot(nM, -uMoonDir));',
'float lat = asin(clamp(dot(nM, uU), -1.0, 1.0));',
'vec2 uv = vec2(lon / 6.2831853 + 0.5, lat / 3.14159265 + 0.5);',
'uv.x = mix(uv.x, 1.0 - uv.x, uMoonFlipU);',
'uv.y = mix(uv.y, 1.0 - uv.y, uMoonFlipV);',
'vec3 alb = mix(vec3(0.62, 0.63, 0.66), texture2D(uMoonTex, uv).rgb, uMoonTexOn);',
'float litM = dot(nM, uSunDir);',
'float phase = smoothstep(-0.08, 0.12, litM);',
'float discM = 1.0 - smoothstep(0.0041, 0.0046, angM);',
'vec3 moonCol = mix(vec3(0.62, 0.64, 0.68), vec3(0.97, 0.95, 0.88), phase);',
'float haloM = exp(-angM * 60.0) * 0.10 * (0.25 + 0.75 * phase);',
'col += (moonCol * discM * phase + vec3(0.75, 0.78, 0.85) * haloM) * 1.25;',
'float phase = smoothstep(-0.05, 0.12, litM);',
'float lam = max(0.0, litM);',
'float limb = 1.0 - uMoonLimb * rr * rr * rr;',
'float shade = lam * lam * 0.85 + lam * 0.18;',
'float earth = uMoonEarth * (1.0 - phase) * (0.35 + 0.65 * (1.0 - rr));',
'float discM = 1.0 - smoothstep(MR * 0.965, MR, angM);',
'col += alb * (shade * limb + earth) * discM * uMoonBright;',
'}',
'float haloM = exp(-angM * 60.0) * 0.10 + exp(-angM * 16.0) * 0.04;',
'col += vec3(0.75, 0.78, 0.85) * haloM * uMoonHalo * (0.25 + 0.75 * uMoonFrac);',
'}',
// faint procedural fill for stars below the catalog limit
'float stars = starField(rd) * skyMask * nightFactor;',
'col += stars * uLight * 0.85;',
'col += stars * uLight * 0.85 * moonOcc;',
// ---- Horizon airlight -------------------------------------
// At and below the horizon the sky shows the same scattered
// column the terrain fades into. Without it the outer rim of
@@ -703,6 +749,17 @@ function init(canvas) {
var uSkyOn = gl.getUniformLocation(prog, 'uSkyOn');
var uStarLon = gl.getUniformLocation(prog, 'uStarLon');
var uPole = gl.getUniformLocation(prog, 'uPole');
var uMoonTex = gl.getUniformLocation(prog, 'uMoonTex');
var uMoonTexOn = gl.getUniformLocation(prog, 'uMoonTexOn');
var uMoonR = gl.getUniformLocation(prog, 'uMoonR');
var uMoonFrac = gl.getUniformLocation(prog, 'uMoonFrac');
var uMoonEarth = gl.getUniformLocation(prog, 'uMoonEarth');
var uMoonLimb = gl.getUniformLocation(prog, 'uMoonLimb');
var uMoonBright = gl.getUniformLocation(prog, 'uMoonBright');
var uMoonHalo = gl.getUniformLocation(prog, 'uMoonHalo');
var uMoonFlipU = gl.getUniformLocation(prog, 'uMoonFlipU');
var uMoonFlipV = gl.getUniformLocation(prog, 'uMoonFlipV');
var uSunR = gl.getUniformLocation(prog, 'uSunR');
var uSunColor = gl.getUniformLocation(prog, 'uSunColor');
var uSunInt = gl.getUniformLocation(prog, 'uSunInt');
var uMoonColor = gl.getUniformLocation(prog, 'uMoonColor');
@@ -801,6 +858,10 @@ function init(canvas) {
uPhysical: uPhysical, uAirColor: uAirColor, uCloudAlbedo: uCloudAlbedo,
uAirDensity: uAirDensity, uCloudHaze: uCloudHaze,
uSkyTex: uSkyTex, uSkyOn: uSkyOn, uStarLon: uStarLon, uPole: uPole,
uMoonTex: uMoonTex, uMoonTexOn: uMoonTexOn, uMoonR: uMoonR,
uMoonFrac: uMoonFrac, uMoonEarth: uMoonEarth, uMoonLimb: uMoonLimb,
uMoonBright: uMoonBright, uMoonHalo: uMoonHalo,
uMoonFlipU: uMoonFlipU, uMoonFlipV: uMoonFlipV, uSunR: uSunR,
// terrain program
terrain: {
prog: terrainProg,
@@ -918,6 +979,17 @@ var CAM_MID_Y = (FL_MIN_KM + FL_MAX_KM) / 2 / KM_PER_TILE;
var BOB_AMP = typeof ALT_BOB.amp === 'number' ? ALT_BOB.amp : 0.77;
var BOB_W = 6.2831853 / (typeof ALT_BOB.periodS === 'number' ? ALT_BOB.periodS : 90);
var BOB_TILT = typeof ALT_BOB.tilt === 'number' ? ALT_BOB.tilt : 0.30; // pitch response
// ---- Camera aim (CONFIG.camera) ---------------------------------------
// The camera always flies the configured path; 'aim' only changes where it
// looks. 'moon' turns the view to the moon - heading to its azimuth, pitch
// to its altitude - while the flight path, the fetch corridor and the
// traffic stay untouched. aimBob keeps a little altitude bob so the view
// never looks frozen, aimEase smooths the turn.
var CAM_CFG = (window.CONFIG && window.CONFIG.camera) || {};
var CAM_AIM_MOON = (CAM_CFG.aim === 'moon');
var CAM_AIM_EASE = (typeof CAM_CFG.aimEase === 'number') ? Math.max(0, Math.min(0.999, CAM_CFG.aimEase)) : 0.94;
var CAM_AIM_BOB = (typeof CAM_CFG.aimBob === 'number') ? Math.max(0, Math.min(1, CAM_CFG.aimBob)) : 0.25;
var aimHdX = 0, aimHdZ = 1, aimPitch = 0.45, aimReady = false;
// CONFIG.clouds = { top: ..., bottom: ... } overrides the defaults; the
// deck must stay below the camera and keep a minimum thickness.
var CLOUD_TOP = 0.9, CLOUD_BOT = -0.3;
@@ -1925,6 +1997,30 @@ function buildTerrainMesh(gl, state) {
var ATMO = (window.CONFIG && window.CONFIG.atmosphere) || {};
var ATMO_TURBIDITY = (typeof ATMO.turbidity === 'number') ? Math.max(0.2, ATMO.turbidity) : 1.0;
var ATMO_MOON_GAIN = (typeof ATMO.moonGain === 'number') ? Math.max(0, ATMO.moonGain) : 0.16;
// ---- Moon appearance (CONFIG.moon) ------------------------------------
// The real disc is 0.0046 rad; the scene draws it larger so texture and
// phase are readable. Scene moonlight follows the disc, but only half of
// the excess over the physical size counts (see CONFIG.moon.lightGain).
var MOON_CFG = (window.CONFIG && window.CONFIG.moon) || {};
var MOON_PHYS_R = 0.0046;
var MOON_ANG_R = (typeof MOON_CFG.angularRadius === 'number')
? Math.max(MOON_PHYS_R, MOON_CFG.angularRadius) : MOON_PHYS_R * 8;
var MOON_LIGHT_GAIN = (typeof MOON_CFG.lightGain === 'number')
? Math.max(0, MOON_CFG.lightGain) : 1 + 0.5 * (MOON_ANG_R / MOON_PHYS_R - 1);
var MOON_EARTHSHINE = (typeof MOON_CFG.earthshine === 'number') ? Math.max(0, MOON_CFG.earthshine) : 0.10;
var MOON_LIMB_DARK = (typeof MOON_CFG.limbDarkening === 'number') ? Math.max(0, Math.min(0.9, MOON_CFG.limbDarkening)) : 0.30;
var MOON_BRIGHT = (typeof MOON_CFG.brightness === 'number') ? Math.max(0, MOON_CFG.brightness) : 1.25;
var MOON_HALO = (typeof MOON_CFG.halo === 'number') ? Math.max(0, MOON_CFG.halo) : 1.0;
var MOON_FLIP_U = MOON_CFG.flipU ? 1 : 0;
var MOON_FLIP_V = MOON_CFG.flipV ? 1 : 0;
// The real sun is 0.00465 rad. Scaling the disc changes its size only -
// the scene lighting (_beam/_beamL) is untouched.
var SUN_CFG = (window.CONFIG && window.CONFIG.sun) || {};
var SUN_ANG_R = (typeof SUN_CFG.angularRadius === 'number')
? Math.max(0.00465, SUN_CFG.angularRadius) : 0.00465 * 12;
var MOON_TEX_SRC = (typeof window.__moonTex === 'string' && window.__moonTex)
? window.__moonTex
: ((typeof MOON_CFG.texture === 'string' && MOON_CFG.texture) ? MOON_CFG.texture : 'vendor/moon.jpg');
var ATMO_NIGHT_FLOOR = (typeof ATMO.nightFloor === 'number') ? Math.max(0.01, ATMO.nightFloor) : 0.05;
var ATMO_ALBEDO_GAIN = (typeof ATMO.albedoGain === 'number') ? Math.max(0.1, ATMO.albedoGain) : 1.0;
var ATMO_SKY_GAIN = (typeof ATMO.skyGain === 'number') ? Math.max(0.05, ATMO.skyGain) : 0.55;
@@ -1976,7 +2072,7 @@ function airlightColor(altRad, moonAlt, moonFrac) {
var beam = beamColor(altRad);
var kDay = ATMO_AIR_GAIN * skyAmbient(altRad) * beamLuma(altRad);
var up = Math.max(0, Math.sin(moonAlt || 0));
var mDiff = ATMO_MOON_GAIN * (moonFrac === undefined ? 0.6 : moonFrac) * up * 0.9;
var mDiff = ATMO_MOON_GAIN * MOON_LIGHT_GAIN * (moonFrac === undefined ? 0.6 : moonFrac) * up * 0.9;
var mBeam = beamColor(moonAlt || 0);
var kMoon = ATMO_AIR_GAIN * mDiff * beamLuma(moonAlt || 0);
return [RAYLEIGH[0] * (beam[0] * kDay + mBeam[0] * kMoon),
@@ -4551,9 +4647,12 @@ var SKYPTS_VERT = [
'uniform vec3 uPole;',
'uniform float uLon;', // sidereal angle
'uniform float uSizeScale;',
'uniform vec3 uMoonDir;',
'uniform float uMoonR;',
'varying vec4 vC;',
'varying float vAlt;',
'varying float vSeed;',
'varying float vMoon;',
'void main() {',
'vec3 d = aSd.xyz;',
// stable per-star seed: derived from the star's own direction, so it
@@ -4563,6 +4662,7 @@ var SKYPTS_VERT = [
'float c = cos(uLon), s = sin(uLon);',
'vec3 rp = d * c + cross(uPole, d) * s + uPole * dot(uPole, d) * (1.0 - c);',
'vAlt = rp.y;',
'vMoon = smoothstep(uMoonR * 0.92, uMoonR * 1.35, length(rp - uMoonDir));',
// finite radius (inside the far plane): w=0 would put z_ndc just
// outside [-1,1] and clip every single point away
'gl_Position = uMVP * vec4(rp * 88.0, 1.0);',
@@ -4579,6 +4679,7 @@ var SKYPTS_FRAG = [
'varying vec4 vC;',
'varying float vAlt;',
'varying float vSeed;',
'varying float vMoon;',
'void main() {',
'vec2 pc = gl_PointCoord - 0.5;',
'float d = length(pc) * 2.0;',
@@ -4590,7 +4691,7 @@ var SKYPTS_FRAG = [
'float altf = smoothstep(-0.02, 0.12, vAlt);',
// vC.w = per-star brightness (mag-based, floored so faint stars
// stay clearly visible), giving an even overall look
'float a = uNight * altf * twk * vC.w;',
'float a = uNight * altf * twk * vC.w * vMoon;',
'gl_FragColor = vec4(vC.rgb * g * a * uBright, 1.0);',
'}'
].join('\n');
@@ -4703,6 +4804,8 @@ function buildSkyTexture(stars) {
pole: gl.getUniformLocation(prog, 'uPole'),
lon: gl.getUniformLocation(prog, 'uLon'),
sizeScale: gl.getUniformLocation(prog, 'uSizeScale'),
moonDir: gl.getUniformLocation(prog, 'uMoonDir'),
moonR: gl.getUniformLocation(prog, 'uMoonR'),
night: gl.getUniformLocation(prog, 'uNight'),
time: gl.getUniformLocation(prog, 'uTime'),
bright: gl.getUniformLocation(prog, 'uBright')
@@ -4803,6 +4906,29 @@ if (window.CONFIG && window.CONFIG.clouds && window.CONFIG.clouds.density) {
}
}
var moonTexture = null, moonTexFailed = false;
function loadMoonTexture(gl) {
var img = new Image();
img.onload = function () {
moonTexture = gl.createTexture();
// flip so v = 0 is the southern edge: the map is north-up
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.bindTexture(gl.TEXTURE_2D, moonTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
dbgLog('[clouds3d] moon map ready (' + img.width + 'x' + img.height + ')');
};
img.onerror = function () {
moonTexFailed = true;
console.warn('[clouds3d] moon map failed to load - flat disc instead');
};
img.src = MOON_TEX_SRC;
}
window.clouds3dRender = function(target) {
if (!target || typeof target !== 'object') {
console.error('clouds3d: invalid target');
@@ -4890,6 +5016,22 @@ window.clouds3dRender = function(target) {
gl.bindTexture(gl.TEXTURE_2D, skyTexture || s.blackTex);
gl.uniform1i(s.uSkyTex, 4);
gl.uniform1f(s.uSkyOn, skyTexture ? 1.0 : 0.0);
// Lunar map on texture unit 6. Until it is loaded the shader falls back
// to a flat grey disc (uMoonTexOn = 0), so a missing asset never blanks
// the moon.
if (!moonTexture && !moonTexFailed) loadMoonTexture(gl);
gl.activeTexture(gl.TEXTURE6);
gl.bindTexture(gl.TEXTURE_2D, moonTexture || s.blackTex);
gl.uniform1i(s.uMoonTex, 6);
gl.uniform1f(s.uMoonTexOn, moonTexture ? 1.0 : 0.0);
gl.uniform1f(s.uMoonR, MOON_ANG_R);
gl.uniform1f(s.uMoonEarth, MOON_EARTHSHINE);
gl.uniform1f(s.uMoonLimb, MOON_LIMB_DARK);
gl.uniform1f(s.uMoonBright, MOON_BRIGHT);
gl.uniform1f(s.uMoonHalo, MOON_HALO);
gl.uniform1f(s.uMoonFlipU, MOON_FLIP_U);
gl.uniform1f(s.uMoonFlipV, MOON_FLIP_V);
gl.uniform1f(s.uSunR, SUN_ANG_R);
// texture path: RA alpha must land at world angle alpha + PI/2 - LST,
// which with u = fract(0.5 + (H + uStarLon)/2PI) means uStarLon = LST - 3PI/2
var _sidTex = starSidereal();
@@ -4943,12 +5085,13 @@ window.clouds3dRender = function(target) {
var _star = starAmbient(sunAlt);
var _mBeam = beamColor(_moonAlt), _mL = beamLuma(_moonAlt);
var _moonUp = Math.max(0, Math.sin(_moonAlt));
var _moonI = ATMO_MOON_GAIN * _moonFrac * _moonUp * _mL;
var _moonI = ATMO_MOON_GAIN * MOON_LIGHT_GAIN * _moonFrac * _moonUp * _mL;
// diffuse moonlight: the whole sky gets brighter with the phase, not
// only the side the moon happens to point at
var _moonDiff = ATMO_MOON_GAIN * _moonFrac * _moonUp * 0.9;
var _moonDiff = ATMO_MOON_GAIN * MOON_LIGHT_GAIN * _moonFrac * _moonUp * 0.9;
var _amb = skyAmbient(sunAlt) + _moonDiff;
var _air = airlightColor(sunAlt, _moonAlt, _moonFrac);
gl.uniform1f(s.uMoonFrac, _moonFrac);
// Theme tint on top of the physics (see themeLighting above). The
// exposure factor ramps from gain (day) to nightGain (night) with the
// same altitude ramp the terrain uses for its night cross-fade.
@@ -5054,6 +5197,33 @@ window.clouds3dRender = function(target) {
if (camY < CAM_MIN_Y) camY = CAM_MIN_Y;
else if (camY > CAM_MAX_Y) camY = CAM_MAX_Y;
var pitch = BOB_ON ? 0.45 - BOB_TILT * Math.cos(bob) : 0.45;
// ---- aim override: look at the moon, fly the same path -------------
// __currentMoonDir is a unit vector in the same world frame the view
// matrix uses, so the heading is just its horizontal part and the pitch
// is its altitude (negative pitch = above the horizon). Below the
// horizon the view holds the moon's azimuth at the horizon line instead
// of snapping back to the flight heading.
if (CAM_AIM_MOON) {
var _md = window.__currentMoonDir;
if (_md) {
var _hlen = Math.hypot(_md[0], _md[2]);
if (_hlen > 1e-4) {
var _alt = Math.asin(Math.max(-1, Math.min(1, _md[1])));
if (_alt < 0.02) _alt = 0.02;
else if (_alt > 1.40) _alt = 1.40;
var _tx = _md[0] / _hlen, _tz = _md[2] / _hlen, _tp = -_alt;
if (!aimReady) { aimHdX = _tx; aimHdZ = _tz; aimPitch = _tp; aimReady = true; }
var _k = 1 - CAM_AIM_EASE;
aimHdX += (_tx - aimHdX) * _k;
aimHdZ += (_tz - aimHdZ) * _k;
aimPitch += (_tp - aimPitch) * _k;
var _al = Math.hypot(aimHdX, aimHdZ) || 1;
hdX = aimHdX / _al;
hdZ = aimHdZ / _al;
pitch = aimPitch - (BOB_ON ? BOB_TILT * Math.cos(bob) * CAM_AIM_BOB : 0);
}
}
}
var aspect = canvas.width / canvas.height;
// The original cloud shader used a non-standard (wider) vertical fov
// so the top of the screen looked above the horizon (rd.y ~ 0.2 at
@@ -5139,6 +5309,9 @@ window.clouds3dRender = function(target) {
? -((window.__currentDayProgress || 0) * 6.2831853)
: 1.5707963267948966 - _sidPts);
gl.uniform1f(skyPts.u.sizeScale, canvas.height / 900);
var _mpt = window.__currentMoonDir || [0, 1, 0];
gl.uniform3f(skyPts.u.moonDir, _mpt[0], _mpt[1], _mpt[2]);
gl.uniform1f(skyPts.u.moonR, MOON_ANG_R);
gl.uniform1f(skyPts.u.night, starNight);
gl.uniform1f(skyPts.u.time, now);
gl.uniform1f(skyPts.u.bright, STAR_BRIGHT);