#define IT 64
#define MAX 10.0
#define EP 1e-3
#define EPN 1e-2
#define CS .xzy

#define E 2.71828182845904523536028747135266249775724709369995

vec3 rgb2hsv(vec3 c)
{
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

vec3 hsv2rgb(vec3 c)
{
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

//Uncomment to only see AO
//#define AMBONLY

#define aoc 12
#define aos 0.109

vec3 aoOffsets[aoc];

vec3 amb(vec3 dir) {
    
    vec3 skyBase = vec3(0.6, 0.8, 0.9)*(1.0+0.6*dot(dir, vec3(0.0,0.0,-1.0)));
    float sunStr = pow(max(1.05* dot(dir, normalize(vec3(1.0,1.0,1.0))),0.0),6.0);
	vec3 skySun = vec3(0.5,0.5,0.2)*sunStr;
    
    skyBase = rgb2hsv(skySun*5.0+skyBase);
    skyBase.x += iTime/5.0;
    skyBase.y =(skyBase.y +1.0)/2.0;
    skyBase = hsv2rgb(skyBase);
    
    return skySun+skyBase;
    
}

float sphere(vec3 pos, float radius)
{
    return length(pos) - radius;
}

float box(vec3 pos, vec3 size)
{
    return length(max(abs(pos) - size, 0.0));
}

float terrain(vec2 pos) {
    return (
        texture(iChannel0,pos/15.0).r/3.0-texture(iChannel1,pos/64.0).g/5.0)
        -(pow(length(pos.xy),2.0)*0.003)
        +sin(-iTime+length(pos)*5.0)/10.0
        
        ;
        ;
}


float dist(vec3 pos) {
    
    
    return max(min(
        max(sphere(pos,0.5), -sphere(vec3(0.5)-pos,0.5)),
        -(terrain(pos.xy)-pos.z)
        ),min(-sphere(-pos,0.48),pos.z-0.2));
    
}


float AO(vec3 pos) {
    float hit;
    float total = float(aoc);
    
    for (int i=0; i<aoc; i++) {
        float d = dist(pos+aoOffsets[i]);
        if (d>0.0) {
            hit+=d;
        }
    }
    
    //float total = 3.0*3.0*3.0;
    //for (int i=-1; i<=1; i++) {
    //    for (int j=-1; j<=1; j++) {
    //        for (int k=-1; k<=1; k++) {
    //            float d= dist(pos+vec3(i,j,k)*0.1);
    //            if (d>=0.0) {
    //                hit+=d;
    //            }
    //                 }
    //    }
    //}
    return max(min(hit*32.0/total,1.0),0.0);
}

vec3 scan(vec3 pos, vec3 dir) {
    
    float totalDistance=0.0;
    for (int i=0; i<IT; i++) {
        float rayDist = dist(pos);
        totalDistance+=rayDist;
        if (abs(rayDist)<=EP) {
            
           #ifdef AMBONLY
            return vec3(AO(pos));
            #endif
            
            if (pos.z<0.0) {
            //return amb(reflect(dir, vec3(0.0,0.0,1.0)));
            }
            
            vec2 eps = vec2(0.0, EPN);
			vec3 normal = normalize(vec3(
    		dist(pos + eps.yxx) - dist(pos - eps.yxx),
    		dist(pos + eps.xyx) - dist(pos - eps.xyx),
    		dist(pos + eps.xxy) - dist(pos - eps.xxy)));
            
            
            
            vec3 sunColor = vec3(1.0,0.8,0.4);
            vec3 sunVector = normalize(vec3(1.0,1.0,1.0));
            float sunAtten = max(0.0, dot(sunVector,normal));
            
            vec3 ambientColor = vec3(0.6,0.8,1.0);
            float ambientAtten = AO(pos);
            
            vec3 reflDir = reflect(dir, normal);
            sunAtten+=pow(max(dot(reflDir, sunVector),0.0),16.0)*ambientAtten;
            
            //return vec3(ambientAtten);
            
            vec4 fog = vec4(0.6, 0.8, 0.9,1.0-1.0/pow(E,totalDistance*0.05));
            fog.a=0.0;
            
            vec3 light = sunColor*sunAtten + ambientColor*ambientAtten;
            vec3 world = mix(texture(iChannel1, pos.xy/7.0).rgb,texture(iChannel1,pos.xy).rgb,0.5)*light;
            
            vec3 finalColor = mix(world, fog.rgb,fog.a);
            
            finalColor = rgb2hsv(finalColor);
            finalColor.x -= iTime;
            finalColor.x += totalDistance*3.0+ length(pos+vec3(cos(iTime)*20.0,sin(iTime)*20.0,0.0));
            finalColor = hsv2rgb(finalColor);
            
            return finalColor ;//*texture(iChannel2, normal).rgb;
        }
        if (totalDistance>=MAX) {
            return amb( reflect(dir, vec3(0.0,0.0,1.0)));
        }
        pos+=dir*(rayDist);
    }
    
    if (dir.z<0.0) {
    	return amb( reflect(dir, vec3(0.0,0.0,1.0)));
    }
    return amb(dir);
}

void mainImage(out vec4 fragColor,in vec2 uv){
    uv = (uv-iResolution.xy/2.0)/iResolution.y;
    float a = iTime*0.125;
    
    

aoOffsets[0]=vec3(	aos*0.0			,aos*-0.525731		,aos*0.850651);
aoOffsets[1]=vec3(	aos*0.850651	,aos*0.0				,aos*0.525731);
aoOffsets[2]=vec3(	aos*0.850651	,aos*0.0				,aos*-0.525731);
aoOffsets[3]=vec3(	aos*-0.850651	,aos*0.0				,aos*-0.525731);
aoOffsets[4]=vec3(	aos*-0.850651	,aos*0.0				,aos*0.525731);
aoOffsets[5]=vec3(	aos*-0.525731	,aos*0.850651		,aos*0.0);
aoOffsets[6]=vec3(	aos*0.525731	,aos*0.850651		,aos*0.0);
aoOffsets[7]=vec3(	aos*0.525731	,aos*-0.850651		,aos*0.0);
aoOffsets[8]=vec3(	aos*-0.525731	,aos*-0.850651		,aos*0.0);
aoOffsets[9]=vec3(	aos*0.0			,aos*-0.525731		,aos*-0.850651);
aoOffsets[10]=vec3(	aos*0.0			,aos*0.525731		,aos*-0.850651);
aoOffsets[11]=vec3(	aos*0.0			,aos*0.525731		,aos*0.850651);
	

    
    //vec3 pos = vec3(cos(a/4.0+sin(a/2.0)),cos(a*0.2),sin(a*0.31)/4.5+1.0)*3.0;
    vec3 pos = vec3(cos(iMouse.x/64.0),sin(iMouse.x/64.0),iMouse.y/64.0+0.3);
    vec3 on = vec3(1.0,uv.x,uv.y);
    vec3 n = pos;

    //n = normalize(pos + (0.1*vec3(cos(a*2.3),cos(a*2.61),cos(a*1.62))));
	vec3 crossRight = normalize( cross(n,vec3(0.0,0.0,1.0)));
	vec3 crossUp = normalize(cross(n, crossRight));
	n = n*(0.5+sin(length(uv-0.5)*5.0+iTime)/5.0) + crossRight*uv.x + crossUp*uv.y;
    
    fragColor.rgb = scan(pos,-normalize(n)).rgb;
}
