Whirlpool (palette sweep)
Rather than hard-code one of the palettes, cycle through all five and
wipe between them with a sweep transition. Three pieces, shown via #+attr_diff
as a changeset from the palettes shader:
- The palettes page’s
palette(int i, vec2 q)dispatcher is already in place; here the index comes fromiTimerather than the@selectselector, so the palette advances on its own instead of waiting for a pick. iTime / HOLDis split byfloor/fractinto a current palette index and how far we are through its slot. The next index is justcurrent + 1(mod 5), so the cycle wraps seamlessly: the palette a slot ends on is the one the next slot begins with.fractholds still for the first part of each slot, then asmoothstepramps a moving boundary across a sweep coordinateswp, mixing the next palette in behind anfwidth-antialiased edge. Hereswpis a diagonal wipe; swap it foruv.x(vertical) or an angle for a rotating, radar-style wipe.
Diff: whirlpool-palettes-shader → whirlpool-palette-sweep-shader
--- whirlpool-palettes-shader
+++ whirlpool-palette-sweep-shader
@@ -1,37 +1,46 @@
// no function pointers in GLSL ES — palettes inline, dispatched by index
// each maps the antialiased quadrant q (q.x = x-bit, q.y = y-bit) to a color
vec3 palette(int i, vec2 q){
if(i == 0) return vec3(q, 0.0); // square
if(i == 1) return vec3(q, q.x + q.y - 2.0*q.x*q.y); // tetra
if(i == 2) return vec3(q, 1.0 - (q.x + q.y - 2.0*q.x*q.y)); // rgbw
if(i == 3) return vec3((2.0*q.x + q.y) / 3.0); // gray
return vec3(1.0 - q.x, 1.0 - q.y, 1.0); // cmy
}
-// a controls-panel dropdown picks the palette by index (default 1 = tetra)
-// @select iPalette 1 square tetra rgbw gray cmy
-uniform int iPalette;
-
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy; // <0,1>
vec2 p = uv - 0.5; // recenter so the swirl pivots on the middle
float r = length(p); // distance from the center
float a = atan(p.y, p.x); // current angle
// twist harder near the center; wrap the time term to one full turn so
// cos/sin never see a large argument (one revolution looks identical)
a += 6.0 * exp(-3.0 * r) + mod(iTime * 0.3, 6.2831853);
// rebuild swirled coordinates back in <0,1>
vec2 sw = vec2(cos(a), sin(a)) * r + 0.5;
- vec3 col = vec3(0); // start with black
vec2 aa = fwidth(sw); // pixel footprint of the swirled coords
vec2 e = smoothstep(0.5 - aa, 0.5 + aa, sw); // antialiased quadrant edge
- col = palette(iPalette, e); // palette chosen live from the Settings panel
+
+ // cycle the 5 palettes, one sweep wipe per slot
+ const float HOLD = 2.5; // seconds per palette (hold + wipe)
+ float T = iTime / HOLD;
+ int i0 = int(mod(floor(T), 5.0)); // current palette
+ int i1 = int(mod(floor(T) + 1.0, 5.0)); // next palette
+ float sweep = smoothstep(0.6, 1.0, fract(T)); // hold, then wipe the last 40%
+
+ // diagonal wipe coord — try uv.x, or atan(p.y,p.x)/6.2831853+0.5
+ float swp = (uv.x + uv.y) * 0.5;
+ float band = fwidth(swp) * 1.5; // antialias the wipe edge
+ // 1 once the sweep has passed this pixel
+ float toNew = smoothstep(swp - band, swp + band, sweep);
+
+ vec3 col = mix(palette(i0, e), palette(i1, e), toNew);
// Output to screen
fragColor = vec4(col,1.0);
}
|
|
// no function pointers in GLSL ES — palettes inline, dispatched by index
// each maps the antialiased quadrant q (q.x = x-bit, q.y = y-bit) to a color
vec3 palette(int i, vec2 q){
if(i == 0) return vec3(q, 0.0); // square
if(i == 1) return vec3(q, q.x + q.y - 2.0*q.x*q.y); // tetra
if(i == 2) return vec3(q, 1.0 - (q.x + q.y - 2.0*q.x*q.y)); // rgbw
if(i == 3) return vec3((2.0*q.x + q.y) / 3.0); // gray
return vec3(1.0 - q.x, 1.0 - q.y, 1.0); // cmy
}
// a controls-panel dropdown picks the palette by index (default 1 = tetra)
// @select iPalette 1 square tetra rgbw gray cmy
uniform int iPalette;
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy; // <0,1>
vec2 p = uv - 0.5; // recenter so the swirl pivots on the middle
float r = length(p); // distance from the center
float a = atan(p.y, p.x); // current angle
// twist harder near the center; wrap the time term to one full turn so
// cos/sin never see a large argument (one revolution looks identical)
a += 6.0 * exp(-3.0 * r) + mod(iTime * 0.3, 6.2831853);
// rebuild swirled coordinates back in <0,1>
vec2 sw = vec2(cos(a), sin(a)) * r + 0.5;
vec3 col = vec3(0); // start with black
vec2 aa = fwidth(sw); // pixel footprint of the swirled coords
vec2 e = smoothstep(0.5 - aa, 0.5 + aa, sw); // antialiased quadrant edge
col = palette(iPalette, e); // palette chosen live from the Settings panel
// Output to screen
fragColor = vec4(col,1.0);
}