float sdfPlane( vec3 p, vec4 n ){

  return dot(p,n.xyz) + n.w;
}

float sdfSphere(vec3 p, vec3 center, float radius){

    return length(p - center) - radius;
}

// https://iquilezles.org/articles/palettes/
vec3 palette( in float t)
{
    vec3 a = vec3(.9f, .5f, .5f);
    vec3 b = vec3(.5f, .5f, .5f);
    vec3 c = vec3(2.0f, .5f, .7f);
    vec3 d = vec3(.5f, .2f, 1.0f);
    return a + b*cos( 6.28318*(c*t+d) );
}

float sdfAll(vec3 p){

    float displacement = sin(5.0 * p.x + iTime) * sin(5.0 * p.y + iTime) * sin(5.0 * p.z +iTime) * .35;

    float sdf1 = sdfSphere(p, vec3(.5, 0, 0), .65) + displacement;

    vec3 center = vec3(sin(iTime - 3.1415/2.) * .7 -.2 , 0, 0.);
    float sdf2 = sdfSphere(p, center, .7);

    float sdf3 = sdfPlane(p, normalize(vec4(0., 1. , 0., 1.8))) + displacement/30.;


    return min(sdf1, min(sdf2, sdf3));
}

vec3 getNormal(vec3 p){

    vec3 offset = vec3(0.001, 0., 0.);

    float gradientX = sdfAll(p + offset.xyy) - sdfAll(p - offset.xyy);
    float gradientY = sdfAll(p + offset.yxy) - sdfAll(p - offset.yxy);
    float gradientZ = sdfAll(p + offset.yyx) - sdfAll(p - offset.yyx);

    return normalize(vec3(gradientX, gradientY, gradientZ));
}

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

    //origin of rays
    const vec3 ro = vec3(0., 1., 5.);
    //normalized direction of rays
    vec3 rd = normalize(vec3(uv, 0.) - ro);

    //light ray
    vec3 lightRay = normalize(vec3(sin(iTime*2.)*0.5,sin(iTime)*1.5,1.));

    vec3 currentRayPos;
    float totalMarchedDist = 0.;

    vec3 color = vec3(0);
    vec3 fog = vec3(0);

    for(int i = 0; i < 200; i ++)
    {
        //march along ray
        currentRayPos = ro + totalMarchedDist * rd;

        //smallest distance to closest object
        float distToSDF = sdfAll(currentRayPos);
        //=> distance aided raymarching
        totalMarchedDist += distToSDF;


        if(distToSDF < 0.001){

            vec3 normal = getNormal(currentRayPos);

            //calculate diffuse shading
            float diffuseLight =  0.6 * dot(normal, lightRay);

            //calculate specular shading
            float specularLight = dot(reflect(lightRay,normal), normalize(currentRayPos - ro));
            specularLight = pow(specularLight, 9.);

            //filter light values outside [0.0, 1.0]
            specularLight = max(0.0, specularLight);
            diffuseLight = max(0.0, diffuseLight);

            float ambientLight = 0.2;

            float light =  min(diffuseLight+specularLight, 1.) + 0.2;

            color = palette(iTime/5. +  length(currentRayPos + iTime/4.));
            color *= (light );

            break;
        }


        float fogFactor = pow(length(currentRayPos - ro), 2.) / 800.;
        fog = vec3(fogFactor, fogFactor, fogFactor);
    }

    // apply fog
    color -= fog;

    fragColor = vec4(color,1.0);
}
