vec3  iPosition;

const mat2 m2 = mat2(1.1,-1.7,1.5,1.6);

float rand(float x)
{
    return fract(sin(cos(x)*124.123)*421.321);
}

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
float noise(vec2 p)
{
    vec2 pm = mod(p,1.0);
    vec2 dist = p-pm;
    float v0=rand(dist.x+dist.y*41.0);
    float v1=rand(dist.x+1.0+dist.y*41.0);
    float v2=rand(dist.x+dist.y*41.0+41.0);
    v0 = mix(v0,v1,smoothstep(0.0,1.0,pm.x));
    v2 = mix(v2,v1,smoothstep(0.0,1.0,pm.x));
    return mix(v0,v2,smoothstep(0.0,1.0,pm.y));
}


float stars(vec3 dir)
{
    vec2 staruv = vec2(atan(dir.x/dir.z)*88.0+iTime*0.1,dir.y*64.0);
    float nbstar = (noise(staruv)+noise(staruv*5.1)+noise(staruv*2.7))*0.3633;
    if (nbstar<0.0) nbstar = 0.0;
    nbstar = pow(nbstar,15.0);
    nbstar*=1.0-abs(dir.y);
    return nbstar;
}


float terrain( vec2 p ) {
    p *= 0.001;
    int i = 0;
    float s = 1.0;
    float t = 0.0;
   for(int i = 0; i<4;i++ ) {
        t += 0.5*(cos(p.x*6.28) + sin(p.y*6.18))*s;
        s *= 0.5 + 0.1*t;
        p = 1.12*m2*p + (t-0.5)*0.2;
    }
    return t*55.0;
}

float intersect(vec3 ro, vec3 rd, float tmin, float tmax, float time ) {
    float t = tmin;
    for ( int i=0; i<160; i++ ) {
        vec3 pos = ro + t*rd;
        float h =  pos.y - terrain(pos.xz);
        if( h<(0.001*t) || t>tmax  ) break;
        t += h * 0.5;
    }
    return t;
}

float calcShadow(in vec3 ro, in vec3 rd ) {
    vec2 eps = vec2( 150.0, 0.0 );
    float height1 = terrain( ro.xz );
    float height2 = terrain( ro.xz );
    float d1 =  5.0, d2 =  50.0, d3 = 150.0;
    float s1 = clamp( 1.0*(height1 + rd.y*d1 - terrain(ro.xz + d1*rd.xz)), 0.0, 1.0 );
    float s2 = clamp( 0.5*(height1 + rd.y*d2 - terrain(ro.xz + d2*rd.xz)), 0.0, 1.0 );
    float s3 = clamp( 0.2*(height2 + rd.y*d3 - terrain(ro.xz + d3*rd.xz)), 0.0, 1.0 );
    return min( min( s1, s2 ), s3 );
}

vec3 calcNormal( in vec3 pos, float t, int j ) {
    float e = 0.005*t;
    vec2 eps = vec2( e, 0.0 );
    float h = terrain( pos.xz );
    return normalize( vec3( terrain(pos.xz-eps.xy)-h, e, terrain(pos.xz-eps.yx)-h ));
}


