#define AA 2
const float eps = 0.0001;
float s2;
vec3 getRay(in vec3 cameraDir, in vec2 uv) {
    vec3 cameraPlaneU = vec3(normalize(vec2(cameraDir.y, -cameraDir.x)), 0);
    vec3 cameraPlaneV = cross(cameraPlaneU, cameraDir) ;
	return normalize(cameraDir + uv.x * cameraPlaneU + uv.y * cameraPlaneV);
}
float j(in float x) {
    return fract(x)*mod(floor(x), 2.0)+floor(x*0.5)+2.0;
}
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,0.0));
}
const uint k = 1234543210U;
uint hash( uvec3 x )
{
	vec3 z = vec3(x)-1000000.+0.5;
    if (length(z) > s2-1.0) return 0u;
    x = ((x>>8U)^x.yzx)*k;
    x = ((x>>8U)^x.yzx)*k;
    x = ((x>>8U)^x.yzx)*k;
    return uint((64.0*(float(x.x)/float(0xffffffffU))));
}
const float s = 0.15;
const float ku = 0.68;
float edge(in vec3 p, in uint d, in bool n) {
    float ds = 1000.0;
    if ((d&1u) != 0u) ds = min(ds, min(max(length(p.yz), -p.x+s)-s,max(length(p.yz), -p.x+ku)-s*1.3));
    if ((d&2u) != 0u) ds = min(ds, min(max(length(p.yz), p.x+s)-s,max(length(p.yz), p.x+ku)-s*1.3));
    if ((d&4u) != 0u) ds = min(ds, min(max(length(p.xz), -p.y+s)-s,max(length(p.xz), -p.y+ku)-s*1.3));
    if ((d&8u) != 0u) ds = min(ds, min(max(length(p.xz), p.y+s)-s,max(length(p.xz), p.y+ku)-s*1.3));
    if ((d&16u) != 0u) ds = min(ds, min(max(length(p.xy), -p.z+s)-s,max(length(p.xy), -p.z+ku)-s*1.3));
    if ((d&32u) != 0u) ds = min(ds, min(max(length(p.xy), p.z+s)-s,max(length(p.xy), p.z+ku)-s*1.3));
   	if (d > 0u) ds = min(ds, length(p)-s); 
    return n?ds:max(ds, box(p, vec3(0.5)));
}
uint getState(in uvec3 id) {
	 uint v = hash(id);
    if ((id.x+id.y+id.z)%2u == 1u) {
        v = ((hash(id-uvec3(1, 0, 0))&1u)<<1)|
            ((hash(id+uvec3(1, 0, 0))&2u)>>1)|
            ((hash(id-uvec3(0, 1, 0))&4u)<<1)|
            ((hash(id+uvec3(0, 1, 0))&8u)>>1)|
            ((hash(id-uvec3(0, 0, 1))&16u)<<1)|
            ((hash(id+uvec3(0, 0, 1))&32u)>>1);
    }
    return v;
}
vec3 norm( vec3 p, in uint id)
{
    const float h = eps;
    const vec2 k = vec2(1,-1);
    return normalize(k.xyy * edge(p + k.xyy * h, id, true) +
                     k.yyx * edge(p + k.yyx * h, id, true) +
                     k.yxy * edge(p + k.yxy * h, id, true) +
                     k.xxx * edge(p + k.xxx * h, id, true));
}
float sphere(in vec3 ro, in vec3 rd, in float ra) {
    float b = dot(ro, rd);
    float c = dot(ro, ro) - ra*ra;
    float h = b*b-c;
    if (h < 0.0) return -1.;
    h = sqrt(h);
    return -b-h;
}
vec3 getColor(in vec3 ro, in vec3 rd) {
    float s = sphere(ro, rd, j(iTime));
    if (s < 0.0) return vec3(1);
    ro = ro+rd*(s*0.7);
    vec3 p = vec3(ro);
    vec3 mp = floor(p);
    vec3 p1 = mp;
    vec3 dd = 1./rd;
    int ii;
    bool h = false;
    int c = 0;
    for (int i = 0; i < 100; i += 1) {
    	vec3 a = -(p-mp)*dd;
    	vec3 b = -((p-mp)-1.0)*dd;
        vec3 ax = max(a, b);
        float m = min(min(ax.x, ax.y), ax.z);
        p += rd*m;
        if (m == ax.x) {p.x += sign(a.x-b.x);p1.x -= sign(a.x-b.x);};
        if (m == ax.y) {p.y += sign(a.y-b.y);p1.y -= sign(a.y-b.y);};
        if (m == ax.z) {p.z += sign(a.z-b.z);p1.z -= sign(a.z-b.z);};
        if (length(p1) > s2*2.0) break;
        float d0 = 0.0;
        vec3 o = (p-mp)-0.5;//(fract(p)-0.5);
        bool hit = false;
        int n;
        uint s = getState(uvec3((p1)+1000000.));
        if (s != 0u) {
            h = true;
        	for (int n = 0; n < 200; n += 1) {
                ii += 1;
        		vec3 pppp = o+rd*d0;
        		float ddd = edge(pppp, s, false);
        	    d0 += ddd;
        	    if (ddd < eps || d0 > 1.) {
        	    	break;
        	    }
        	}
        	vec3 pp = o+rd*d0;
            vec3 n = norm(pp, s);
        	if (d0 < 1.) return (1.-exp(-0.05/vec3(float(ii)*0.1*(dot(n, rd)+1.0))));
    	} else {
            if (h) c += 1;
        }
        if (c > 10) break;
    }
    return vec3(1);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    s2 = j(iTime);
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    vec2 m = iMouse.z<1.0?vec2(iTime*0.25+0.5, 3.141592*0.5+sin(iTime*0.5+2.0)*0.5):((iMouse.xy)/iResolution.xy) * vec2(6.28, 3.14159263);
    vec3 ro = vec3(sin(m.y) * cos(-m.x), sin(m.y) * sin(-m.x), cos(m.y))*3.0*s2*0.7;
    vec3 rd = getRay(-normalize(ro), uv);
    vec3 color = (1.-exp(-getColor(ro, rd)*2.0));
    fragColor = vec4(color, 1);
}
