#define MAX_DIST 20.0

float f(in float x, in float z)
{
    float y = cos(x) + cos(z) + cos(x * 8.0) * 0.16;
    return y;
}

vec3 castRay(in vec3 ro, in vec3 rd)
{
    const float maxt = MAX_DIST;
    const float delt = MAX_DIST / 1000.0;
    
    for(float t = 0.0; t < MAX_DIST; t += delt)
    {
        vec3 p = ro + rd*maxt*pow(t/maxt, 1.5);
        
        if (p.y < f(p.x, p.z)) {
            return p;
        }
    }
    
    return ro + rd*maxt;
}

vec3 getNormal(in vec3 p)
{
    float eps = 0.001;
    vec3 n = vec3(f(p.x - eps, p.z) - f(p.x + eps, p.z),
                  2.0*eps,
                  f(p.x, p.z - eps) - f(p.x, p.z + eps) );
    return normalize(n);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec2 uv = fragCoord.xy / iResolution.xy;
    float t = iTime * 0.5;
    
    vec3 ro = vec3(0.0, 2.5 + f(0.0, t), t);
    vec3 rd = vec3(uv.x - 0.5 + sin(t * 0.6) * 0.15, uv.y - 1.0, 0.6);
    rd = normalize(rd);
    
    vec3 hitp = castRay(ro, rd);
    float ndist = distance(ro, hitp) / MAX_DIST;
    vec3 normal = getNormal(hitp);
    
    vec4 baseColor = vec4(0.8, 0.6, 0.8, 1.0);
    vec4 cubeColor = texture(iChannel0, reflect(rd, normal));
    vec4 color = mix(baseColor, cubeColor, 0.5);
        
    vec3 lightDir = normalize(vec3(cos(t) * 0.5, 0.5, sin(t) * 0.5));
    float light = clamp(dot(normal, lightDir), 0.0, 1.0);
    float specular = pow(clamp(dot(reflect(rd, normal), lightDir), 0.0, 1.0), 64.0);
    vec4 fog = pow(ndist, 2.0) * vec4(0.5, 0.5, 1.0, 1.0) + pow(ndist, 4.0);
    fragColor = color * light + specular + fog;
}
