#define PI 3.14159
#define EPSILON 0.0001
#define MAX_DIST 100.0

float rand2(vec2 co) {
    float a = 12.9898;
    float b = 78.233;
    float c = 43758.5453;
    float dt = dot(co.xy ,vec2(a,b));
    float sn = mod(dt,3.14);
    return fract(sin(sn) * c);
}

float noise(vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);

    float h1 = rand2(i)*sin(iTime+100000.0*rand2(i));
    float h2 = rand2(i + vec2(1.0, 0.0))*sin(iTime+100000.0*rand2(i + vec2(1.0, 0.0)));
    float h3 = rand2(i + vec2(1.0, 1.0))*sin(iTime+100000.0*rand2(i + vec2(1.0, 1.0)));
    float h4 = rand2(i + vec2(0.0, 1.0))*sin(iTime+100000.0*rand2(i + vec2(0.0, 1.0)));

    vec2 u = smoothstep(0.0, 1.0, f);

    return mix(mix(h1, h2, u.x), mix(h4, h3, u.x), u.y);
}

float fbm(vec2 st) {
    float value = 0.0;
    float amplitude = 1.2;
    for (int i=0; i<6; i++) {
        value += amplitude*noise(st);
        st *= 2.0;
        amplitude *= 0.5;
    }
    return value;
}

float map(vec3 p) {
    float h = fbm(p.zx*0.2);
    return p.y-h;
}

float trace(vec3 o, vec3 r) {
    float t = 0.1;
    for (int i = 0; i < 64 ; i++) {
        vec3 p = o + r*t;
        float d = map(p);
        if (d < 0.001*t || d > MAX_DIST) {
            return t;
        }
        t += d*0.9;
    }
    return t;
}

float shadow(vec3 o, vec3 r) {
    float t = 0.0;
    float k = 16.0;
    float res = 1.0;
    for (int i = 0; i < 32 ; i++) {
        vec3 p = o + r*t;
        float d = map(p);
        if (d < 0.001*t) {
            return 0.0;
        }
        t += d*0.9;
        res = min(res, k*d/t);
    }
    return res;
}

vec3 normal(vec3 p) {
    return normalize(vec3(
        map(vec3(p.x + EPSILON, p.y, p.z)) - map(vec3(p.x - EPSILON, p.y, p.z)),
        map(vec3(p.x, p.y + EPSILON, p.z)) - map(vec3(p.x, p.y - EPSILON, p.z)),
        map(vec3(p.x, p.y, p.z  + EPSILON)) - map(vec3(p.x, p.y, p.z - EPSILON))
    ));
}

vec3 textur(vec3 p) {
    return vec3(0.4,0.4,1.0)*(1.5+p.y);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    vec2 uv = 2.0*fragCoord.xy/iResolution.xy - vec2(1.0);
    uv.x *= iResolution.x/iResolution.y;

    float speed = 5.0;
    float day = 0.5+0.5*sin(0.7*iTime);
    float y = -1.0*cos(0.7*iTime);

    vec3 light = vec3(100.0*y, 40.0*(day-0.5), 100.0+speed*iTime);

    vec3 r = normalize(vec3(uv, 1.0));
    vec3 o = vec3(0.0, 3.0, speed*iTime);
    float t = trace(o, r);

    vec3 c;

    vec3 lightcolor = vec3(0.5-abs(0.5-day),0.5-(1.0-day),1.0-(1.0-day)*2.0);

    if (t > MAX_DIST) {
        c = lightcolor+vec3(0.7)*(0.5-r.y);
        c *= 0.2+day;

        float sun = 1.0-smoothstep(0.1, 0.3, length(uv - vec2(y, day-0.5)));
        c += sun*0.8*vec3(1.0,1.0,1.0);

        float star = 1.0-smoothstep(0.0, 0.1, length(fract(uv*10.0)+rand2(floor(uv*10.0))-uv*vec2(0.3,1.0)+vec2(-0.8,0.0)));
        c += (1.0-smoothstep(0.0, 0.2, day))*star*vec3(1.0);
    } else {
        vec3 p = o+t*r;
        vec3 n = normal(p);

        vec3 r = r-2.0*dot(r,n)*n;

        float specular = pow(dot(r,normalize(light-p)),40.0);

        float s = shadow(p, normalize(light-p));

        vec3 col = textur(p);

        c = col*vec3(0.2*(0.5+day)+clamp((dot(n,normalize(light-p))),0.0,1.0)*s)+specular*lightcolor*(0.2+day);

    }

    fragColor = vec4(c, 1.0);
}