float rand(vec2 co) {
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

float noise(vec2 p) {
    vec2 i = floor(p);
    vec2 f = fract(p);
    vec2 u = f*f*f*(f*(f*6.-15.)+10.);
    vec2 d = vec2(1.0, 0.0); 
    
    float x1 = rand(i);
    float x2 = rand(i + d.xy);
    float x3 = rand(i + d.yx);
    float x4 = rand(i + d.xx);
    
    float mix1 = mix(x1, x2, u.x);
    float mix2 = mix(x3, x4, u.x);
    
    return mix(mix1, mix2, u.y);
    
}

float fbm(vec2 p, int n) {
    float s = .5;
    float f = 0.;
    mat2 r = 2. * mat2(0.76024, 0.64963, -0.64963, 0.76024);
    for(int i = 0; i < n; i++) {
        f += s * noise(p);
        p *= r;
        s *= .5;
    }
    return f;
}
float sdBox( vec3 p, vec3 b)
{
  vec3 q = abs(p) - b;
  return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}

vec2 getVoxel(vec3 p) {
    vec3 c = vec3(5.0, 0.0, 5.0);
    vec3 q = mod(p + 0.5 * c, c) - 0.5 * c;
    vec3 b = 0.5 * c.xxx;
    vec2 xz = floor((p.xz - 0.5 * c.xz) / c.xz);
    b.y += 10. * noise(xz / 5.0) + 20. * fbm(xz / 10., 2);
    vec2 r;
    r.x = float(sdBox(q, b) < 0.0);
    r.y = b.y;
    return r;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    vec2 ndc = 2.0 * uv - 1.0;
    ndc.y /= iResolution.x/iResolution.y;
    vec3 rd = normalize(vec3(ndc, 1.0));
    vec3 ro = vec3(1.0, 30.0, 20. * iTime);
    vec3 mp = floor(ro);
    vec3 d = abs(vec3(length(rd))/rd);
    
    bool hit = false;
    bvec3 mask;
    vec3 dist = (sign(rd) * (mp - ro) + (sign(rd) * 0.5) + 0.5) * d;
    for(float t = 0.0; t < 500.; t++) {        
        if(getVoxel(mp).x == 1.0){
            hit = true;
            break;
        }
        mask = lessThanEqual(dist.xyz, min(dist.yzx, dist.zxy));
        dist += vec3(mask) * d;
        mp += vec3(mask) * sign(rd);
        if(mp.y < 10.0) break;
    }
    
    vec3 col = vec3(0.53, 0.81, .92);
    if(hit) { 
        vec2 r = getVoxel(mp);
        col = vec3(0.7098, 0.3961, 0.1176);
        if(r.y > 15.0 - 2. * noise(mp.xz)) col = vec3(0.4862, 0.9882, 0.0); 
        col += 0.4 * fbm(mp.xz * 2.0 + mp.y, 2);
        float dz = 0.025 * (mp.z - ro.z);
        col *= 1.0/(1.0 + dz * dz);
    }
    if(mp.y < 10.0) col = vec3(0.0, 0.0, 1.0);
    
    // Output to screen
    fragColor = vec4(col,1.0);
}
