Files
ai-hell/frontend/shaders/compositor.frag

196 lines
5.6 KiB
GLSL

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