이때 부딪힌 지점에서 조명 방향으로 ray를 하나 더 쏴줌. (이것을 Shadow ray 라고 함)
충돌 지점에서 Shadow ray를 쏠때, 정확히 충돌지점에서 다시 광원쪽으로 Shadow ray를 쏘게 되면 floating point 수치 오차 때문에 원래 충돌했던 물체와 또 부딪혀서 빛이 막힐 수 있다. 따라서 충돌지점에서 아주 작은 값 만큼 움직여서 거기서 shadow ray 를 쏜다.
이 ray가 광원까지 도달하는지 안하는지 판단한다.
만약 도달하지 못한다면 ambient 컬러만 반영해주고 diffuse, specular는 적용되지 않도록 하면 된다.
// 광선이 물체에 닿으면 그 물체의 색 반환
vec3 traceRay(Ray &ray)
{
// Render first hit
const auto hit = FindClosestCollision(ray);
Ray shadowRay = Ray{hit.point, glm::normalize(light.pos - hit.point)};
// floating point 계산 오차 때문에 shadowRay가 물체 안에 있을 수 있음
// 그래서 아주 작은 값 만큼 물체 밖으로 빼줌
shadowRay.start += shadowRay.dir * 1e-4f;
// shadowRay가 광원까지 갈 때 물체에 닿으면 빛이 가려진 것이므로 ambient만 반환
// 이때 광원과 shadwRay 사이에 다른 물체가 있으면 그 물체에 의해 가려진 것이므로 ambient만 반환
if (hit.d >= 0.0f)
{
if (FindClosestCollision(shadowRay).d > 0.0f && FindClosestCollision(shadowRay).d < glm::distance(light.pos, hit.point))
{
return hit.obj->amb;
}
// Diffuse
const vec3 dirToLight = glm::normalize(light.pos - hit.point);
const float diff = glm::max(dot(hit.normal, dirToLight), 0.0f);
// Specular
const vec3 reflectDir = 2.0f * dot(hit.normal, dirToLight) * hit.normal - dirToLight;
const float specular = glm::pow(glm::max(glm::dot(-ray.dir, reflectDir), 0.0f), hit.obj->alpha);
return hit.obj->amb + hit.obj->dif * diff + hit.obj->spec * specular;
}
return vec3(0.0f);
}