#version 330 core
out vec4 FragColor;
uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
uniform sampler2D iChannel0;
uniform vec2 iChannelResolution[4];

// Converted Apollonian Fractures shader

float Hash2d(vec2 uv)
{
    float f = uv.x + uv.y * 37.0;
    return fract(sin(f)*104003.9);
}
float mixP(float f0, float f1, float a)
{
    return mix(f0, f1, a*a*(3.0-2.0*a));
}
vec2 noise2dTex2(vec2 uv)
{
    vec2 fr = fract(uv);
    vec2 smoothv = fr*fr*(3.0-2.0*fr);
    vec2 fl = floor(uv);
    uv = smoothv + fl;
    return textureLod(iChannel0, (uv + 0.5)/iChannelResolution[0].xy, 0.0).xy;
}
const vec2 zeroOne = vec2(0.0, 1.0);
float noise2d(vec2 uv)
{
    vec2 fr = fract(uv.xy);
    vec2 fl = floor(uv.xy);
    float h00 = Hash2d(fl);
    float h10 = Hash2d(fl + zeroOne.yx);
    float h01 = Hash2d(fl + zeroOne);
    float h11 = Hash2d(fl + zeroOne.yy);
    return mixP(mixP(h00, h10, fr.x), mixP(h01, h11, fr.x), fr.y);
}

float Fractal(vec2 p)
{
    vec2 pr = p;
    float scale = 1.0;
    float iter = 1.0;
    for (int i = 0; i < 12; i++)
    {
        vec2 n2 = noise2dTex2(p*0.15*iter+iTime*1.925);
        float nx = n2.x - 0.5;
        float ny = n2.y;
        pr += vec2(nx, ny)*0.0002*iter*iter*iter;
        pr = fract(pr*0.5+0.5)*2.0 - 1.0;
        float len = pow(dot(pr, pr), 1.0+nx*0.5);
        float inv = 1.1/len;
        pr *= inv;
        scale *= inv;
        iter += 1.0;
    }
    float b = abs(pr.x)*abs(pr.y)/scale;
    return pow(b, 0.125)*0.95;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord.xy / iResolution.xy;
    uv -= 0.5;
    uv.x *= iResolution.x / iResolution.y;
    uv *= 0.94;

    vec2 warp = normalize(uv) * (1.0-pow(length(uv), 0.45));
    vec3 finalColor = vec3(Fractal(uv*2.0+1.0),
                           Fractal(uv*2.0+37.0),
                           Fractal((warp+0.5)*2.0+15.0));
    finalColor = 1.0 - finalColor;
    float circle = 1.0-length(uv*2.2);
    float at = atan(uv.x, uv.y);
    float aNoise = noise2d(vec2(at * 30.0, iTime));
    aNoise = aNoise * 0.5 + 0.5;
    finalColor *= pow(max(0.0, circle), 0.1)*2.0;
    finalColor *= 1.0 + pow(1.0 - abs(circle), 30.0);
    finalColor += vec3(1.0, 0.3, 0.03)*3.0 * pow(1.0 - abs(circle), 100.0) * aNoise;
    float outer = (1.0 - pow(max(0.0, circle), 0.1)*2.0);
    finalColor += vec3(1.,0.2,0.03)*0.4* max(0.0, outer*(1.0-length(uv)));

    // make background black by masking areas outside the circle
    float mask = smoothstep(0.0, 0.02, circle);
    finalColor *= mask;

    fragColor = vec4(finalColor, 1.0);
}

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