fix: the sky meets the sea, and the places rail admits it scrolls
Two defects a photograph found and no gate could. **The sky seam.** A phone held upright drew a hard horizontal step across the top 10.5% of the frame — a flat duller slab above a warm gradient, meeting in one pixel. Painting the sky dome red proved the slab was the dome itself and everything below the line was sea; so this was never missing geometry, it was two halves of one join disagreeing about a colour. The sea is a flat plane, so however wide it is made the camera's own far plane cuts it at an elevation *below* the horizontal — about 12 degrees in a plan view of the state. atmosphere.ts has fogged it to exactly the horizon colour by then, which is the entire mechanism by which water dissolves into sky instead of ending in an edge. The dome at that same angle has to be the same number, and it was not: the below-horizon darkening ramped to its full 0.82 over the first 11.5 degrees. The sea arrived at the horizon colour and met a sky already a fifth darker. Measured 155,107,99 against 127,88,81 across one pixel. Holding the horizon's own colour for the first 30 degrees costs nothing the darkening was for — a floor or an ocean covers that band whenever there is one, and where there is not, matching is the whole point. Measured after: the worst single-row step anywhere in the sky falls from 15/255 to 8, and the 8 is water texture rather than a line. render/skyHorizonSeam.test.ts parses the shader text, which is a weaker instrument than a frame and the strongest one available with no GL context. It asserts the one number the picture proved wrong — how far down the dome still paints the horizon colour — and it caught a real off-by-a-hair while being written: 0.42 is sin(24.8 deg), just inside the 25 deg the sea can reach. It also guards the backtick-in-a-template-literal trap that has now broken this same shader three times. **The places rail.** Its cap is min(22rem, 42vh) and rows are variable height, so it always landed mid-row, and a row sliced through its own text reads as a button that failed to draw rather than as more list. markOverflow writes data-overflow and the stylesheet fades whichever end has list behind it. Every measurement in it is guarded, because ui/mount.ts is deliberately tested with no DOM at all: no geometry must mean no attribute, not data-overflow=NaN. A pixel of slack at each end, because fractional layout reports a fully-scrolled list a fraction short and a fade that never switches off dims a row over nothing. stylesheet.test.ts asserts the two files still speak the same four words — nothing else connects them and the failure is silent. 1,664 + 295 tests, all gates, budget 779,789 tri / 150 draws / p95 16.7 ms. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+25
-4
@@ -864,10 +864,31 @@ void main() {
|
||||
*/
|
||||
vec3 color = mix( uHorizon, uTop, pow( clamp( height, 0.0, 1.0 ), 0.42 ) );
|
||||
|
||||
// Below the horizon there is no sky. The ocean covers this in the city and
|
||||
// the ground plane covers it in a sited office, so what it has to be is
|
||||
// simply *not brighter* than the horizon it sits under.
|
||||
color = mix( color, uHorizon * 0.82, clamp( - height * 5.0, 0.0, 1.0 ) );
|
||||
/*
|
||||
* Below the horizon there is no sky, and what is painted there has one job:
|
||||
* be *not brighter* than the horizon it sits under, for the office floors and
|
||||
* ocean that normally cover it.
|
||||
*
|
||||
* The band immediately under the horizontal is the exception, and it is the
|
||||
* only part of this a board ever shows. The sea is a flat plane, so however
|
||||
* wide it is made the camera's own far plane cuts it off at an elevation some
|
||||
* degrees *below* the horizontal — around 12 degrees in a plan view of the
|
||||
* state. atmosphere.ts fogs it to exactly the horizon colour by the time it
|
||||
* gets there, which is what makes the water dissolve into sky rather than end
|
||||
* in an edge. So whatever the dome paints at that same angle is the other half
|
||||
* of that join, and it has to be the same number.
|
||||
*
|
||||
* It was not: the darkening used to ramp in over the first 11.5 degrees, so
|
||||
* the sea arrived at the horizon colour and met a sky already at 0.82 of it.
|
||||
* That is a hard step of a fifth of the brightness, drawn all the way across
|
||||
* the frame — 1.7% of a desktop window and **10.5% of a phone held upright**,
|
||||
* where the camera pitches down far enough to put the whole band in shot.
|
||||
*
|
||||
* Holding the horizon's own colour for the first 30 degrees costs nothing that
|
||||
* the darkening was for: a floor or an ocean covers that band whenever there
|
||||
* is one, and where there is not, matching is the entire point.
|
||||
*/
|
||||
color = mix( color, uHorizon * 0.82, smoothstep( 0.50, 0.98, - height ) );
|
||||
|
||||
/*
|
||||
* Two glows, one warm quarter of sky.
|
||||
|
||||
Reference in New Issue
Block a user