#version 330 core

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
uniform float uMainIterations;
uniform float uWarpAmount;
uniform float uVerticalDrift;
uniform float uPatternFrequency;
uniform float uPatternSeed;
uniform float uStepBase;
uniform float uDepthOffset;
uniform float uDepthWeight;
uniform float uExposure;
uniform vec2 uGlowCenter;
uniform float uGlowSize;
uniform vec3 uColorTint;

out vec4 FragColor;

// helper: stable tanh for vec4
vec4 tanh4(vec4 x) {
    vec4 e = exp(clamp(2.0 * x, vec4(-20.0), vec4(20.0)));
    return (e - vec4(1.0)) / (e + vec4(1.0));
}

void mainImage(out vec4 o, vec2 u) {
    float i = 0.0;
    float d = 0.0;
    float s = 0.0;
    float t = iTime;
    vec3 p = iResolution;
    o = vec4(0.0);

    u = (u - p.xy / 2.0) / p.y;

    float mainIterations = clamp(uMainIterations, 1.0, 200.0);
    float patternFrequency = max(uPatternFrequency, 0.01);
    float patternSeed = max(uPatternSeed, 0.0001);
    float glowSize = max(uGlowSize, 0.01);
    float exposure = max(uExposure, 1.0);

    for(i = 0.0; i < mainIterations; i += 1.0) {
        p = vec3(u * d, d + t);
        // inner iterative deformation
        for(s = 0.15; s < 1.0; ) {
            p += cos(t + p.yzx * 0.6) * sin(p.z * 0.1) * uWarpAmount;
            p.y += sin(t + p.x) * uVerticalDrift;
            p += abs(dot(sin(p * s * patternFrequency), p - p + patternSeed)) / s;
            s *= 1.5;
        }
        d += s = uStepBase + abs(uDepthOffset + p.y) * uDepthWeight;
        o += vec4(uColorTint, 0.0) / s;
    }

    vec2 uu = (u - uGlowCenter) / glowSize;
    float denom = dot(uu, uu);
    denom = max(denom, 1e-6);
    o = tanh4(o / exposure / denom);
}

void main() {
    mainImage(FragColor, gl_FragCoord.xy);
    // Ensure the output has an explicit alpha (use requested uAlpha).
    // Previous multiplication left alpha at 0 in many cases, producing a
    // fully transparent texture when used as an input to post-processes.
    FragColor.a = uAlpha;
}
