#define MAX_STEPS 			50
#define MAX_DISTANCE 		8.5
#define MARCHING_STEP_INC 	.4
#define EPSILON 			.01

#define PI 3.14159265358979323846
#define TIMER(sec, min, max) (((mod(iTime, (sec)) * ((max) - min)) / (sec)) + (min))

float o[4];

float scol[7];

vec3 final_color = vec3(0.);
float current_depth = 1.;
vec3 current_color = vec3(0.);

#define MAX_DISPLACE	 	.25
#define MAX_COLOR_BLEED		1.25
void colorize(in float depth, in vec3 color) {
    float c = smoothstep(depth-MAX_COLOR_BLEED, depth+MAX_COLOR_BLEED, current_depth);
	float d = smoothstep(depth-MAX_DISPLACE, depth+MAX_DISPLACE, current_depth);

	current_depth= depth*(d) + current_depth*(1.-d);
	current_color = (1.-c)*current_color + (c)*color;
}

vec4 texCube(in sampler2D sam, in vec3 p, in vec3 n, in float k ) {
	vec4 x = texture( sam, p.yz );
	vec4 y = texture( sam, p.zx );
	vec4 z = texture( sam, p.xy );
    vec3 w = pow( abs(n), vec3(k) );
	return (x*w.x + y*w.y + z*w.z) / (w.x+w.y+w.z);
}

mat2 mm2(in float a) {float c = cos(a), s = sin(a);return mat2(c,s,-s,c);}

#define K 12.
float smin(in float a, in float b ) {float res = exp( -K*a ) + exp( -K*b ); return -log( res )/K;}

float sdTorus(in vec3 p, in vec2 t){vec2 q = vec2(length(p.xz)-t.x,p.y); return length(q)-t.y;}
vec3 opTwist(in vec3 p, in float d){float  c = cos(d*p.y+d); float  s = sin(d*p.y+d); mat2   m = mat2(c,-s,s,c); return vec3(m*p.xz,p.y);}

float scene(in vec3 p) {
    o[0] = sdTorus(p, vec2(1.+scol[2], .15)) + cos(1.5*p.x)*cos(1.5*p.y)*sin(1.5*p.z);
    o[1] = sdTorus(p, vec2(1.+scol[3], .15)) + cos(1.5*p.x)*cos(1.5*p.y)*cos(1.5*p.z);
    o[2] = sdTorus(p, vec2(1.+scol[4], .15)) + cos(1.5*p.x)*sin(1.5*p.y)*sin(1.5*p.z);
    o[3] = sdTorus(p, vec2(1.+scol[5], .15)) + sin(1.5*p.x)*sin(1.5*p.y)*sin(1.5*p.z);

    return smin(o[0], smin(o[1], smin(o[2], o[3])));
}

float rayMarch(in vec3 origin, in vec3 ray) {
	float t = 0.;
	for (int i=0; i < MAX_STEPS; i++) {
		float d = scene(origin + ray*t);
        if (d < EPSILON) break;
        t += d*MARCHING_STEP_INC;
        if (t > MAX_DISTANCE) break;
	}
    return t;
}

float ambientOcculation(in vec3 origin, in vec3 ray) {
    const float delta = 0.1;
    const int samples = 6;
    float r = 0.;
    for (int i=1; i <= samples; i++) {
        float t = delta * float(i);
        float d = scene(origin + ray*t);
        float len = abs(t - d);
        r += len * pow(2.0, -float(i));
    }
    return r;
}

float shadowSample(in vec3 origin, in vec3 ray) {
    float r = 1.;
    float t = 1.;
    const int samples = 6;
    for (int i=0; i <= samples; i++) {
        float d = scene(origin + ray*t);
        r = min(r, 16.0*d/t);
        t += d;
    }
    return r;
}

vec3 getNormal(in vec3 p, in float ep) {
	float d0 = scene(p);
	float dX = scene(p - vec3(ep, 0.0, 0.0));
	float dY = scene(p - vec3(0.0, ep, 0.0));
	float dZ = scene(p - vec3(0.0, 0.0, ep));

	return normalize(vec3(dX-d0, dY-d0, dZ-d0));
}


