#version 330 core

out vec4 FragColor;

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

mat2 rot (float a) { return mat2(cos(a),-sin(a),sin(a),cos(a)); }
float map(vec3 p);

float hash12(vec2 p) {
    vec3 p3  = fract(vec3(p.xyx) * .1031);
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.x + p3.y) * p3.z);
}

float smin( float d1, float d2, float k ) {
    float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
    return mix( d2, d1, h ) - k*h*(1.0-h);
}

vec3 getNormal (vec3 pos) {
    vec2 noff = vec2(0.001,0.0);
    return normalize(vec3(map(pos)-map(pos-noff.xyy), map(pos)-map(pos-noff.yxy), map(pos)-map(pos-noff.yyx)));
}

float getAO( in vec3 pos, in vec3 nor, in float scale) {
    float occ = 0.0;
    float sca = 1.0;
    for( int i=0; i<5; i++ ) {
        float h = 0.01 + scale*float(i)/4.0;
        float d = map( pos + h*nor );
        occ += (h-d)*sca;
        sca *= 0.95;
        if( occ>0.35 ) break;
    }
    return clamp( 1.0 - 3.0*occ, 0.0, 1.0 );
}

float map(vec3 p) {
    float scale = 3.0;
    float dist = 100.0;
    float a = 1.0;
    for (float i = 0.0; i < 3.0; ++i) {
        vec3 s = p * scale / a;
        s.z += iTime / a + p.x;
        dist = smin(dist, abs(dot(sin(s), cos(s.yzx)) / scale * a) - .01, .15);
        a /= 3.0;
    }
    dist = smin(abs(length(p) - 1.0) - .4, dist, -.55);
    return dist;
}

void mainImage(out vec4 color, in vec2 pixel)
{
    color = vec4(0.0, 0.0, 0.0, 1.0);

    vec2 p = (pixel.xy - iResolution.xy / 2.0) / iResolution.y;
    vec3 pos = vec3(0.0);
    vec3 ray = normalize(vec3(p, 0.2));
    float rng = hash12(gl_FragCoord.xy);

    float shade = 1.0;
    for (shade; shade > 0.0; shade -= 1.0 / 20.0) {
        float d = map(pos);
        if (d < 0.001) break;
        d *= 0.9 + 0.1 * rng;
        pos += ray * d;
    }

    if (shade > 0.0) {
        vec3 normal = getNormal(pos);
        vec3 tint = 0.75 + 0.25 * -normal;
        tint += 2.0 * vec3(0.9, 0.7, 0.3) * getAO(pos, normal, 0.15);
        color = vec4(tint * shade, 1.0);
    }

    color.a *= uAlpha;
}

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