precision highp float;
uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
uniform sampler2D backbuffer;
#define lightPos vec3(3., 0., 0.)

mat2 rot(float t){return mat2(cos(t), sin(t), -sin(t), cos(t));}
float hash(vec2 uv){return fract(sin(dot(uv,vec2(12.9898, 78.233)))*43758.5453123);}
float noise(vec2 uv){
    vec2 f = fract(uv);
	vec2 i = floor(uv);
	float a = hash(i);
	float b = hash(i + vec2(1., 0.));
	float c = hash(i + vec2(0., 1.));
	float d = hash(i + vec2(1., 1.));
	return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}
vec2 intersect(vec3 p){
    vec2 res = vec2(-1., 20.);
	vec2 obj;
	vec3 p_o = p;

	float sdBox;
	p.y = abs(p.y);
	vec3 q = abs(p-vec3(0., 4., 0.))-vec3(5., .01, 5.);
	sdBox = length(max(q, 0.)+min(max(q.x, max(q.y, q.z)), 0.));
	obj = vec2(1.1, sdBox);
	if(obj.y < res.y)res = obj;

	p = p_o;
	p.x = abs(p.x);
	q = abs(p-vec3(5., 0., 0.))-vec3(.01, 4., 5.);
	sdBox = length(max(q, 0.)+min(max(q.x, max(q.y, q.z)), 0.));
	obj = vec2(1.2, sdBox);
	if(obj.y < res.y)res = obj;

	
	p = p_o;
	p.z = abs(p.z);
	q = abs(p-vec3(0., 0., 5.))-vec3(5., 4., .2);
	sdBox = length(max(q, 0.)+min(max(q.x, max(q.y, q.z)), 0.));
	obj = vec2(1.3, sdBox);
	if(obj.y < res.y)res = obj;
	

	p = p_o;
	p = abs(p)-.2;
	p.xy = rot(.4)*p.xy;
	p = abs(p)-1.;//-1.
	p.yz = rot(.4)*p.yz;

	p.xz = rot(time/10.)*p.xz;

	q = abs(p-vec3(0., 0., 0.))-vec3(1.);
	sdBox = length(max(q, 0.))-length(min(max(q.x, max(q.y, q.z)), 0.))-.12;
	obj = vec2(1., sdBox);
	if(obj.y < res.y)res = obj;

	return res;
}
vec2 rayMarch(vec3 ro, vec3 rd){
	vec2 res = vec2(-1., 20.);
	float t = 0.;
	vec3 ray;
	for(float i = 0.;i < 200.;i++){
		ray = ro + rd*t;
		vec2 obj = intersect(ray);
		if(abs(obj.y) < .01){
			res = obj;
			break;
		}
		if(t > 20.)break;
		t += abs(obj.y)*.5;
	}
	return vec2(res.x, t);
}
vec3 getNor(vec3 p){
	vec2 e = vec2(0., 0.01);
	return normalize(vec3(
				intersect(p+e.yxx).y - intersect(p-e.yxx).y,
				intersect(p+e.xyx).y - intersect(p-e.xyx).y,
				intersect(p+e.xxy).y - intersect(p-e.xxy).y
				));
}
vec3 sideWallCol(vec3 p, bool light){
	vec3 col;
	vec3 mate = vec3(step(hash(floor(p.yz*2.)+floor(time)), .5));
	mate = light?mate:mate*.03;
	vec3 nor = getNor(p);
	float diff = clamp(dot(normalize(lightPos-p), nor), 0., 1.);
	col = mate*diff;
	return col;
}
vec3 wallCol(vec3 p, bool light){
	vec3 col;
	vec3 mate = fract(p.x*.1+time/10.)<.5?vec3(1.):vec3(0.);
	mate = light?mate:mate*.03;
	vec3 nor = getNor(p);
	float diff = clamp(dot(normalize(lightPos-p), nor), 0., 1.);
	col = mate*diff;
	return col;
}
vec3 floorCol(vec3 p, bool light){
	vec3 col;
	vec3 mate = fract(p.z+time/10.)<.5?vec3(1.):vec3(0.);
	mate = light?mate:mate*.03;
	vec3 nor = getNor(p);
	float diff = clamp(dot(normalize(lightPos-p), nor), 0., 1.);
	col = mate*diff;
	return col;
}

vec3 lightCol(vec3 p){
	return vec3(1.);
}
vec3 colHub(float id, vec3 hitPos, bool light){
	vec3 col = vec3(0.);
	if(id == 1.1)col = floorCol(hitPos, light);
	else if(id == 1.2)col = sideWallCol(hitPos, light);
	else if(id == 1.3)col = wallCol(hitPos, light);

	return col;
}

vec3 prismCol(vec3 p, vec3 rd, bool light, vec3 baseCol){
	vec3 col = vec3(0.);
	vec3 norIn = getNor(p);

	//surface reflection
	vec3 rdRefl = reflect(rd, norIn);
	vec2 obj = rayMarch(p+norIn*.5, rdRefl);
	vec3 reflCol = colHub(obj.x, p + rdRefl*obj.y, light);
	float diff = .2-clamp(dot(rd, norIn), 0., .2);

	//refraction
	vec3 rdIn = refract(rd, norIn, 1./1.45);
	vec3 roIn = p - norIn*.02;

	obj = rayMarch(roIn, rdIn);
	vec3 inPos = roIn + rdIn*obj.y;
	vec3 norOut = getNor(inPos);

	//inside reflection & refraction
	vec3 rdOut = refract(rdIn, -norOut, 1.45);
	vec3 roOut = inPos + norOut*.02;
	if(length(rdOut) == 0.){
		rdOut = reflect(rdIn, -norOut);
		vec3 inPos2 = inPos + rdOut*rayMarch(inPos, rdOut).y;
		roOut = inPos2 + rdOut*2.;
	}

	obj = rayMarch(roOut, rdOut);
	vec3 hitPos = roOut + rdOut*obj.y;
	col = colHub(obj.x, hitPos, light);

	col = baseCol*col;
	return col*.5 + reflCol*diff;
}
void main(void) {
	vec2 p = (gl_FragCoord.xy*2.-resolution.xy)/min(resolution.x, resolution.y);

	vec3 ray;
	vec3 ro = fract(time/12.)<.5?vec3(0., -2., 4.5):vec3(0., 0., 4.5);
	ro.xz = rot(time*.1)*ro.xz;
	vec3 rd = normalize(vec3(p, -1.));
	rd.yz = fract(time/12.)<.5?rot(radians(20.))*rd.yz:rot(radians(0.))*rd.yz;
	rd.xz = rot(time*.1)*rd.xz;
	float t = 0.;
	vec3 col = vec3(0.);
	vec2 obj = rayMarch(ro, rd);
	ray = ro + rd*obj.y;
	float tcycle = fract(time/12.);
	bool light = tcycle< .95?true:tcycle<.97?mod(floor(tcycle*400.),2.)==0.?true:false:false;

	int colIndex = int(mod(floor(time/12.), 4.));
	vec3 baseCol = colIndex==0?vec3(1.):colIndex==1?vec3(1., .2, 0.):colIndex==2?vec3(0., .2, 1.):vec3(1., .8, .1);
	if(obj.x != -1.){
		if(obj.x == 1.1)col = floorCol(ray, light);
		else if(obj.x == 1.2)col = sideWallCol(ray, light);
		else if(obj.x == 1.)col = prismCol(ray, rd, light, baseCol);
		else if(obj.x == 1.3)col = wallCol(ray, light);
	}
	col = pow(col, vec3(.4545));
    gl_FragColor = vec4(col, 1.);
}
