#define PI (atan(1.)*4.)
#define TAU atan(1.)*8.

void lookAt(inout vec3 rd,vec3 ro,vec3 ta,vec3 up){
    vec3 w=normalize(ta-ro),u=normalize(cross(w,up));
    rd=rd.x*u+rd.y*cross(u,w)+rd.z*w;
}

void rot(inout vec3 p,vec3 a,float t){
	a=normalize(a);
	vec3 u=cross(a,p),v=cross(a,u);
	p=u*sin(t)+v*cos(t)+a*dot(a,p);   
}

float map(vec3 p){
	rot(p,vec3(cos(iTime*.3),sin(iTime*.5),.5*sin(iTime*.2)),iTime*.7);
    float r1=.7;
    float r2=.3;
    float r3=.05;
    float n1=1.;
    float n2=.6;
    //float a=.3;
    float a=.5;//floor(30.*fract(iTime*.5))*.1;
    float b=.1;
    p.zx=vec2(atan(p.z,p.x)/PI*n1, length(p.zx)-r1);
    p.xy=vec2(atan(p.x,p.y)/PI*n2, length(p.xy)-r2);
    p.x+=a*p.z;
    p.x=mod(p.x,b*2.)-b;
    return length(p.xy)-r3;
}


vec3 calcNormal(vec3 p)
{
  vec3 n=vec3(0);
  for(int i=0; i<4; i++){
    vec3 e=.001*(vec3(9>>i&1, i>>1&1, i&1)*2.-1.);
    n+=e*map(p+e);
  }
  return normalize(n);
}

float march(vec3 ro, vec3 rd, float near, float far)
{
    float t=near,d;
    for(int i=0;i<70;i++)
    {
        t+=d=map(ro+rd*t);
        if (d<.001) return t;
        if (t>=far) return far;
    }
    return far;
}

vec3 doColor(vec3 p)
{
    return vec3(.7,.5,.3)+cos(p*2.)*.5+.5;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = (fragCoord.xy*2.-iResolution.xy)/iResolution.y;
    vec3 ro = vec3(0,0,3);
    vec3 rd = normalize(vec3(uv,3));
    vec3 ta =vec3(0);
    lookAt(rd,ro,ta,vec3(0,1,0));    
    vec3 col= vec3(0);
	const float maxd=50.;
    float t=march(ro,rd,0.,maxd);
    if(t<maxd)
    {
        vec3 p=ro+rd*t;
        col=doColor(p); 
        vec3 n=calcNormal(p);      
		vec3 lightPos=ro+vec3(2,5,2);
    	vec3 li=lightPos-p;
		float len=length(li);
		li/=len;
		float dif=clamp(dot(n,li),0.,1.);
        col*=max(dif,0.);
        float rimd=pow(clamp(1.-dot(reflect(-li,n),-rd),0.,1.),2.5);
		float frn=rimd+2.2*(1.-rimd);
    	col*=frn*.8;
        col*=max(.5+.5*n.y,.1);
        col*=exp2(-2.*pow(max(0.,1.-map(p+n*.8)/.8),2.));
        col+=vec3(.8,.6,.2)*pow(clamp(dot(reflect(rd,n),li),0.,1.),10.);
    }
    col=pow(col,vec3(1./2.2));
    fragColor.xyz=col;
}
