what it does "One pattern across the screen" scenes have nothing to watch. Warping the field around a focal point was the previous attempt and it did not fix that — measured on Voronoi it raised orientation variety while layout stayed at 0.004, which is another way of saying the cells changed and the frame still had no subject. So a subject is now a form that does something TO the field, and which thing is the song's decision rather than the scene's: shift, warp, punch, morph or overlay. The same form punching a hole, bending the pattern or running it at another rate are three different videos, and across six songs the bank picks overlay, morph, shift, morph, warp and punch — so it is a real axis rather than a constant with five names. subjectSDF, subjectMask, subjectEdge and subjectWarp are in the contract, so any field scene can take a subject without knowing where the focal points are. Moiré Grid is the pilot and implements all five impacts. 0.031 to 0.037 — up a fifth, and the layout block moved from essentially nothing to 0.018, which is the first time anything has moved layout on a full-frame field. That is the number that speaks to the complaint: the frame's energy is no longer spread evenly, so there is somewhere to look. It is still under the 0.04 bar, and I have not seen it. The score says there is now a subject; whether it is worth watching is the question the gallery cannot answer. One bug worth recording: the subject helpers were placed above the ink in the preamble, and subjectEdge draws with inkStroke. GLSL has no forward declarations, so the whole preamble failed to compile and every scene using it rendered pure black — which the gallery reported as a variety of exactly 0.000 across all six blocks rather than as an error. An all-zero row means a dead shader, not a boring scene. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
112 lines
5.0 KiB
JavaScript
112 lines
5.0 KiB
JavaScript
// Geometric family: two rotating line grids interfering.
|
|
//
|
|
// Moiré is a spatial-aliasing effect by nature, so this scene is the most likely
|
|
// in the library to alias badly. Line width is held above a floor and scaled by
|
|
// u_pixelScale, which is what keeps a 720p preview and a 4K export looking the
|
|
// same rather than the preview shimmering.
|
|
|
|
export const moireGrid = {
|
|
name: 'Moiré Grid',
|
|
family: 'geometric',
|
|
kind: 'fragment',
|
|
// Personality: see look/Personality.js.
|
|
// Crisp line work: the track's surface grain would only fur the edges.
|
|
texture: 0,
|
|
consumes: ['ink'],
|
|
traits: ['camera', 'style'],
|
|
|
|
params: {
|
|
// The subject: the song's protagonist standing in the field, with the
|
|
// interference running at a different phase inside it. A window rather
|
|
// than a shape laid on top — the pattern is still the picture, it just
|
|
// has somewhere to be about, and the edge gives the eye a line to track.
|
|
subject: { type: 'float', range: [0, 1], default: 0.7, uniform: 'u_subject' },
|
|
subjSize: { type: 'float', range: [0.2, 0.75], default: 0.42, uniform: 'u_subjSize' },
|
|
density: { type: 'float', range: [6, 60], default: 22, uniform: 'u_density', bias: 'density' },
|
|
offset: { type: 'float', range: [0.0, 0.5], default: 0.08, uniform: 'u_offset' },
|
|
rotate: { type: 'float', range: [0, 0.25], default: 0.04, uniform: 'u_rotate', bias: 'motion', rate: true },
|
|
// Named u_lineWidth, not u_width: the shader contract already declares
|
|
// `uniform float u_width` for stereo width, and a colliding name is a
|
|
// redefinition error that renders the scene as a black frame.
|
|
width: { type: 'float', range: [0.06, 0.5], default: 0.2, uniform: 'u_lineWidth' , slowAxis: true },
|
|
warp: { type: 'float', range: [0, 1], default: 0.25, uniform: 'u_warp' },
|
|
palette: { type: 'palette', count: 4 },
|
|
},
|
|
|
|
reactive: {
|
|
offset: { feature: 'bandLow', amount: 0.35 },
|
|
warp: { feature: 'flux', amount: 0.2, response: 'smooth' },
|
|
},
|
|
|
|
shader: `
|
|
// Anti-aliased line grid: the smoothstep edge is widened by the screen-space
|
|
// derivative, so lines stay a consistent visual weight at any resolution.
|
|
float grid(vec2 q, float density, float width) {
|
|
vec2 g = q * density;
|
|
vec2 f = abs(fract(g) - 0.5);
|
|
float d = min(f.x, f.y);
|
|
float aa = max(fwidth(d), 0.001);
|
|
return smoothstep(width * 0.5 + aa, width * 0.5 - aa, d);
|
|
}
|
|
|
|
vec4 scene(vec2 uv, vec2 p) {
|
|
float t = u_time * u_rotate + u_seed;
|
|
p = sigFolded(sigCamera(p));
|
|
|
|
vec2 warp = vec2(fbm(p * 1.5 + t, 3), fbm(p * 1.5 - t, 3)) - 0.5;
|
|
vec2 q = p + warp * u_warp;
|
|
|
|
// Drawn in the track's hand: its line weight scales this scene's.
|
|
float weight = u_lineWidth * (0.5 + u_sigLine);
|
|
float a = grid(rot(t * 6.28318530718) * q, u_density, weight);
|
|
float b = grid(rot(-t * 6.28318530718 + u_offset * 3.14159) * (q + u_offset), u_density, weight);
|
|
|
|
// The subject does something TO the field, and which thing is the song's
|
|
// decision rather than this scene's — the same form punching a hole,
|
|
// bending the grid or running it at another rate are three different
|
|
// videos. See Identity.IMPACTS.
|
|
float d = subjectSDF(p, u_subjSize);
|
|
float inside = smoothstep(0.01, -0.01, d) * u_subject;
|
|
|
|
// WARP bends the pattern around the form instead of replacing it inside,
|
|
// so it is applied to the coordinate everything below is drawn from.
|
|
if (impactIs(1.0)) q = subjectWarp(q, u_subjSize, u_subject * 1.2);
|
|
|
|
vec2 qi = q + vec2(u_offset * 0.7, -u_offset * 0.4);
|
|
float ai = grid(rot(t * 6.28318530718 * 1.35) * qi, u_density * 1.18, weight);
|
|
float bi = grid(rot(-t * 6.28318530718 * 0.7 + u_offset * 3.14159) * (qi + u_offset), u_density * 0.86, weight);
|
|
|
|
// The interference term is the point: where both grids land, it peaks.
|
|
float interference = a * b;
|
|
float either = max(a, b);
|
|
|
|
if (impactIs(0.0)) { // shift: a different beat inside
|
|
interference = mix(interference, ai * bi, inside);
|
|
either = mix(either, max(ai, bi), inside);
|
|
} else if (impactIs(2.0)) { // punch: the field stops at the form
|
|
interference *= 1.0 - inside;
|
|
either *= 1.0 - inside;
|
|
} else if (impactIs(3.0)) { // morph: one grid survives inside
|
|
interference = mix(interference, a * 0.35, inside);
|
|
either = mix(either, a, inside);
|
|
} else if (impactIs(4.0)) { // overlay: the form reads as solid
|
|
either = max(either, inside);
|
|
interference = max(interference, inside * 0.6);
|
|
}
|
|
|
|
vec3 col = pal(0) * 0.05;
|
|
col += pal(1) * either * 0.35;
|
|
col += pal(2) * interference * (0.8 + 0.35);
|
|
|
|
// The rim, so the form has an edge to follow rather than only a change of
|
|
// texture. Drawn in the song's hand like everything else.
|
|
col = mix(col, pal(3), inkStroke(d) * u_subject);
|
|
|
|
col *= 0.6 + 0.4 * exp(-dot(p, p) * 0.3);
|
|
return vec4(inkValue(col), 1.0);
|
|
}
|
|
`,
|
|
};
|
|
|
|
export default moireGrid;
|