#version 330 core

in vec2 vUV;
out vec4 FragColor;

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

const float PI = 3.14159265359;

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

float random(vec3 v) {
    return fract(sin(dot(v, vec3(12.9898, 78.233, 19.8321))) * 43758.5453);
}

float valueNoise(vec3 v) {
    vec3 i = floor(v);
    vec3 f = smoothstep(0.0, 1.0, fract(v));
    return mix(
        mix(
            mix(random(i), random(i + vec3(1.0, 0.0, 0.0)), f.x),
            mix(random(i + vec3(0.0, 1.0, 0.0)), random(i + vec3(1.0, 1.0, 0.0)), f.x),
            f.y
        ),
        mix(
            mix(random(i + vec3(0.0, 0.0, 1.0)), random(i + vec3(1.0, 0.0, 1.0)), f.x),
            mix(random(i + vec3(0.0, 1.0, 1.0)), random(i + vec3(1.0, 1.0, 1.0)), f.x),
            f.y
        ),
        f.z
    );
}

float fbm(vec3 v) {
    float n = 0.0;
    float a = 0.5;
    for (int i = 0; i < 5; i++) {
        n += a * valueNoise(v);
        v *= 2.0;
        a *= 0.5;
    }
    return n;
}

vec3 repeat3(vec3 p, float repCoef){
    return (fract(p/repCoef - 0.5) - 0.5) * repCoef;
}

vec2 repeat2(vec2 p, float repCoef){
    return (fract(p/repCoef - 0.5) - 0.5) * repCoef;
}

float repeat1(float z, float repCoef){
    return (fract(z/repCoef - 0.5) - 0.5) * repCoef;
}

vec3 kifs(vec3 p){
    for (int i=0; i < 4; i++) {
        p = abs(p) - 1.22;
        p.xy *= rotate(2.2 * float(i));
        p.xz *= rotate(22.2 * float(i));
    }
    return p;
}

vec2 polarMod(vec2 p, float r) {
    float a = atan(p.x, p.y) + (PI/r);
    float n = PI * 2.0 / r;
    a = floor(a/n) * n;
    p *= rotate(a);
    return p;
}

float sdBox(vec3 p, vec3 s){
    vec3 q = abs(p) - s;
    return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}

float distanceFunction(vec3 p){
    vec3 p1 = p;
    p1.xy *= rotate(p1.z * 0.922);
    p1.yz *= rotate(p1.y * 0.006 * fbm(p + vec3(iTime * .3)));
    p1.x = repeat1(p1.x, 6.0);
    p1.y = repeat1(p1.y, 4.0);
    p1.z = repeat1(p1.z, 2.0);
    p1.xy = polarMod(p1.xy, 22.0);
    p1 = kifs(p1);
    float d = sdBox(p1, vec3(0.4, 0.8, 0.6));
    return d;
}

vec3 getNormal(vec3 p){
    vec2 error = vec2(0.01, 0.0);
    return normalize(vec3(distanceFunction(p + vec3(error.x, error.y, error.y)) - distanceFunction(p - vec3(error.x, error.y, error.y)),
                          distanceFunction(p + vec3(error.y, error.x, error.y)) - distanceFunction(p - vec3(error.y, error.x, error.y)),
                          distanceFunction(p + vec3(error.y, error.y, error.x)) - distanceFunction(p - vec3(error.y, error.y, error.x))));
}

vec3 renderingFunc(vec2 uv){
    vec3 color = vec3(0.0);
    vec3 camPos = vec3(0.0, 0.0, -2.0 + iTime);
    vec3 lookPos = vec3(0.0, 0.0, iTime);
    vec3 forward = normalize(lookPos - camPos);
    vec3 right = normalize(cross(vec3(0.0, 1.0, 0.0), forward));
    vec3 up = normalize(cross(forward, right));
    float fov = 1.0;
    vec3 rayDir = normalize(uv.x * right + uv.y * up + forward * fov);

    float d = 0.0;
    float df = 0.0;
    float accum = 0.0;
    vec3 p = vec3(0.0);
    for(int i = 0; i < 80; i++){
        p = camPos + rayDir * d;
        df = distanceFunction(p);
        if(df <= 0.001) break;
        if(df > 100.0) break;
        df = max(abs(df), 0.01);
        accum += exp(-df * 22.0);
        d += df * 0.65;
    }
    color += accum * 1.1 * vec3(0.3961, 0.6157, 0.7255);

    float fog = (1.0 - clamp(length(camPos - p)/25.0, 0.1, 1.0));
    color *= pow(fog, 12.0);

    return color;
}

void mainImage(out vec4 O, in vec2 fragCoord){
    vec2 uv = (fragCoord * 2.0 - iResolution.xy)/min(iResolution.x, iResolution.y);
    vec3 color = renderingFunc(uv);
    O = vec4(color, 1.0);
}

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