#version 330 core

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
out vec4 FragColor;

#define T iTime
#define P(z) (vec3(cos((z)*.1)*4.+tanh(cos((z) * .1) * 1.8) * 4., \
                   tanh(cos((z) * .15) * .4) * 8., (z)))
#define R(a) mat2(cos(a+vec4(0,33,11,0)))
#define N normalize

float tunnel(vec3 p, float c) {
    return c - length((p - P(p.z)).xy);
}

float gaz(vec3 p){
    p.xy--;
    p=mod(p,2.)-1.;
    p=abs(p)-1.;
    if(p.x < p.z)p.xz=p.zx;
    if(p.y < p.z)p.yz=p.zy;
    if(p.x < p.y)p.xy=p.yx;
    float s=2.;
    for(int i=0;i<7;i++){
        float r2=2.2/clamp(dot(p,p),.1,1.);
        p=abs(p)*r2-vec3(.6,.6,3.5);
        s*=r2;
    }
    return length(p*2.)/s-.005;
}

float surface(vec3 p) {
    p /= 1e1;
    return gaz(p) * 1e1;
}

float map(vec3 p) {
    float ground = 5. - p.y;
    return min(tunnel(p, 16.), max(tunnel(p, 1.), surface(p)));
}

void mainImage(out vec4 o, in vec2 u) {
    float s, i, d;
    vec3 r = iResolution;
    u = (u+u-r.xy)/r.y;
    u.yx -= .3;

    vec3  p = P(T*2.),
          Z = N(P(T*2.+6.) - p),
          X = N(vec3(Z.z, 0, -Z)),
          D = N(vec3(R(sin(T*.1)*.2)*u, 1) * mat3(-X, cross(X, Z), Z));

    // march
    for(o*=i; i++<1e2; )
        p += D*s,
        d += s = map(p),
        o += s ;

    // normal
    const float h = 0.005;
    const vec2 k = vec2(1,-1);
    vec3 n = N(k.xyy*map( p + k.xyy*h ) + 
               k.yyx*map( p + k.yyx*h ) + 
               k.yxy*map( p + k.yxy*h ) + 
               k.xxx*map( p + k.xxx*h ) );
    
    // diffuse
    o *= (.1 + max(dot(n, -D), 0.));
    o /= d;
    
    // reflection march
    vec4 ref;
    for(p += n*.15, D = reflect(D, n), s=i=0.; i++<42.; )
        p += D*s,
        s = map(p),
        ref += s;

    o += ref;
    o = tanh(o / 1e1 / min(d, 2e1) * exp(d/9.));
}

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