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); }