#version 330 core

out vec4 FragColor;

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;

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

float fractal(vec3 p) {
    float w = 4.0;
    float l = 0.0;
    for (int it = 0; it < 6; ++it) {
        p = cos(p - 0.5);
        l = 2.0 / max(dot(p, p), 1e-6);
        p *= l;
        w *= l;
    }
    return length(p) / w;
}

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

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

    o = vec4(0.0);
    for (i = 0.0; i < 100.0; i += 1.0) {
        // march
        p = vec3(u * d, d + t + t);
        // spin by t, twist by p.z
        mat2 rot = mat2(cos(.05 * t + p.z * .2), -sin(.05 * t + p.z * .2), sin(.05 * t + p.z * .2), cos(.05 * t + p.z * .2));
        p.xy *= rot;
        s = sin(4.0 + p.y + p.x);
        n = 5.0;
        for (; n < 16.0; n += n) {
            s -= abs(dot(cos(p * n), vec3(1.0))) / n;
        }
        float step = 0.002 + abs(min(fractal(p), s)) * 0.5;
        d += step;
        o += vec4(1.0) / max(step, 1e-6);
    }

    o = o / 2e5 / max(length(u), 1e-6);
    o = pow(o.xxxx, vec4(1.0, 2.0, 12.0, 0.0)) * 6.0;
    o = tanh4(mix(o, o.yzxw, length(u)));
}

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