//Модель отражения Фонга
/* состоит из
окружающего освещения, диффузного отражения
(ламбертовское отражение) и зеркального отражения

*/

const int MAX_MARCHING_STEPS = 255;
const float MIN_DIST = 0.0;
const float MAX_DIST = 100.0;
const float PRECISION = 0.001;

float sdSphere(vec3 p, float r, float R, vec3 offset)
{
    return sqrt((R - sqrt(p.x * p.x + p.y * p.y)) * (R - sqrt(p.x * p.x + p.y * p.y)) + p.z * p.z)  - r;
}

float sdFloor(vec3 p) {
  return p.y + 2.;
}

float sdScene(vec3 p) {
  float co = min(sdSphere(p, .4, 1., vec3(0, 0, -2)), sdFloor(p));
 // float co = sdSphere(p, 1., vec3(0, 0, 0));
  return co;
}


float rayMarch(vec3 ro, vec3 rd) {
  float depth = MIN_DIST;

  for (int i = 0; i < MAX_MARCHING_STEPS; i++) {
    vec3 p = ro + depth * rd;
    float d = sdScene(p);
    depth += d;
    if (d < PRECISION || depth > MAX_DIST) break;
  }

  return depth;
}

vec3 calcNormal(vec3 p) {
    vec2 e = vec2(1.0, -1.0) * 0.0005;
    return normalize(
      e.xyy * sdScene(p + e.xyy) +
      e.yyx * sdScene(p + e.yyx) +
      e.yxy * sdScene(p + e.yxy) +
      e.xxx * sdScene(p + e.xxx));
}

mat3 camera(vec3 cameraPos, vec3 lookAtPoint) {
	vec3 cd = normalize(lookAtPoint - cameraPos); // camera direction
	vec3 cr = normalize(cross(vec3(0, 1, 0), cd)); // camera right
	vec3 cu = normalize(cross(cd, cr)); // camera up

	return mat3(-cr, cu, -cd);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  vec2 uv = (fragCoord-.5*iResolution.xy)/iResolution.y;
  vec3 backgroundColor = vec3(0.835, 1, 1);
  vec3 col = vec3(0);

  vec3 lp = vec3(0); // lookat point (aka camera target)
  vec3 ro = vec3(0, 0, 4);

  vec3 rd = camera(ro, lp) * normalize(vec3(uv, -1)); // ray direction

  float d = rayMarch(ro, rd);

  if (d > MAX_DIST) {

      col = backgroundColor;

  } else {

      vec3 p = ro + rd * d; // point on surface found by ray marching
      vec3 normal = calcNormal(p); // surface normal

      // light
      vec3 lightPosition = vec3(cos(iTime), 2, sin(iTime));
      vec3 lightDirection = normalize(lightPosition - p);

      // ambient
      float k_a = 0.7;
      vec3 i_a = vec3(0.7, 0., 0.8);
      vec3 ambient = k_a * i_a;

      // diffuse
      float k_d = 0.5;
      float dotLN = clamp(dot(lightDirection, normal), 0., 1.);
      vec3 i_d = vec3(1., 0., 0.);
      vec3 diffuse = k_d * dotLN * i_d;

      // specular
      float k_s = 0.6;
      float dotRV = clamp(dot(reflect(lightDirection, normal), -rd), 0., 1.);
      vec3 i_s = vec3(1, 1, 1);
      float alpha = 10.;
      vec3 specular = k_s * pow(dotRV, alpha) * i_s;

      vec3 newRayOrigin = p + normal * PRECISION * 2.;
      float shadowRayLength = rayMarch(newRayOrigin, lightDirection); // cast shadow ray to the light source
      if (shadowRayLength < length(lightPosition - newRayOrigin)) ambient *= 0.3; // if the shadow ray hits the sphere, set the diffuse reflection to zero, simulating a shadow


      // final sphere color
      col = ambient + diffuse + specular;

  }

  fragColor = vec4(col, 1.0);
}
