#version 330 core

out vec4 FragColor;

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
uniform sampler2D iChannel0;

#define T (iTime*.5)
#define P(z) (vec3((cos((z) * .2) * .3) * 10., \
                   (cos((z) * .15) * .3) * 10., (z)))
#define R(a) mat2(cos(a), -sin(a), sin(a), cos(a))
#define N normalize

bool hitRoot = false;

vec3 tex3D(sampler2D tex, in vec3 p, in vec3 n)
{
    n = max((abs(n) - 0.2) * 7.0, 0.001);
    n /= (n.x + n.y + n.z);

    return (texture(tex, p.yz) * n.x + texture(tex, p.zx) * n.y + texture(tex, p.xy) * n.z).xyz;
}

float smin(float a, float b, float k)
{
    float f = max(0.0, 1.0 - abs(b - a) / k);
    return min(a, b) - k * 0.25 * f * f;
}

float fractal(vec3 p)
{
    float s, w, l;

    p.y *= .4;
    p += cos(p.yzx * 12.0) * .07;
    p.x -= 1.6;
    for (s = 0.0, w = .7; s++ < 7.0; p *= l, w *= l)
        p = abs(sin(p)) - 1.0,
        l = 1.5 / dot(p, p);

    return length(p) / w - .0005;
}

float light(vec3 p)
{
    p.xy -= P(p.z).xy;
    float t = iTime * .5;
    return length(p - vec3(
            tanh(cos(t * 1.8) * 2.0) * .25,
            tanh(cos(t * 1.0) * 3.0) * .3,
            4.0 + T + tanh(cos(t * .4) * 4.0) * 1.0
        )) - .25;
}

float root(vec3 p)
{
    p.xy *= R(sin(p.z * 2.0) * .1 + sin(p.z * 1.0) * .3);
    p = mod(p, 1.0) - .5;
    return .6 * length(p.xy) - .01;
}

float map(vec3 p)
{
    float s, r;
    r = root(p);

    p.xy -= P(p.z).xy;
    s = smin(fractal(p), 2.0 - abs(p.y), .8);
    hitRoot = r < s;
    s = min(s, r);
    return s;
}

float AO(in vec3 pos, in vec3 nor)
{
    float sca = 1.9, occ = 0.0;
    for (int i = 0; i < 5; i++)
    {
        float hr = 0.01 + float(i) * 0.5 / 4.0;
        float dd = map(nor * hr + pos);
        occ += (hr - dd) * sca;
        sca *= 0.7;
    }
    return clamp(1.0 - occ, 0.0, 1.0);
}

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, in vec2 u)
{
    float s = .1, d = 0.0, i = 0.0;
    vec3 r = iResolution;
    u = (u - r.xy / 2.0) / r.y;
    vec3 e = vec3(.001, 0.0, 0.0),
         p = P(T), ro = p,
         Z = N(P(T + 1.0) - p),
         X = N(vec3(Z.z, 0.0, -Z.x)),
         D = vec3(R(sin(T * .2) * .4) * u, 1.0) * mat3(-X, cross(X, Z), Z);

    o = vec4(0.0);
    for (; i++ < 128.0;)
        p = ro + D * d * .65,
        d += s = map(p),
        o += 4e1 * vec4(6.0, 9.0, 1.0, 0.0) + 1.0 / max(s, .001);

    vec3 grad = vec3(map(p) - map(p - e.xyy),
                     map(p) - map(p - e.yxy),
                     map(p) - map(p - e.yyx));
    r = N(grad);

    o.rgb *= tex3D(iChannel0, p * .5, r);
    o *= o;
    //o *= hitRoot ? .2 * vec4(4.0, 3.0, 2.0, 0.0) : vec4(1.0);
    o *= AO(p, r);
    o = tanh4(sqrt(max(d * o / pow(max(abs(light(p)), 1e-4), 3.0) / 5e9, vec4(0.0))));
}

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