196 lines
5.6 KiB
GLSL
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);
|
|
}
|