diff --git a/flow-state/src/scenes/shader/isometric-blocks.js b/flow-state/src/scenes/shader/isometric-blocks.js index 6597edd..d50eeed 100644 --- a/flow-state/src/scenes/shader/isometric-blocks.js +++ b/flow-state/src/scenes/shader/isometric-blocks.js @@ -27,6 +27,16 @@ export const isometricBlocks = { // enough to nothing. Displacing them per cell puts the SONG in the // arrangement rather than only in the elevation. scatter: { type: 'float', range: [0, 0.85], default: 0.3, uniform: 'u_scatter', bias: 'density' }, + // Extra disturbance AT the song's focal points, on top of the scatter + // everywhere — not instead of it. + // + // It was a trade at first, spending the scatter budget near the focus + // and calming the rest, and that measured worse than no focus at all: + // 0.049 down to 0.037. Most of the field went back onto the rigid + // lattice and took the orientation variety with it, 0.164 to 0.105. The + // focal point has to be something the field gains, not something the + // rest of it pays for. + gather: { type: 'float', range: [0, 1.6], default: 0.8, uniform: 'u_gather' }, plan: { type: 'float', range: [0.2, 0.5], default: 0.4, uniform: 'u_plan' }, tilt: { type: 'float', range: [0.35, 0.75], default: 0.55, uniform: 'u_isoTilt' }, light: { type: 'float', range: [0, 1.4], default: 0.65, uniform: 'u_light' }, @@ -88,14 +98,29 @@ vec4 scene(vec2 uv, vec2 p) { float row = rowHere - float(7 - j); vec2 cell = vec2(colX, row); - // Each cell steps off its slot by a fixed amount of its own, so the - // plan of the field is the song's rather than the lattice's. Bounded - // under half a cell: past that blocks cross each other and the - // isometric read — which is the whole point of the scene — breaks down. - vec2 offset = (hash22(cell + u_seed + 3.7) - 0.5) * u_scatter * cw * 0.9; + // Where this block would stand if nothing disturbed it, so the focus can + // be asked about the cell rather than about the pixel. + float restY = (row + stagger) * tilt * cw; + float focus = focusField(vec2((colX + 0.5) * cw - 0.5, restY)); - float groundY = (row + stagger) * tilt * cw + offset.y; // where this cell meets the ground - float h = columnHeight(cell, t) * (1.0 + offset.x * 2.0); + // Each cell steps off its slot by a fixed amount of its own, and how far + // depends on how close it is to what the song is looking at. Spread + // evenly this is noise on a grid; concentrated, the lattice stands + // except where it is disturbed, and the field acquires a subject. + // + // Bounded under half a cell: past that blocks cross each other and the + // isometric read — which is the whole point of the scene — breaks down. + float amount = u_scatter * (1.0 + focus * u_gather * 2.0); + vec2 offset = (hash22(cell + u_seed + 3.7) - 0.5) * amount * cw * 0.9; + + float groundY = restY + offset.y; // where this cell meets the ground + // Blocks rise toward the focus as well as scattering around it, so the + // disturbance is something you can see in the skyline rather than only + // in the plan — and rising changes WHERE the frame's energy is, which + // scattering alone did not. + float h = columnHeight(cell, t) + * (1.0 + offset.x * 2.0) + * (1.0 + focus * u_gather * u_focusPull * 1.3); float dy = p.y - groundY; // height up the block face // Side wall: the plan swept up from the ground line to the top face.