새소식

인기 검색어

게임 개발/그래픽스

[그래픽스] Line-sphere Intersection 을 이용해 3차원 구 그리기

  • -

참고자료:

https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection

  1. 구의 식과 직선의 식을 d에 대해서 풀면 구와 직선의 intersection에 대해 구할 수 있다.
  2. 두 식을 연립방정식의 해를 구하듯이 구할 수 있다.
  3. 식을 정리하면 2차 방정식같은 형태가 나오고, 이를 근의 공식으로 구한다.
  4. ∇나블라(nabla) 를 구한다음 이를 이용해, d를 구한다.
    • ∇>0: 만나는 점이 2개, +- 에 대해 모두 각각 따로 구해야 한다.
    • ∇==0: 만나는 점이 1개
    • ∇<0: 만나지 않는다
    • d 값이 음수면 충돌하지 않은 것

 

위 방식을 그래픽스의 3차원 구 그리기에 활용할 수 있다.

이때는

u : direction of line (a non-zero vector) 의 값을 유닛 벡터로 사용한다.

따라서 ||u||^2 의 값은 1로 단순화 할 수 있다.

 

이 방식으로 ray를 쏴서 구에 hit 하는 지점까지의 거리를 구할 수 있다.

-> d값이 2개라면 작은 값을 선택하면 된다(가까운 쪽에 먼저 닿을거고, 먼쪽은 가까운쪽을 뚫고 나올때 부딪히는 부분일테니까)

 

    class Sphere
    {
    public:
        glm::vec3 center;
        float radius;
        glm::vec3 color; // 뒤에서 '재질(material)'로 확장

        Sphere(const glm::vec3 &center, const float radius, const glm::vec3 &color)
            : center(center), color(color), radius(radius)
        {
        }

        // Wikipedia Line–sphere intersection
        // https://en.wikipedia.org/wiki/Line-sphere_intersection
        Hit IntersectRayCollision(Ray &ray)
        {
            Hit hit = Hit{-1.0f, vec3(0.0f), vec3(0.0f)}; // d가 음수이면 충돌을 안한 것으로 가정

            /*
             * hit.d = ... // 광선의 시작점으로부터 충돌지점까지의 거리 (float)
             * hit.point = ... // 광선과 구가 충돌한 지점의 위치 (vec3)
             * hit.normal = .. // 충돌 지점에서 구의 단위 법선 벡터(unit normal vector)
             */


            // Nabla = (origin - center)^2 - (||origin - center||^2 + radius^2)
            const float nabla = pow(glm::dot(ray.dir, ray.start - this->center),2) - (pow(glm::length(ray.start - this->center), 2) - pow(this->radius, 2));

            // d = -direction * (origin - center) +- sqrt(nabla)
            if (nabla < 0.0f)
            {
                return hit; // 광선이 구와 충돌하지 않음
            }
            else if (nabla == 0.0f)
            {
                hit.d = -glm::dot(ray.dir, ray.start - this->center);
                hit.point = ray.start + ray.dir * hit.d;
                hit.normal = glm::normalize(hit.point - center);
            }
            else
            {
                // 두 값중 작은 값이 더 가까운 충돌 지점
                const float d1 = -glm::dot(ray.dir, ray.start - this->center) + sqrt(nabla);
                const float d2 = -glm::dot(ray.dir, ray.start - this->center) - sqrt(nabla);
                hit.d = glm::min(d1, d2);

                hit.point = ray.start + ray.dir * hit.d;
                hit.normal = glm::normalize(hit.point - this->center);
            }
            return hit;
        }
    };
Contents

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

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