From ee821dd724c64f5ff4afaafe72381369a787335c Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Wed, 7 Oct 2026 19:02:06 +0200 Subject: [PATCH] Airliner cockpit view + night stars - Cruise altitude lowered to y=2.0 (just above cloud top at y=1.8) - Look direction nearly level (rd.y ~ -0.05 at horizon line) - Stars rendered as 3D hash-grid sphere, only above horizon and only when uDayProgress < 1 (i.e. at night). Subtle sin twinkle keeps them from looking like static dither. --- vendor/clouds3d.js | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js index 8029513..6232c38 100644 --- a/vendor/clouds3d.js +++ b/vendor/clouds3d.js @@ -101,20 +101,36 @@ var FRAG_SRC = [ 'void main() {', 'vec2 uv = (gl_FragCoord.xy / uResolution.xy) * 2.0 - 1.0;', 'uv.x *= uResolution.x / uResolution.y;', - // Camera path: slow drift in X, mostly above the cloud deck. - // pow(...,n) shapes the sin into short dips, so the view spends - // most of its time at cruising altitude and only briefly skims - // the top of the cloud layer. + // Camera path: slow drift in X, mostly cruising just above the + // cloud deck like an airliner cockpit. pow(...,n) shapes the + // sin into short dips so we occasionally skim the cloud top. 'float drift = uTime * uSpeed * 0.45;', 'float diveT = pow(0.5 + 0.5 * sin(uTime * 0.10), 5.0);', - 'float camY = mix(2.6, 0.9, diveT);', + // Cloud layer tops around y≈1.8. Cruise just above it. + 'float camY = mix(2.0, 0.9, diveT);', 'vec3 ro = vec3(drift, camY, drift * 0.7);', - // Ray direction: tilt down a bit more so the cloud deck fills - // the lower frame when we're above it. - 'vec3 rd = normalize(vec3(uv.x * 0.55, -0.35 + uv.y * 0.30, 1.0));', + // Ray direction: nearly level, like looking out a cockpit + // window. Horizon sits around the middle of the frame. + 'vec3 rd = normalize(vec3(uv.x * 0.55, -0.05 + uv.y * 0.35, 1.0));', // Sky gradient based on ray Y (background). 'float skyT = clamp(rd.y * 0.7 + 0.5, 0.0, 1.0);', 'vec3 col = mix(uBg, uSky, skyT);', + // Stars: project ray onto a far sphere, hash-grid for star + // positions. Only visible above the horizon and at night, + // with a small twinkle so they don't look like a static + // dither pattern. + 'float starField(vec3 r) {', + 'vec3 sp = r * 220.0;', + 'vec3 sf = floor(sp);', + 'vec3 sfp = fract(sp) - 0.5;', + 'float h = hash3(sf);', + 'float starD = length(sfp);', + 'float mask = step(0.985, h) * smoothstep(0.35, 0.0, starD);', + 'float twinkle = 0.55 + 0.45 * sin(uTime * 3.0 + h * 47.0);', + 'return mask * twinkle;', + '}', + 'float stars = starField(rd) * smoothstep(0.05, 0.35, rd.y) * (1.0 - uDayProgress);', + 'col = mix(col, uLight, clamp(stars, 0.0, 1.0) * 0.9);', // Density threshold: weather 'sunny' -> high (sparse clouds), // 'stormy' -> low (overcast deck). 'float baseThresh = mix(0.72, 0.40, uDensity);',