Back to shaders
Shader test bench
Gn Q Smear Noise.fs
runnable fragment
Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.
Code
precision mediump float;
/*{
"CATEGORIES": [
"DistortionEffect",
"Glitch"
],
"CREDIT": "Gnomalab",
"DESCRIPTION": "\"Smear caótico con ángulos cuantizados por brillo. Incluye ruido espacial/temporal y un motor de feedback inestable que genera texturas de glitch orgánico y estéticas similares al datamosh.\"",
"INPUTS": [
{
"NAME": "inputImage",
"TYPE": "image"
},
{
"DEFAULT": 0.45,
"LABEL": "Brightness Threshold",
"MAX": 1,
"MIN": 0,
"NAME": "brightThreshold",
"TYPE": "float"
},
{
"DEFAULT": 18,
"LABEL": "Smear Velocity",
"MAX": 50,
"MIN": -50,
"NAME": "velocity",
"TYPE": "float"
},
{
"DEFAULT": 0.4,
"LABEL": "Velocity Sustain",
"MAX": 0.6,
"MIN": 0,
"NAME": "velocityDecay",
"TYPE": "float"
},
{
"DEFAULT": 0.97,
"LABEL": "Colour Sustain",
"MAX": 1,
"MIN": 0,
"NAME": "colourDecay",
"TYPE": "float"
},
{
"DEFAULT": 24,
"LABEL": "Max Angular Steps",
"MAX": 64,
"MIN": 1,
"NAME": "maxAngleSteps",
"TYPE": "float"
},
{
"DEFAULT": 0.6,
"LABEL": "Temporal Noise Amount",
"MAX": 2,
"MIN": 0,
"NAME": "timeNoiseAmt",
"TYPE": "float"
},
{
"DEFAULT": 1.2,
"LABEL": "Temporal Noise Speed",
"MAX": 5,
"MIN": 0,
"NAME": "timeNoiseSpeed",
"TYPE": "float"
},
{
"DEFAULT": 0.4,
"LABEL": "Spatial Noise Amount",
"MAX": 2,
"MIN": 0,
"NAME": "spatialNoiseAmt",
"TYPE": "float"
},
{
"DEFAULT": 0.15,
"LABEL": "Feedback Instability",
"MAX": 1,
"MIN": 0,
"NAME": "feedbackChaos",
"TYPE": "float"
}
],
"ISFVSN": "2",
"PASSES": [
{
"HEIGHT": "$HEIGHT",
"PERSISTENT": true,
"TARGET": "decayBuffer",
"WIDTH": "$WIDTH"
},
{
"PERSISTENT": true,
"TARGET": "outputPass"
}
],
"VSN": "1.0.0"
}
*/
// ----------------------------------------------------
// Utils
// ----------------------------------------------------
float brightness(vec4 c)
{
return (c.r + c.g + c.b) / 3.0;
}
float hash(vec2 p)
{
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}
float timeNoise(float t)
{
return fract(sin(t * 13.37) * 43758.5453);
}
// ----------------------------------------------------
// Main
// ----------------------------------------------------
void main()
{
vec4 inputPixel = IMG_THIS_PIXEL(inputImage);
float inBright = brightness(inputPixel);
float decayBright = brightness(IMG_THIS_PIXEL(decayBuffer));
// ---------------- PASS 0 : velocity memory ----------------
if (PASSINDEX == 0)
{
float v = max(inBright, decayBright) * velocityDecay;
gl_FragColor = vec4(vec3(v), 1.0);
}
// ---------------- PASS 1 : output ----------------
else
{
if (inBright > brightThreshold)
{
gl_FragColor = IMG_THIS_NORM_PIXEL(inputImage);
}
else
{
// --- base chaotic angle ---
float angle = (inBright + decayBright) * 6.28318;
// --- temporal noise ---
angle += timeNoise(TIME * timeNoiseSpeed) * timeNoiseAmt;
// --- spatial noise ---
float sNoise = hash(gl_FragCoord.xy * 0.02) * spatialNoiseAmt;
angle += sNoise;
// --- brightness dependent quantize ---
float steps = mix(maxAngleSteps, 1.0, inBright);
float stepSize = 6.28318 / max(steps, 1.0);
angle = floor(angle / stepSize) * stepSize;
vec2 dir = vec2(cos(angle), sin(angle));
// --- unstable feedback offset (datamosh-ish) ---
vec2 chaosOffset = dir * decayBright * velocity;
chaosOffset += feedbackChaos * vec2(
hash(vec2(TIME, gl_FragCoord.y)),
hash(vec2(gl_FragCoord.x, TIME))
) * 20.0;
vec2 loc = gl_FragCoord.xy + chaosOffset;
vec4 smear = IMG_PIXEL(outputPass, loc);
vec4 base = IMG_THIS_PIXEL(outputPass);
float mixVal = inBright * colourDecay;
gl_FragColor = mix(smear, base, mixVal);
}
}
}