void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 uv = fragCoord.xy / iResolution.xy;
    vec3 final = vec3(0,0,0); // final color in whoch we mix everything
    vec3 dir = vec3((uv.x-0.5)*iResolution.x/iResolution.y,uv.y-0.5,1.0); //direction to use for stars
    dir.z = (1.0-length(dir)*0.5);
    dir = normalize(dir);
    vec2 xy = -1.0 + 2.0*gl_FragCoord.xy/iResolution.xy;
    vec2 sp = xy*vec2(iResolution.x/iResolution.y,0.75);
    float time = 16.5 + (0.0+iTime-0.0)*0.1;

    // camera
    float cr = 0.18*sin(-0.1*time);
    vec3  ro = 5100.0*vec3(5, 0, (time + 1.0 )*0.2); // ray origin kinda like camera
    vec3  ta =5100.0*vec3(5, 0, (time + 7.0 )*0.2);
    ro.y = terrain( ro.yz) + 60.0 + 30.0*sin(1.0*(time-14.4));
    ta.y = ro.y - 100.0;
    vec3  cw = normalize(ta-ro);
    vec3  cp = normalize( vec3(0.0, 1.0, 0.0) );
    vec3  cu = normalize( cross(cw,cp) );
    vec3  cv = normalize( cross(cu,cw) );
    vec3  rd = normalize( sp.x*cu + sp.y*cv + 1.5*cw );

    // raymarch
    float tmin = 10.0; //near
    float tmax = 3000.0; //far

    //durectional light (sun)
    vec3 lightdir = normalize( vec3(-0.8,0.2,0.5) );

    // background
    float sundir = clamp(0.5 + 0.5*dot(rd,lightdir),0.0,1.0);
    float cho = max(rd.y,0.0);
	vec3 bgcol = mix( mix(vec3(0.2,0.5,0.55), vec3(0.80,0.70,0.20),pow(1.0-cho,3.0 + 4.0-4.0*sundir)),
                          vec3(0.43+0.2,0.4-0.1*sundir,0.4-0.25), pow(1.0-cho,10.0+ 8.0-8.0*sundir) );
    vec3 starscolor=vec3(stars(dir));
    float sundotc = clamp( dot(rd,lightdir), 0.0, 1.0 );

    //moon
    vec2 PosM = vec2(0.3,0.9);
	float moon = smoothstep(0.9,0.93,1.-length(uv-PosM));
	vec2 PosM2 = PosM + vec2(0.015, 0);
	moon -= smoothstep(0.9,0.93,1.-length(uv-PosM2)); //cut part of it
	moon = clamp(moon, 0., 1.7);
	moon += 0.7*smoothstep(0.80,0.99,1.-length(uv-PosM));

    final = bgcol + starscolor + moon; //complete sky

    float t = intersect( ro, rd, tmin, tmax, time );

    if ( t>tmax ) { //if above the horizon (pretty much)
        // clouds
        vec2 sc = ro.xz + rd.xz*(1000.0-ro.y)/rd.y;
        final = mix( final, 0.25*vec3(0.5,0.9,1.0), 0.4*smoothstep(0.0,1.0,texture(iChannel1,0.000009*sc).x) );
    }
    else{
	 	// mountains
        vec3 pos = ro + t*rd;
        vec3 nor = calcNormal( pos, t, 5 );
        vec3 sor = calcNormal( pos, t, 2 );
        vec3 ref = reflect( rd, nor );

        // ground
        final = vec3(0.05,0.05,0.05); //grey
        final *= 0.2 + sqrt( texture( iChannel0, 0.01*pos.xz*vec2(0.5,1.0) ).x *
                           texture( iChannel1, 0.01*pos.xz*vec2(0.5,1.0) ).x );
        vec3 col2 = vec3(1.0,0.2,0.1)*0.01;
        final = mix( final, col2, 0.0 );
        float s = smoothstep(0.8,0.9,nor.y - 0.01*(pos.y-20.0));
        s *= smoothstep( 0.2,0.3,0.11*nor.x+texture(iChannel1, 0.001*pos.xz).x);
        vec3 gcol = 0.13*vec3(0.1,0.2,0); //color of grass
        vec3 ptnor = nor;

        // snow
        s = ptnor.y + 0.04*pos.y - 0.25 ;
        s = smoothstep(0.84, 0.84, s );
        final = mix( final, 0.15*vec3(0.42,0.6,0.8), s);
        nor = mix( nor, sor, 0.5*smoothstep(0.9, 0.95, s ) );

        // lighting
        float amb = clamp( nor.y,0.0,1.0);
        float dif = clamp( dot( lightdir, nor ), 0.0, 1.0 );
        float bac = clamp( dot( normalize( vec3(-lightdir.x, 0.0, lightdir.z ) ), nor ), 0.0, 1.0 );
        float sha = mix( calcShadow( pos, lightdir ), 1.0, 0.0 );
        vec3 Lshadow  = vec3(0.0);
        Lshadow += dif*vec3(11.0,5.00,3.00)*vec3( sha, sha*sha*0.4+0.6*sha, sha*sha*0.8+0.2*sha );
        final *= Lshadow;

        //fog
        final = mix( final, bgcol, 1.0-exp(-0.0000004*t*t) );

    }


    final = pow( final, vec3(0.62) );

	fragColor = vec4(final,1.0);
}