#version 330 core

out vec4 FragColor;

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
uniform float uIterations;
uniform float uStepBase;
uniform float uStepScale;
uniform float uTravelSpeed;
uniform float uRadiusBase;
uniform float uRadiusAbsWeight;
uniform float uRadiusWaveScale;
uniform float uRadiusWaveAmount;
uniform float uNoiseStart;
uniform float uNoiseEnd;
uniform float uNoiseGrowth;
uniform float uNoiseTimeScale;
uniform float uNoiseSpatialScale;
uniform float uNoiseWeight;
uniform float uColorFrequency;
uniform vec4 uColorPhase;
uniform float uBrightness;
uniform float uExposure;

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, r = 0.0, s = 0.0, d = 0.0, n = 0.0, t = iTime;
    vec3 p = iResolution;
    u = (u - p.xy / 2.0) / p.y;

    o = vec4(0.0);
    float iterations = clamp(uIterations, 1.0, 200.0);
    float noiseEnd = max(uNoiseEnd, uNoiseStart + 0.0001);
    float noiseGrowth = max(uNoiseGrowth, 1.0001);

    for (o *= i; i++ < iterations;
         d += s = uStepBase + abs(r) * uStepScale,
         o += uBrightness * (1.0 + cos(uColorFrequency * p.z + uColorPhase)) / max(s, 1e-6))
        for (p = vec3(u * d, d + t * uTravelSpeed),
             r = uRadiusBase - abs(p.y) * uRadiusAbsWeight + cos(t - dot(u, u) * uRadiusWaveScale) * uRadiusWaveAmount,
             n = uNoiseStart;
             n < noiseEnd;
             n *= noiseGrowth)
            r -= abs(dot(sin(uNoiseTimeScale * t + uNoiseSpatialScale * p * n), vec3(uNoiseWeight))) / n;

    o = tanh4(o / max(uExposure, 1.0));
}

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