#version 330 core

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
uniform float uIterations;
uniform float uUvScale;
uniform float uDepthSpeed;
uniform float uInnerStart;
uniform float uInnerEnd;
uniform float uInnerGrowth;
uniform float uDeformFrequency;
uniform float uDeformWeight;
uniform float uSwirlFrequency;
uniform float uSwirlAmount;
uniform float uStepBase;
uniform float uRingRadius;
uniform float uRingWeight;
uniform float uBrightnessBase;
uniform vec4 uColorPhase;
uniform float uExposure;

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;
    o = vec4(0.0);

    for(i = 0.0; i < uIterations; i += 1.0) {
        vec3 p = d * normalize(vec3(u * uUvScale, 0.0) - iResolution.xyy);
        p.z -= t * uDepthSpeed;
        // inner iterative deformation
        for(s = uInnerStart; s < uInnerEnd; ) {
            p -= dot(cos(t + p * s * uDeformFrequency), vec3(uDeformWeight)) / s;
            p += sin(p.yzx * uSwirlFrequency) * uSwirlAmount;
            s *= uInnerGrowth;
        }
        d += s = uStepBase + abs(uRingRadius - length(p.yx)) * uRingWeight;
        o += (uBrightnessBase + cos(d + uColorPhase)) / s;
    }

    o = tanh4(o / uExposure);
}

void main() {
    mainImage(FragColor, gl_FragCoord.xy);
    FragColor.a *= uAlpha;
}
