#version 330 core
in vec2 UV;
out vec4 fragColor;
uniform sampler2D iChannel0; // Noise tex
uniform vec2 iResolution;
uniform float iGlobalTime;

#define ITERMAX 40
const float EPS = 0.01;
vec3 LIGHTPOS = vec3(8.0*cos(1.61*iGlobalTime), 8.0*sin(1.89*iGlobalTime), 100.0+iGlobalTime*4.0);

mat3 rotX(float a){return mat3(1.0,0.0,0.0,0.0,cos(a),-sin(a),0.0,sin(a),cos(a));}
mat3 rotY(float a){return mat3(cos(a),0.0,sin(a),0.0,1.0,0.0,-sin(a),0.0,cos(a));}
mat3 rotZ(float a){return mat3(cos(a),-sin(a),0.0,sin(a),cos(a),0.0,0.0,0.0,1.0);}

float hash(float c){return fract(sin(dot(c,12.9898))*43758.5453);}
float snoise(vec3 x) {
    vec3 p = floor(x);
    vec3 f = fract(x);
	f = f*f*(3.0-2.0*f);
	
	vec2 uv = (p.xy+vec2(37.0,17.0)*p.z) + f.xy;
	vec2 rg = texture2D( iChannel0, (uv+ 0.5)/256.0, -100.0 ).yx;
	return mix( rg.x, rg.y, f.z );
}

float box( vec3 p, vec3 b, float r )
{
  vec3 d = abs(p) - b;
  return min(max(d.x,d.y),0.0) +
         length(max(d.xy,0.0))-r;
}

float tunnel(vec3 p) {
    float am = 4.2;
    float noiseterm = am*(snoise(0.15*(p+iGlobalTime*9.0)))-am*0.5;
    float noiseter2 = 1.5*(snoise(0.5*(p+iGlobalTime*11.0)))-1.5*0.5;
    
    return box(p+(noiseterm-noiseter2)-vec3(0,0,0), vec3(0.5), 0.05);
}

float df(vec3 p) {
    return tunnel(p);
}

vec3 getNormal(vec3 p, float e)
{
	vec3 n = vec3(df(p + vec3(e, 0, 0)) - df(p - vec3(e, 0, 0)),
                  df(p + vec3(0, e, 0)) - df(p - vec3(0, e, 0)),
                  df(p + vec3(0, 0, e)) - df(p - vec3(0, 0, e)));
	return normalize(n);
}
#ifdef VISIBLE_LIGHT
float lit;
#endif
vec4 shade(vec3 p, float rat){
    vec3 normal = getNormal(p, 0.05);
    vec3 lightDir = normalize(LIGHTPOS - p);
    float lf = 0.0;
    #ifdef VISIBLE_LIGHT
    lf = 1.0+2.5*sqrt(lit);
    #endif
    float ct = (lf) * max(0.0, dot(normal, lightDir));
    ct = pow(ct, 1.3)*0.8;
    vec4 colorcand = vec4(0.0);
			if (ct < 0.7) ct = 0.0;
			ct = smoothstep(-0.3, 6.60, ct);
			colorcand = vec4(ct,ct,ct, 1.0);
			colorcand.x = smoothstep(0.11, 0.95, colorcand.x);
			colorcand.y = smoothstep(0.12, 0.85, colorcand.x)+0.001;
			colorcand.z = smoothstep(0.25, 0.70, colorcand.x);
			colorcand *= colorcand;
			colorcand += vec4(vec3(0.002),0.0);
    colorcand += (1.0-rat)*50.0*pow(max(dot(normalize(LIGHTPOS-p),normal),0.0),35.0);
    return pow(colorcand, vec4(0.3))-0.06;
}

vec3 getRefl(vec3 p, vec3 ray){
    for (int i = 0; i < ITERMAX/2; i++) {
        float d = df(p);
        if (d < EPS || 24.0 < d) {
            break;
        }
        p += d *ray;
    }
    return p;
}

vec3 getColor(vec2 uv){
 	vec3 ap = vec3(10.0*sin(0.2*iGlobalTime),4.0*cos(0.3*iGlobalTime),iGlobalTime*4.0);
    vec3 p = ap;
    float brightn = 0.0;
    mat3 shake = rotX(.02*texture2D(iChannel0, vec2(iGlobalTime*0.0064)).x)*
                 rotY(.02*texture2D(iChannel0, vec2(iGlobalTime*0.0051)).y)*
                 rotZ(.02*texture2D(iChannel0, vec2(iGlobalTime*0.0049)).z);
    vec3 ray = normalize(vec3(uv, 0.8)*shake);
    vec3 r = vec3(0.0);    
    
    float m = 0.0;
    float d = 0.0;
    for (int i = 0; i < ITERMAX; i++) {
        d = df(p);
        m += d;
        if (d < EPS || 85.0 < m) {
            break;
        }
        p += d * ray;
    }
    
    float rat = clamp(0.7/log(-0.5+0.1*length(LIGHTPOS-p)), 0.0, 1.0);
    float glow = 2.0/pow(length(LIGHTPOS-p), 0.6);
    
    vec3 c;
    if(m <= 87.0 && !(d > 0.0094 && d < 2.0) ){
		c = brightn+glow+(rat*shade(p, rat)+(1.-rat)*shade(r, rat)).xyz;
    } else {
        c = vec3(0.05);
    }
    return c+.06*hash(dot(p.x,p.y)+iGlobalTime)-0.03;;
}

void main()
{
	vec2 uv = UV - vec2(0.5);
    uv.y *= iResolution.y/iResolution.x;
    
    vec3 c = vec3(0.0);
    c += getColor( uv+vec2(-0.06, 0.2)/iResolution.xy );
    c += getColor(uv);
    c += getColor( uv+vec2(0.2, -0.06)/iResolution.xy);
    c /= 3.0;
    
    float syn = mod(-iGlobalTime, 0.46371);
    syn *= syn*syn*20.0;
    float v=1.5/(1.0+(5.0-syn)*dot(uv,uv));
    vec3 bw = smoothstep(0.0, 0.8, vec3( pow((c.x+c.y+c.z)/3.0, 0.6) ));
    fragColor = vec4(v*bw, 1.0);
}