Deliberately a target rather than a description of where the library sits. Of the scenes measured since the region block went in, only Droste Feedback at 0.324 is comfortably over; Plasma Bloom is 0.082, Voronoi Shatter and Apollonian Gasket 0.062, Moiré Grid 0.047. Most of the library fails this, which is the point — a bar set where the work already is measures nothing. Third value in two days, and both previous moves were instrument corrections rather than changes of mind: the motion block was reading zero for the entire library, and adding the region block made the total a mean over six blocks instead of five. Both raised every score, so 0.04 had quietly drifted down relative to the thing it was judging. Documented as something to re-read off a fresh gallery whenever the descriptor changes, rather than a number to carry forward. The marker now sits at the middle of each meter and the grades land where they should: 0.047, 0.062 and 0.082 all red, 0.140 amber, 0.324 green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
143 lines
6.0 KiB
JavaScript
143 lines
6.0 KiB
JavaScript
// Structural family: a flight through mountains. A real perspective camera
|
|
// travelling forward over a raymarched heightfield — peaks rise, pass to either
|
|
// side and occlude what is behind them, with a snowline near the top and the
|
|
// ridge silhouettes drawn in the song's hand.
|
|
//
|
|
// Ridge Terrain is the neighbour, and it is the flat version of this: parallax
|
|
// bands of 1D noise that slide sideways and can never hide each other, because
|
|
// there is no depth for them to hide in. Here the image is a 3D field sampled
|
|
// along the ray, so the motion is INTO the frame rather than across it, valleys
|
|
// open and close as the camera passes through them, and a near peak eats a far
|
|
// one. The massifs themselves are the song's protagonist, extruded: a hollow
|
|
// cast makes calderas, a notched one makes star-shaped ridges.
|
|
|
|
export const mountainFlight = {
|
|
name: 'Mountain Flight',
|
|
family: 'structural',
|
|
kind: 'fragment',
|
|
// Personality: see look/Personality.js. Identity: see look/Identity.js.
|
|
consumes: ['cast', 'ink', 'staging'],
|
|
traits: ['camera', 'space', 'style'],
|
|
|
|
params: {
|
|
relief: { type: 'float', range: [0.15, 1.4], default: 0.55, uniform: 'u_relief', bias: 'energy' },
|
|
rugged: { type: 'float', range: [0.15, 1.1], default: 0.45, uniform: 'u_rug', bias: 'density' },
|
|
ridges: { type: 'int', range: [2, 5], default: 4, uniform: 'u_oct', bias: 'density' },
|
|
massif: { type: 'float', range: [0.1, 0.9], default: 0.5, uniform: 'u_massif' },
|
|
span: { type: 'float', range: [4, 16], default: 8.0, uniform: 'u_span', bias: 'density' },
|
|
altitude: { type: 'float', range: [0.15, 2.2], default: 0.7, uniform: 'u_alt', slowAxis: true },
|
|
snowline: { type: 'float', range: [0.15, 1.1], default: 0.55, uniform: 'u_snow', bias: 'energy' },
|
|
speed: { type: 'float', range: [0.1, 2.5], default: 0.8, uniform: 'u_speed', bias: 'motion', rate: true },
|
|
palette: { type: 'palette', count: 6 },
|
|
},
|
|
|
|
reactive: {
|
|
relief: { feature: 'bandLow', amount: 0.22, response: 'smooth' },
|
|
snowline: { feature: 'bandHigh', amount: 0.2, response: 'smooth' },
|
|
altitude: { feature: 'bandSub', amount: 0.15, response: 'smooth' },
|
|
},
|
|
|
|
shader: `
|
|
// The massif standing in the world cell around w: the song's protagonist, used
|
|
// as a PLAN rather than a picture — its footprint is extruded into a mountain.
|
|
// Kept strictly inside its own cell so one sample of one cell is the whole
|
|
// answer, which is what makes it affordable inside the march.
|
|
float massifH(vec2 w) {
|
|
vec2 cell = floor(w / u_span);
|
|
vec2 local = w - (cell + 0.5) * u_span;
|
|
|
|
// Which node of the song's lattice this cell got, and how big it stands.
|
|
vec3 node = stageNode(floor(hash12(cell) * 7.0), 8.0);
|
|
vec2 at = clamp(node.xy, -1.0, 1.0) * u_span * 0.18;
|
|
float s = u_span * 0.2 * clamp(u_massif * node.z, 0.08, 1.1);
|
|
|
|
float d = castMain((local - at) / s) * s;
|
|
// Flanks fall away from the footprint's edge; the summit is its middle.
|
|
float m = sat(-d / max(s * 0.9, 1e-3));
|
|
return pow(m, 0.7) * u_relief * 1.7;
|
|
}
|
|
|
|
float terrainH(vec2 w) {
|
|
// The song's element size is the size of the whole landscape's vocabulary:
|
|
// a few enormous massifs, or a crowd of small ones.
|
|
vec2 q = w * u_rug / max(stageScale(), 0.25);
|
|
|
|
float sum = 0.0, norm = 0.0, amp = 1.0;
|
|
for (int i = 0; i < 5; i++) {
|
|
if (i >= u_oct) break;
|
|
// Ridged, not billowy: mountains have crests.
|
|
float n = 1.0 - abs(vnoise(q) * 2.0 - 1.0);
|
|
sum += n * n * amp;
|
|
norm += amp;
|
|
q = rot(0.63) * q * 2.03;
|
|
amp *= 0.45;
|
|
}
|
|
return (sum / max(norm, 1e-3)) * u_relief + massifH(w);
|
|
}
|
|
|
|
vec4 scene(vec2 uv, vec2 p) {
|
|
float t = u_time * u_speed + u_seed;
|
|
p = sigCamera(p);
|
|
|
|
// The camera rides a fixed height over whatever it is flying across, so a
|
|
// tall massif lifts the flight path instead of swallowing it.
|
|
vec3 ro = vec3(u_seed * 3.7, 0.0, t * 4.0);
|
|
ro.y = terrainH(ro.xz) + u_alt * (1.0 + stageScale() * 0.3);
|
|
|
|
// The track's horizon decides how far down the camera is looking.
|
|
vec3 rd = normalize(vec3(p.x, p.y - sigHorizonY() * 0.5, 1.6));
|
|
|
|
float dist = 0.4;
|
|
float hit = 0.0;
|
|
float gap = 0.0;
|
|
float grazed = 1e3; // closest the ray came, per unit distance
|
|
|
|
// A march ends on its hit or its far plane, not on a param.
|
|
// lint: fixed-cost
|
|
for (int i = 0; i < 72; i++) {
|
|
vec3 at = ro + rd * dist;
|
|
gap = at.y - terrainH(at.xz);
|
|
grazed = min(grazed, gap / max(dist, 1.0));
|
|
if (gap < 0.002 * dist) { hit = 1.0; break; }
|
|
// Cone stepping: safe near the surface, cheap out at the far end.
|
|
dist += max(gap * 0.5, 0.03 + dist * 0.02);
|
|
if (dist > 46.0) break;
|
|
}
|
|
|
|
float fog = sat(dist / 46.0);
|
|
vec3 col;
|
|
|
|
if (hit > 0.5) {
|
|
vec3 at = ro + rd * dist;
|
|
float e = 0.02 + dist * 0.006;
|
|
vec3 nrm = normalize(vec3(
|
|
terrainH(at.xz - vec2(e, 0.0)) - terrainH(at.xz + vec2(e, 0.0)),
|
|
2.0 * e,
|
|
terrainH(at.xz - vec2(0.0, e)) - terrainH(at.xz + vec2(0.0, e))));
|
|
|
|
vec3 sun = normalize(vec3(0.55, 0.42, -0.5));
|
|
float diff = sat(dot(nrm, sun));
|
|
|
|
float elev = sat(at.y / max(u_relief * 2.2, 0.2));
|
|
// Snow lies high, and only where the slope will hold it.
|
|
float snow = smoothstep(u_snow, u_snow + 0.16, elev * (0.45 + 0.75 * nrm.y));
|
|
|
|
vec3 rock = mix(pal(2), pal(3), sat(elev * 1.3));
|
|
col = mix(rock, pal(5), snow);
|
|
col *= 0.22 + 0.9 * diff;
|
|
col += pal(1) * 0.12 * sat(nrm.y); // sky bounce into the flats
|
|
} else {
|
|
col = mix(pal(1) * 0.45, pal(0) * 0.22, sat(rd.y * 2.2 + 0.15));
|
|
fog = 0.85;
|
|
// The ridgeline the sky is cut against, drawn at the song's weight.
|
|
col += pal(4) * inkStroke(grazed * 0.6) * 0.5;
|
|
}
|
|
|
|
col = sigAir(col, p, fog);
|
|
return vec4(inkValue(col), 1.0);
|
|
}
|
|
`,
|
|
};
|
|
|
|
export default mountainFlight;
|