#version 330 core


#define T iTime

#define P(z) vec3(cos((z) *  .03) * 16.0, 0.0, (z))
#define R(a) mat2(cos(a), -sin(a), sin(a), cos(a))
#define N normalize

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

out vec4 FragColor;

float mist;
float lights;

bool fractalHit = false;

float sminf(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;
}

vec3 look(vec3 p, float zoffs) {
    float t = iTime;
    return p-vec3(
            tanh(cos(t*2.0)*0.5) * 1.0,
            tanh(cos(t*1.0)*2.0) * 1.2 + 6.0,
            zoffs+T*6.0+tanh(cos(t*0.4)*4.0)*4.0
        );
}

float orb(vec3 p) {
    return length(look(p, 16.0)) - 0.01;
}

float fogf(vec3 p) {
    vec3 q = p;
    float n, t = T;
    q.y += 30.0;
    for (n = 1.0; n < 4.1; n += n)
        q.yz += abs(dot(cos(T + q.z*0.01 + 0.4*q / n), vec3(0.6))) * n;
    return q.y;
}

float fractal(vec3 p) {
    float s, w, l;
    p += cos(p.yzx*0.2)*3.0;
    float a = 3.0 + 0.4*sin(p.x)*sin(p.z)*sin(p.y);
    p /= 6.0;
    p *= vec3(0.5, 0.25, 0.8);
    p.xy -= 1.5;
    p.y -= 0.5;
    for (s = 0.0, w = 0.5; s++ < 8.0; p *= l, w *= l)
        p = sin(p),
        l = a/dot(p,p);
    return length(p)/w*4.0;
}

float mapf(vec3 p) {
    float n, s, o, f;
    vec3 q = p;
    p.xy -= P(p.z).xy;
    p.y += 10.0;
    s = sminf(14.0 - p.y, f = fractal(p), 2.0);
    fractalHit = f <= s;
    mist = 0.1 + 0.2*abs(fogf(p));
    lights = orb(p);
    return min(mist, min(s, lights));
}

void mainImage(out vec4 o, in vec2 u) {
    float s = 0.1;
    float d = 1.0 + 0.05*dot(fract(sin(u)), sin(u));
    float i = 0.0;
    vec3 r = iResolution;

    u = (u+u-r.xy)/r.y;
    u.y += 0.65;

    vec3 e = vec3(0.005,0.0,0.0);
    vec3 p = P(T*6.0), ro = p;
    vec3 Z = N(P(T*6.0+4.0) - p);
    vec3 X = N(vec3(Z.z,0.0,-Z.x));
    vec3 D = vec3(R(sin(T*0.3)*0.2)*u, 1.0) * mat3(-X, cross(X, Z), Z);

    o = vec4(0.0);
    while(i++ < 80.0) {
        p = ro + D * d;
        d += s = max(mapf(p)*0.6, 0.001);
        o += 1.0/ s
            + 1e3*vec4(1.0,2.0,6.0,0.0)/lights
            + 3e2*vec4(6.0,2.0,1.0,0.0)/mist;
    }

    o = tanh(o*o/3e9*exp(-d/64.0));
    o.a = 1.0;
}

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