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:
1 parent
afc779e27e
commit
ee56f2a1c8
4 files changed
+215
-25
No files matched your search
@@ -11,6 +11,7 @@ What gets inlined:
|
||||
- vendor/suncalc.js, vendor/plane3d.js,
|
||||
vendor/clouds3d.js (plain <script> swap)
|
||||
- vendor/stars.json (window.__starsData)
|
||||
- vendor/moon.jpg (window.__moonTex, data URI)
|
||||
- every stylesheet referenced by window.THEMES
|
||||
(window.__inlineThemeCss, so
|
||||
theme switching works offline)
|
||||
@@ -24,6 +25,7 @@ Overpass cache, which is the gentlest way to hit those upstreams.
|
||||
The build fails loudly if any external script or stylesheet reference survives.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@@ -128,12 +130,18 @@ def main():
|
||||
# embedded data preamble (must run before the scene scripts)
|
||||
stars = json.loads(read('vendor/stars.json'))
|
||||
theme_css = {name: read(fname) for name, fname in themes.items()}
|
||||
moon_path = os.path.join(ROOT, 'vendor', 'moon.jpg')
|
||||
moon_uri = ''
|
||||
if os.path.exists(moon_path):
|
||||
with open(moon_path, 'rb') as f:
|
||||
moon_uri = 'data:image/jpeg;base64,' + base64.b64encode(f.read()).decode('ascii')
|
||||
embed = (
|
||||
'<script>\n'
|
||||
'window.__starsData = %s;\n'
|
||||
'window.__moonTex = "%s";\n'
|
||||
'window.__inlineThemeCss = %s;\n'
|
||||
'</script>\n'
|
||||
% (json.dumps(stars, separators=(',', ':')), json.dumps(theme_css))
|
||||
% (json.dumps(stars, separators=(',', ':')), moon_uri, json.dumps(theme_css))
|
||||
)
|
||||
# must come before config.js so the early theme loader sees the CSS
|
||||
anchor = '<script src="config.js"></script>'
|
||||
@@ -158,8 +166,8 @@ def main():
|
||||
with tempfile.NamedTemporaryFile('w', suffix='.html', delete=False) as f:
|
||||
f.write(html)
|
||||
tmp = f.name
|
||||
print('OK: self-contained (%d bytes, %d themes, %d stars)'
|
||||
% (len(html), len(themes), len(stars)))
|
||||
print('OK: self-contained (%d bytes, %d themes, %d stars, moon map %d KB)'
|
||||
% (len(html), len(themes), len(stars), len(moon_uri) // 1024))
|
||||
os.unlink(tmp)
|
||||
return
|
||||
|
||||
@@ -167,8 +175,9 @@ def main():
|
||||
f.write(html)
|
||||
|
||||
print('Wrote %s (%d bytes)' % (out, len(html)))
|
||||
print('Inlined: %s, stars.json (%d entries), themes: %s'
|
||||
% (', '.join(INLINED_SCRIPTS), len(stars), ', '.join(sorted(themes))))
|
||||
print('Inlined: %s, stars.json (%d entries), moon.jpg (%d KB), themes: %s'
|
||||
% (', '.join(INLINED_SCRIPTS), len(stars), len(moon_uri) // 1024,
|
||||
', '.join(sorted(themes))))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Vendored
+8
@@ -8,6 +8,14 @@ stars.json - star catalogue derived from the Yale Bright Star Catalogue
|
||||
(5th ed., public catalogue data). Generated for this project; see the
|
||||
real-sky notes in clouds3d.js.
|
||||
|
||||
moon.jpg - equirectangular near-side lunar map (1024x512, downscaled from
|
||||
the 2048x1024 original) used for the moon disc texture.
|
||||
Source: https://www.solarsystemscope.com/textures/ (via Wikimedia Commons,
|
||||
"Solarsystemscope texture 2k moon.jpg")
|
||||
Author: Solar System Scope
|
||||
License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
|
||||
Attribution required: Solar System Scope / solarsystemscope.com
|
||||
|
||||
plane3d.js - low-poly airliner mesh (480 triangles, flat shaded, no textures)
|
||||
Original geometry generated by tools/plane_gen.py in this repository; offered
|
||||
under CC0 1.0 like the rest of the project's own assets.
|
||||
|
||||
Vendored
+193
-20
@@ -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);
|
||||
|
||||
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 169 KiB |
Reference in new issue
Block a user