새소식

인기 검색어

게임 개발/그래픽스

[그래픽스] 그림자

  • -

삼각형 두개로 사각형 그리기

class Square : public Object
{
public:
    Triangle triangle1, triangle2;

    Square(vec3 v0, vec3 v1, vec3 v2, vec3 v3)
        : triangle1(v0, v1, v2), triangle2(v0, v2, v3)
    {
    }

    virtual Hit CheckRayCollision(Ray &ray)
    {
        auto hit1 = triangle1.CheckRayCollision(ray);
        auto hit2 = triangle2.CheckRayCollision(ray);

        if (hit1.d >= 0.0f && hit2.d >= 0.0f)
        {
            return hit1.d < hit2.d ? hit1 : hit2;
        }
        else if (hit1.d >= 0.0f)
        {
            return hit1;
        }
        else
        {
            return hit2;
        }
    }
};
Raytracer(const int &width, const int &height)
    : width(width), height(height)
{
    auto sphere1 = make_shared<Sphere>(vec3(0.0f, 0.0f, 0.6f), 0.4f);

    sphere1->amb = vec3(0.2f, 0.0f, 0.0f);
    sphere1->dif = vec3(1.0f, 0.1f, 0.1f);
    sphere1->spec = vec3(1.5f);
    sphere1->alpha = 50.0f;

    this->sphere = sphere1; // GUI 연결하기 위해 보관

    objects.push_back(sphere1);

    auto ground = make_shared<Square>(vec3(-2.0f, -1.0f, 0.0f), vec3(-2.0f, -1.0f, 4.0f), vec3(2.0f, -1.0f, 4.0f), vec3(2.0f, -1.0f, 0.0f));
    ground->amb = vec3(0.2f);
    ground->dif = vec3(0.8f);
    ground->spec = vec3(1.0f);
    ground->alpha = 50.0f;
    objects.push_back(ground);

    light = Light{{0.0f, 1.0f, 0.2f}}; // 화면 뒷쪽
}

그림자 구현

  1. 시점에서 ray를 쏴서 바닥에 닿는데,
  2. 이때 부딪힌 지점에서 조명 방향으로 ray를 하나 더 쏴줌. (이것을 Shadow ray 라고 함)
  3. 충돌 지점에서 Shadow ray를 쏠때, 정확히 충돌지점에서 다시 광원쪽으로 Shadow ray를 쏘게 되면 floating point 수치 오차 때문에 원래 충돌했던 물체와 또 부딪혀서 빛이 막힐 수 있다. 따라서 충돌지점에서 아주 작은 값 만큼 움직여서 거기서 shadow ray 를 쏜다.
  4. 이 ray가 광원까지 도달하는지 안하는지 판단한다.
  5. 만약 도달하지 못한다면 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);
}
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.