The scene sky is one fixed dusk for every theme, so a translucent card put
Midnight's link at 4.12/3.37 and Light's and Silver's at 2.49 (Libby). The card
is now the theme's bubble colour with no transparency and no backdrop-filter,
so text and link contrast no longer depend on the scene or a blur. Themes
without --sky-bubble (Light, Silver, Dark, Butter) get link #99c7ff on the card
(Dark and Butter already used that hue); Midnight and Noonday keep their own.
WCAG 2.x ratios, computed, text / Register link on the card:
Light, Silver, Dark, Butter #b7c7d9 / #99c7ff on #0d1a26 10.21 / 10.03
Midnight Sky #aeccff / #609385 on #0a090e 12.17 / 5.68
Noonday Sky #342d25 / #2a4f27 on #b9b6af 6.70 / 4.62
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Libby's read: at 54% fill Noonday text was 2.99:1 and the Register link 2.06:1
over shaded terrain. Dark text on a light bubble fails when a dark ground shows
through, so Noonday's card now takes the bubble colour with no transparency.
Midnight is unchanged (54%).
WCAG 2.x ratios, computed (relative luminance, sRGB):
Noonday, bubble #b9b6af, text #342d25, link #2a4f27, card over black / white:
54% fill: text 2.23 / 9.51, link 1.54 / 6.55 (fails over dark)
80% fill: text 4.36 / 7.86, link 3.00 / 5.42 (fails over dark)
90% fill: text 5.44 / 7.24, link 3.75 / 4.99 (link fails over dark)
100% fill: text 6.70, link 4.62, independent of backdrop and blur
Midnight, bubble #0a090e, text #aeccff, link #609385, 54% fill:
over black text 12.49, link 5.83; over white text 2.62, link 1.22
(white is not in the night scene; 4.5:1 over the horizon glow is an
estimate, not measured)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
One conflict, index.html's manifest link: this branch had %BASE_URL%manifest.json (4afd37e7), dev has ./manifest.json
(45aad35e). Both make the link base-relative for static previews; dev's form is kept, matching the file's other
relative links.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Phone shots of 'terrain · low' showed hard-edged facets on the near ground and stepped crests. Cause: the grid
(192 quads low, 256 full, 7 m wide at the nearest ground) was coarser than the 2.9 m height texels the fragment
shader reads its normals from, so gullies the polygons could not carry showed as hard shading edges. Measured by
rendering the low tier at 192..384 quads against a 768-quad reference: mean abs error 1.7 / 1.1 / 0.7 / 0.5 on
the day frame at 192 / 256 / 320 / 384; the warp's shape barely moved it.
- GRID: still 192, low 320, full 384; a test holds every tier to a quad of at most two texels at the nearest ground
the camera sees, and the tiers ordered by cost. The low triangle ceiling goes from 80k to 210k (205k used),
unmeasured on a phone.
- cavity: the shader read it from the nearest texel (blocks of one texel); it is now bilinear (hBil) at a continuous
position, with a CPU twin (bilinearHeight, curvatureAt, cavityAt, CAVITY) and a test that shading is continuous
across texel borders where the nearest-texel read jumps.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Haze was the Hillaire volume alone, ~40% opaque by 2 km and without the night glow
the sky adds, so the far ground sat well off the sky at the horizon (night: sky
73,79,99 vs far ground 20,24,31). Past SKY_FADE the surface is now skyColour flat
along the horizon in its ray's azimuth, in the CPU twin and the GLSL alike.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Replaces the exp(-(k d)^1.6) haze with the camera volume of Hillaire, EGSR 2020 section 5.4 (and sebh/UnrealEngineSkyAtmosphere
RenderCameraVolumePS): slices on the squared distribution, in-scatter integrated with each step's extinction analytic, alpha the
grey opacity 1 - mean(T). Baked into baked.scatter.aerial_perspective by src/sky/scatter.js (RGBA16F 7 x 96 x 12, 86 KB), looked up
in the terrain fragment shader (two fetches). Night: the moon lights the haze through the same volume, plus a deep blue starlight floor.
Shortcuts, with units and constants, in src/sky/README.md: sun-relative indexing, 12 x 8 x 7 texels, path = scene metres x 8,
ground ignored, single scattering, no multi-scattering LUT.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- albedo wobble, grain and strata are triplanar (weights |n|^4 over yz, xz, xy), so a cliff is not the floor pattern smeared down it
- normals from bilinear height at a continuous position, stencil 1.5 texels each side
- the grid is packed toward the camera at the patch centre (warpUv), 192 low / 256 full, vertex heights bilinear: near ridge silhouettes no longer step in 23 m blocks
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Why it read hazy and flat at 390x844: the camera started with the noon sun straight behind it (no shading), the low tier
sampled a 512 height map (6 m texels), plain exp(-k d) haze greyed the near ground, albedo had no grain, and the night
ground sat at ambient 0.018 (near black).
- camera starts with the noon sun 100 degrees off axis (START_TURNS; ?az= still moves the sun)
- haze T = exp(-(k d)^1.6), k 0.0007: near ground clear, far ridges melt
- low tier bakes 1024 texels
- albedo grain (three-wave octaves, fine one fades with distance) and rock strata; shader-only cavity shading
- night: moon from the sun's mirrored position, ambient lifted by nightness
- tier label (full / low / still / sky-only) in preview builds or ?tierlabel=1
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
createTerrainScene returned null without WebGL2, leaving plain dark ground. The door now mounts
terrain/TerrainSky2D.ts on the same canvas: skyColour (the CPU twin of SKY_FRAG) painted once, the star
layer on top when dark, same ?sky= ?sun= ?az= choice. chooseSky moves from Terrain.ts to terrainSky.ts
so both scenes share one owner.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Drops the vendored terrain/skyData.ts and the computed azimuth. terrainSky.ts reads src/sky/baked.ts:
sun {elevation_deg, azimuth_deg, color_linear}, ambient_linear, horizon_hex. terrainScatter.ts decodes
baked.scatter and uploads sky_view as an RGB16F 3D texture; SKY_FRAG and GRID_FRAG sample it (README
sampler), the sky for the view ray, the haze for the horizon in the fragment's azimuth.
- Sky: 1 - exp(-exposure * radiance) + night glow (midnight horizon colour, off as the sun nears the
horizon) + sun disc; +-0.5/255 dither on the 8-bit output.
- Stars fade by sun elevation (starGain: 1 below -12, 0 above -6) instead of a dark-sky gate; the
field is placed against the midnight gradient.
- ?sun=<deg>&az=<deg> (or the sun option) lights between the two bakes for dusk (skyAtSun).
- Changed numbers, noted in scene/README.md: haze colour is the scattered sky (was the gradient's
horizon stop), ambient is ambient_linear as baked (was gradient zenith x 0.25 x night lift up to 4),
HAZE_K unchanged at 0.0004.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Terrain overlay draws the ridge field at 2.4x alpha (capped at 1) and 1.3x
radius; the dark-sky gate is unchanged.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
scatter.js ray-marches a single-scattering Rayleigh+Mie atmosphere whose vertical optical depth is
sun.js's TAU, and bakes two tables into baked.scatter: transmittance to space by view elevation
(32x1) and sky radiance by azimuth from the sun x view elevation x sun elevation (7x32x12), RGB
half floats, little-endian base64, value x 64. Layout, exact decode and a GLSL sampler are in
src/sky/README.md. Tests pin layout, encoding, checksums and physical anchors (zenith transmittance
equals exp(-TAU); blue noon, red low sun toward the sun, black below the planet-shadow limit).
The per-sky sun {elevation_deg, azimuth_deg, color_linear}, ambient_linear and horizon_hex were
already baked in 7216456a; sun.js now exports TAU.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reuses ridge/stars.ts (placement, tint, rolling 32-star twinkle) projected with the terrain camera
onto an overlay cut to the sky; stops with the terrain loop.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Bands moved so snow caps only the peaks and soil/scree/rock read; haze
0.0007 -> 0.0004 so the albedo is not washed out; ambient x4 at night so a
moonlit face is readable on a phone.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Rock on steep faces, soil to scree to snow by height, snow only on gentle
faces; no texture reads. Pure albedoAt()/albedoPalette() mirror the grid
fragment shader; the palette is read from the baked sky (snow line and tint
by sun elevation: cool haze at night, warm sun by day).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The noise is evaluated at vec3(xz * f, c), so .z is the slope along the held
constant axis. Confirmed numerically (harness/scene/gradAxis.cjs): analytic
.xy matches central differences for fbm and phacelle, .z does not. Only the
ray-march scene's normals were affected; the grid tier already used .xy.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Previews are static dirs with no SPA fallback; a reload or shared link on
/login/matrix.org was an nginx 404. preview-config now enables the hash
router, and index.html's manifest link follows the base path.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Preview on Starfire showed one flat normal per height-map texel (blocky ground)
and a hard step in the sky gradient at the geometric horizon. Normals now blend
the four surrounding texels; the sky's elevation mapping eases through the
horizon; haze k 0.0012 -> 0.0007 so relief reads.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
style 'terrain' (WebGL2, tier-gated, dynamic import): height map baked once on the
GPU into RGBA8 (promoted from the terrain-tier-spike), a displaced grid, the baked
gradient as sky, baked sun/ambient/haze. Sky from option, ?sky=, or the stars'
night notion. Cadence guard, pauses, context loss and compile failure follow the
ridge scene. preview-config.js takes an optional style argument.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Vendors the small set the terrain needs from Void's baked sky (PR #22, 7b4aa2f):
sun elevation, the 7-stop gradient, sun.js light().direct, plus a sun azimuth
computed from the same series (not in the bake). Lambert from the sun, ambient
from the gradient zenith, exp(-k d) haze toward the gradient's horizon stop;
a sun below the horizon lights nothing. Pure functions, no GL.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Every star is seeded, placed toward the zenith, faded by the local sky
luma, and baked once into the sky canvas. A night sky is one whose
zenith luma is below 110; the ridge scene has no theme flag, so the
dusk default qualifies faintly and a day sky gets no stars and no
overlay.
32 slots twinkle at a time on a small overlay canvas under the ridges:
each slot runs back-to-back one-cycle twinkles (own 2-7 s period and
offset) of one star from its own partition, shuffled per epoch, never
repeating across the seam. Gain is a pure function of (slot, t), 0.35x
to 1.15x, exactly 1 at start and end. Dimming paints the sky's own
colour over the baked star; brightening adds a star. The pass is
clear + at most 32 arcs at 20 fps, stopped under reduced motion (live),
while hidden, in tier 'still', and after the slow-frame fallback.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The blur flattens the sky and ridges behind the text, so the fill can be
half as opaque and let the scene show through. Text over the unblurred
horizon glow is 4.15:1 (4.5 needs 54%); measured over the real blurred
backdrop at 390x844 it is >= 4.94:1.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>