#version 330 core

in vec2 vUV;
out vec4 FragColor;

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
uniform sampler2D iChannel0;

#define FAR 1.5

int id = 0; // Object ID - Red perspex: 0; Black lattice: 1.

vec3 tex3D( sampler2D tex, in vec3 p, in vec3 n ){
   
    n = max((abs(n) - .2), .001);
    n /= (n.x + n.y + n.z ); // Roughly normalized.
    
    p = (texture(tex, p.yz)*n.x + texture(tex, p.zx)*n.y + texture(tex, p.xy)*n.z).xyz;
    
    return p*p;
}


float n3D(vec3 p){
    
    const vec3 s = vec3(7, 157, 113);
    vec3 ip = floor(p); p -= ip; 
    vec4 h = vec4(0., s.yz, s.y + s.z) + dot(ip, s);
    p = p*p*(3. - 2.*p); //p *= p*p*(p*(p * 6. - 15.) + 10.);
    h = mix(fract(sin(h)*43758.5453), fract(sin(h + s.x)*43758.5453), p.x);
    h.xy = mix(h.xz, h.yw, p.y);
    return mix(h.x, h.y, p.z); // Range: [0, 1].
}

vec2 hash22(vec2 p) { 

    float n = sin(dot(p, vec2(41, 289)));
    
    // Animated.
    p = fract(vec2(262144, 32768)*n); 
    // Note the ".45," insted of ".5" that you'd expect to see. When edging, it can open 
    // up the cells ever so slightly for a more even spread. In fact, lower numbers work 
    // even better, but then the random movement would become too restricted. Zero would 
    // give you square cells.
    return sin( p*6.2831853 + iTime )*.45 + .5; 
    
}

float Voronoi(in vec2 p){
    
    vec2 g = floor(p), o; p -= g;
    
    vec3 d = vec3(1); // 1.4, etc. "d.z" holds the distance comparison value.
    
    for(int y = -1; y <= 1; y++){
        for(int x = -1; x <= 1; x++){
            
            o = vec2(x, y);
            o += hash22(g + o) - p;
            
            d.z = dot(o, o);             
            d.y = max(d.x, min(d.y, d.z));
            d.x = min(d.x, d.z); 
                       
        }
    }
    
    return max(d.y/1.2 - d.x*1., 0.)/1.2;
    
}

float heightMap(vec3 p){
    
    id =0;
    float c = Voronoi(p.xy*4.); // The fiery bit.
    
    if (c<.07) {c = smoothstep(0.7, 1., 1.-c)*.2; id = 1; }

    return c;
}

float m(vec3 p){
   
    float h = heightMap(p); 
    
    return 1. - p.z - h*.1;
    
}

vec3 nr(vec3 p, inout float edge) { 
    
    vec2 e = vec2(.005, 0);

    float d1 = m(p + e.xyy), d2 = m(p - e.xyy);
    float d3 = m(p + e.yxy), d4 = m(p - e.yxy);
    float d5 = m(p + e.yyx), d6 = m(p - e.yyx);
    float d = m(p)*2.;
         
    edge = abs(d1 + d2 - d) + abs(d3 + d4 - d) + abs(d5 + d6 - d);
    edge = smoothstep(0., 1., sqrt(edge/e.x*2.));
    
    return normalize(vec3(d1 - d2, d3 - d4, d5 - d6));
}

vec3 eMap(vec3 rd, vec3 sn){
    
    vec3 sRd = rd; 
    
    rd.xy -= iTime*.25;
    rd *= 3.;
        
    float c = n3D(rd)*.57 + n3D(rd*2.)*.28 + n3D(rd*4.)*.15; // Noise value.
    c = smoothstep(0.5, 1., c); 
    
    vec3 col = vec3(min(c*1.5, 1.), pow(c, 2.5), pow(c, 12.)).zyx; 
    
    return mix(col, col.yzx, sRd*.25+.25); 
    
}

void mainImage(out vec4 c, vec2 u){

    vec3 r = normalize(vec3(u - iResolution.xy*.5, iResolution.y)), 
         o = vec3(0), l = o + vec3(0, 0, -1);
   
    vec2 a = sin(vec2(1.570796, 0) + iTime/8.);
    r.xy = mat2(a, -a.y, a.x) * r.xy;

    float d, t = 0.;
    
    for(int i=0; i<32;i++){
        
        d = m(o + r*t);
        if(abs(d)<0.001 || t>FAR) break;
        t += d*.7;

    }
    
    t = min(t, FAR);
    
    c = vec4(0);
    
    float edge = 0.; 
    
    if(t<FAR){
    
        vec3 p = o + r*t, n = nr(p, edge);

        l -= p; 
        d = max(length(l), 0.001);
        l /= d;

        float hm = heightMap(p);
        
        vec3 tx = tex3D(iChannel0, (p*2. + hm*.2), n);

        c.xyz = vec3(1.)*(hm*.8 + .2);
        
        c.xyz *= vec3(1.5)*tx;
        
        c.x = dot(c.xyz, vec3(.299, .587, .114));
        if (id==0) c.xyz *= vec3(min(c.x*1.5, 1.), pow(c.x, 5.), pow(c.x, 24.))*2.;
        else c.xyz *= .1;
        
        float df = max(dot(l, n), 0.);
        float sp = pow(max(dot(reflect(-l, n), -r), 0.), 32.);
        
        if(id == 1) sp *= sp;
        
        c.xyz = c.xyz*(df + .75) + vec3(1, .97, .92)*sp + vec3(.5, .7, 1)*pow(sp, 32.);
        
        vec3 em = eMap(reflect(r, n), n);
        if(id == 1) em *= .5;
        c.xyz += em;
        
        c.xyz *= 1. - edge*.8;
        
        c.xyz *= 1./(1. + d*d*.125);
        
    }
    
    c = vec4(sqrt(clamp(c.xyz, 0., 1.)), 1.);
    
}

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