feat: add WebGL compositor fragment shader with all distortion effects
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
precision highp float;
|
||||
|
||||
// Textures
|
||||
uniform sampler2D u_currentImage;
|
||||
uniform sampler2D u_nextImage;
|
||||
|
||||
// Transition
|
||||
uniform float u_blend; // 0=current, 1=next
|
||||
uniform int u_transitionMode; // 0=crossfade, 1=dissolve, 2=glitch_cut, 3=melt_morph
|
||||
|
||||
// Shader params (from escalation engine, all 0-1)
|
||||
uniform float u_morphSpeed;
|
||||
uniform float u_shaderSeverity;
|
||||
uniform float u_noiseLevel;
|
||||
uniform float u_time;
|
||||
|
||||
// Scare flash
|
||||
uniform float u_flashIntensity; // 0=none, 1=full
|
||||
uniform int u_flashType; // 0=white, 1=inversion, 2=face_flash
|
||||
|
||||
// Vignette and overlay
|
||||
uniform float u_vignetteStrength;
|
||||
uniform vec3 u_colorTint; // palette color shift
|
||||
|
||||
varying vec2 v_uv;
|
||||
|
||||
// --- Noise functions ---
|
||||
|
||||
float hash(vec2 p) {
|
||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
|
||||
}
|
||||
|
||||
float noise(vec2 p) {
|
||||
vec2 i = floor(p);
|
||||
vec2 f = fract(p);
|
||||
float a = hash(i);
|
||||
float b = hash(i + vec2(1.0, 0.0));
|
||||
float c = hash(i + vec2(0.0, 1.0));
|
||||
float d = hash(i + vec2(1.0, 1.0));
|
||||
vec2 u = f * f * (3.0 - 2.0 * f);
|
||||
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
|
||||
}
|
||||
|
||||
// --- Effect functions ---
|
||||
|
||||
vec2 meshWarp(vec2 uv, float severity, float time) {
|
||||
float freq = 3.0 + severity * 8.0;
|
||||
float amp = 0.002 + severity * 0.03;
|
||||
uv.x += sin(uv.y * freq + time * 0.5) * amp;
|
||||
uv.y += cos(uv.x * freq + time * 0.7) * amp;
|
||||
return uv;
|
||||
}
|
||||
|
||||
vec3 chromaticAberration(sampler2D tex, vec2 uv, float severity) {
|
||||
float offset = 0.001 + severity * 0.015;
|
||||
float r = texture2D(tex, uv + vec2(offset, 0.0)).r;
|
||||
float g = texture2D(tex, uv).g;
|
||||
float b = texture2D(tex, uv - vec2(offset, 0.0)).b;
|
||||
return vec3(r, g, b);
|
||||
}
|
||||
|
||||
vec2 meltEffect(vec2 uv, float severity, float time) {
|
||||
float melt = severity * 0.05 * noise(vec2(uv.x * 10.0, time * 0.3));
|
||||
uv.y += melt;
|
||||
return uv;
|
||||
}
|
||||
|
||||
vec2 glitchEffect(vec2 uv, float severity, float time) {
|
||||
float glitchLine = step(0.98 - severity * 0.15, hash(vec2(floor(uv.y * 50.0), floor(time * 8.0))));
|
||||
uv.x += glitchLine * (hash(vec2(time, uv.y)) - 0.5) * severity * 0.1;
|
||||
return uv;
|
||||
}
|
||||
|
||||
float scanlines(vec2 uv, float time) {
|
||||
return 0.95 + 0.05 * sin(uv.y * 800.0 + time * 2.0);
|
||||
}
|
||||
|
||||
float filmGrain(vec2 uv, float time, float amount) {
|
||||
return 1.0 - amount * 0.5 * (hash(uv * 1000.0 + time) - 0.5);
|
||||
}
|
||||
|
||||
float vignette(vec2 uv, float strength) {
|
||||
vec2 center = uv - 0.5;
|
||||
float dist = length(center);
|
||||
return 1.0 - smoothstep(0.3, 0.9, dist) * strength;
|
||||
}
|
||||
|
||||
float pulse(float time, float severity) {
|
||||
return 1.0 + sin(time * (1.0 + severity * 3.0)) * severity * 0.1;
|
||||
}
|
||||
|
||||
// --- Transition functions ---
|
||||
|
||||
vec3 transitionCrossfade(vec2 uv, float blend) {
|
||||
vec3 a = texture2D(u_currentImage, uv).rgb;
|
||||
vec3 b = texture2D(u_nextImage, uv).rgb;
|
||||
return mix(a, b, blend);
|
||||
}
|
||||
|
||||
vec3 transitionDissolve(vec2 uv, float blend) {
|
||||
vec3 a = texture2D(u_currentImage, uv).rgb;
|
||||
vec3 b = texture2D(u_nextImage, uv).rgb;
|
||||
// Dissolve through black
|
||||
float mid = 0.5;
|
||||
if (blend < mid) {
|
||||
return a * (1.0 - blend / mid);
|
||||
} else {
|
||||
return b * ((blend - mid) / (1.0 - mid));
|
||||
}
|
||||
}
|
||||
|
||||
vec3 transitionGlitchCut(vec2 uv, float blend, float time) {
|
||||
// Hard cut with glitch artifacts
|
||||
float threshold = 0.5 + 0.1 * sin(time * 20.0);
|
||||
vec3 img = blend < threshold
|
||||
? texture2D(u_currentImage, uv).rgb
|
||||
: texture2D(u_nextImage, uv).rgb;
|
||||
// Add RGB split at transition point
|
||||
if (abs(blend - threshold) < 0.1) {
|
||||
img = chromaticAberration(blend < threshold ? u_currentImage : u_nextImage, uv, 0.8);
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
vec3 transitionMeltMorph(vec2 uv, float blend, float time) {
|
||||
vec2 meltUV = uv;
|
||||
meltUV.y += blend * 0.1 * noise(vec2(uv.x * 5.0, time));
|
||||
vec3 a = texture2D(u_currentImage, meltUV).rgb;
|
||||
vec3 b = texture2D(u_nextImage, uv).rgb;
|
||||
return mix(a, b, smoothstep(0.3, 0.7, blend));
|
||||
}
|
||||
|
||||
// --- Main ---
|
||||
|
||||
void main() {
|
||||
vec2 uv = v_uv;
|
||||
float severity = u_shaderSeverity;
|
||||
|
||||
// Apply distortion effects
|
||||
uv = meshWarp(uv, severity * u_morphSpeed, u_time);
|
||||
uv = meltEffect(uv, severity * 0.5, u_time);
|
||||
uv = glitchEffect(uv, severity, u_time);
|
||||
|
||||
// Clamp UV to prevent sampling outside texture
|
||||
uv = clamp(uv, 0.0, 1.0);
|
||||
|
||||
// Image transition
|
||||
vec3 color;
|
||||
if (u_transitionMode == 0) {
|
||||
color = transitionCrossfade(uv, u_blend);
|
||||
} else if (u_transitionMode == 1) {
|
||||
color = transitionDissolve(uv, u_blend);
|
||||
} else if (u_transitionMode == 2) {
|
||||
color = transitionGlitchCut(uv, u_blend, u_time);
|
||||
} else {
|
||||
color = transitionMeltMorph(uv, u_blend, u_time);
|
||||
}
|
||||
|
||||
// Chromatic aberration on composited image
|
||||
if (severity > 0.1) {
|
||||
vec3 aberrated = chromaticAberration(u_currentImage, uv, severity);
|
||||
color = mix(color, aberrated, severity * 0.3);
|
||||
}
|
||||
|
||||
// Color tint / palette shift
|
||||
color = mix(color, color * u_colorTint, severity * 0.3);
|
||||
|
||||
// Pulse brightness
|
||||
color *= pulse(u_time, severity);
|
||||
|
||||
// Film grain / noise
|
||||
color *= filmGrain(v_uv, u_time, u_noiseLevel);
|
||||
|
||||
// Scanlines
|
||||
color *= scanlines(v_uv, u_time);
|
||||
|
||||
// Vignette
|
||||
color *= vignette(v_uv, 0.5 + severity * 0.5);
|
||||
|
||||
// Flash effects
|
||||
if (u_flashIntensity > 0.0) {
|
||||
if (u_flashType == 0) {
|
||||
// White-out
|
||||
color = mix(color, vec3(1.0), u_flashIntensity);
|
||||
} else if (u_flashType == 1) {
|
||||
// Inversion
|
||||
color = mix(color, 1.0 - color, u_flashIntensity);
|
||||
} else {
|
||||
// Face flash (red tint)
|
||||
color = mix(color, vec3(1.0, 0.0, 0.0), u_flashIntensity * 0.7);
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(color, 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user