float f1(in float x) {
    return sqrt(1.-(x-1.)*(x-1.));
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = (fragCoord.xy / iResolution.xy) - vec2(.5);
    uv.x *= iResolution.x/iResolution.y;

    #	define MM 4.5
    scol[0]=texture(iChannel2, vec2(0., 0.25) ).x;     scol[0]=f1(clamp(1.*scol[0]*scol[0], 0., 1.)); scol[0]*=MM*scol[0]*scol[0];
    scol[1]=texture(iChannel2, vec2(.17*1., 0.25) ).x; scol[1]=f1(clamp(1.*scol[1]*scol[1], 0., 1.)); scol[1]*=MM*scol[1]*scol[1];
    scol[2]=texture(iChannel2, vec2(.17*2., 0.25) ).x; scol[2]=f1(clamp(1.*scol[2]*scol[2], 0., 1.)); scol[2]*=MM*scol[2]*scol[2];
    scol[3]=texture(iChannel2, vec2(.17*3., 0.25) ).x; scol[3]=f1(clamp(1.*scol[3]*scol[3], 0., 1.)); scol[3]*=MM*scol[3]*scol[3];
    scol[4]=texture(iChannel2, vec2(.17*4., 0.25) ).x; scol[4]=f1(clamp(1.*scol[4]*scol[4], 0., 1.)); scol[4]*=MM*scol[4]*scol[4];
    scol[5]=texture(iChannel2, vec2(.17*5., 0.25) ).x; scol[5]=f1(clamp(1.*scol[5]*scol[5], 0., 1.)); scol[5]*=MM*scol[5]*scol[5];
    scol[6]=texture(iChannel2, vec2(.99, 0.25) ).x;    scol[6]=f1(clamp(1.*scol[6]*scol[6], 0., 1.)); scol[6]*=MM*scol[6]*scol[6];

	vec3 eye = vec3(0., 0., -5.);
    vec3 light = vec3(3., -1.5, -6.);
    vec3 ray = vec3(vec2(uv.x, uv.y)*2., 1.);

    float depth;

	float rz = TIMER(20. ,0., PI*2.);
    float rx = .75;
    float ry = -2.65;

    eye.zx*=mm2(rx); eye.xy*=mm2(rz); eye.zy*=mm2(ry);
    light.zx*=mm2(rx); light.xy*=mm2(rz); light.zy*=mm2(ry);
   	ray.zx*=mm2(rx); ray.xy*=mm2(rz); ray.zy*=mm2(ry);

    depth = rayMarch(eye, ray);
	if (depth < MAX_DISTANCE) {
    	colorize(o[0], vec3(1.*scol[0], 1.*scol[6], 1.*scol[4]));
    	colorize(o[1], vec3(0., 0., 2.*scol[2]));
    	colorize(o[2], vec3(1.*scol[1], 1.*scol[5], 0.));
    	colorize(o[3], vec3(1.*scol[2], 0., 1.*scol[3]));

		vec3 p = (eye + ray*depth);
		float d_ep=length(p - depth);
		vec3 p_normal = getNormal(p, d_ep*d_ep*EPSILON*0.01);

        vec3 light_dir = -normalize(light-p);
        vec3 reflected_light_dir = reflect(-light_dir, -p_normal);

        float shadow = shadowSample(p, -light_dir);

#		define J .003
		float attenuation = 1./(1. + J*pow( length(light-p), 2.0));
       	attenuation*=max(1., 1.+shadow);

        float ambient = 1.0 - ambientOcculation(p, -ray);
        ambient = pow(max(ambient, 0.), 8.);
        float diffuse = max(0., dot(light_dir, p_normal));
        float lighting = (max(1., diffuse*.3 + ambient*.7) )*attenuation;

        vec3 reflectioncolor = texture(iChannel1, reflect(ray, p_normal)).rrr;
        vec3 texcol = texCube(iChannel0, .1*p, p_normal, 1.0 ).rgb * lighting;

        current_color*=.09;
        final_color = (clamp(mix(current_color+vec3(.0), reflectioncolor*1.1, max(0., 1.+(dot(-p_normal, ray)))), 0., 1.)+current_color*.7)*lighting;
        final_color = final_color + texcol*.1;

        final_color *= max(dot(p_normal,ray),0.);
	} else {
        float d = dot((uv+.5),(uv+.7));
	 	uv -= (uv*d)*.85 + uv*.785;
        final_color=max(vec3(0.), vec3(.2*(-uv.y+.7),.3*(-uv.y+.7),.5*(-uv.y+.7)));
        final_color= mix(final_color, texture(iChannel3, vec2(uv)*.5*mm2(PI*TIMER(600., 0., PI*2.))  ).rbb*.5, .3);
    }

    fragColor = vec4(pow(final_color, vec3(1.3)), 1.);
}
