#version 330 core

in vec2 vUV;
out vec4 FragColor;

uniform vec3 iResolution; // x=width, y=height
uniform float iTime;
uniform float uAlpha;

mat2 rotate(float a) {
    float c = cos(a), s = sin(a);
    return mat2(c, s, -s, c);
}

float box(vec3 p, vec3 b) {
    vec3 d = abs(p) - b;
    return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,vec3(0.0)));
}

float map(vec3 p) {
    p.x += sin(p.z*0.13)*3.0;
    p.y += sin(p.z*0.1)*2.0;
    p.xy *= rotate(p.z*0.10);
    const float l = 1.0;
    p.z = mod(p.z, l) - l*0.5;

    for(int i=0; i<4; i++) {
        p = abs(p) - 1.6;
        p.xz *= rotate(0.3);
        p.xy *= rotate(1.0);
    }
    return box(p, vec3(0.1, 0.8, 0.5));
}

vec3 getNormal(vec3 p) {
    float d = 1.0;
    return normalize(vec3(
        map(p + vec3(d,0,0)) - map(p - vec3(d,0,0)),
        map(p + vec3(0,d,0)) - map(p - vec3(0,d,0)),
        map(p + vec3(0,0,d)) - map(p - vec3(0,0,d))
    ));
}

float spc(vec3 ray, vec3 normal, vec3 lightDir) {
    vec3 R = reflect(-lightDir, normal);
    return pow(max(dot(R, -ray), 0.0), 10.0);
}

void mainImage(out vec4 O, in vec2 fragCoord)
{
    vec2 p = (fragCoord * 2.0 - iResolution.xy) / min(iResolution.x, iResolution.y);

    vec3 cPos = vec3(0.0, 0.0, -iTime*8.0);
    float dx = sin((-8.0 + cPos.z)*0.13)*3.0;
    float dy = sin((-8.0 + cPos.z)*0.1)*2.0;
    cPos.xy -= 0.5*vec2(dx, dy);
    vec3 cDir = normalize(vec3(-dx, -dy, -30.0));
    vec3 cUp = vec3(0.0, 1.0, 0.0);
    vec3 cSide = cross(cDir, cUp);

    vec3 ray = normalize(cSide * p.x + cUp * p.y + cDir);

    vec3 pos;
    float d = 0.0;
    float depth = 0.0;
    int steps = 0;
    for (int i=0;i<99;i++) {
        steps = i;
        pos = cPos + ray * depth;
        d = map(pos);
        depth += d*0.9;
        if (d < 0.001 || depth > 50.0) break;
    }

    vec3 col = vec3(0.0);
    if (d < 0.001) {
        vec3 normal = getNormal(pos);

        const float s = 2.0;
        vec3 redLightDir = normalize(vec3(cos(s*iTime), sin(s*iTime), cos(s*iTime)));
        vec3 blueLightDir = normalize(vec3(0.0,0.0,sin(pos.z*0.2)*10.0));

        float blueSpecular = spc(ray, normal, blueLightDir);
        col += vec3(0.0,blueSpecular/3.0,blueSpecular/2.0)*0.8;
        float redSpecular = spc(ray, normal, redLightDir);
        col += vec3(redSpecular, redSpecular/7.0, 0.14);
        col += 800.0*col * pow((clamp(depth, 40.0, 60.0)-40.0)/50.0, 2.0);
    }
    float c = pow(float(steps)/60.0, 6.0);
    col += vec3(c/8.0, c/5.0, c);
    O = vec4(col, 1.0);
}

void main()
{
    vec2 fragCoord = vUV * iResolution.xy;
    vec4 outColor;
    mainImage(outColor, fragCoord);
    outColor.a *= uAlpha;
    FragColor = outColor;
}
