본문 바로가기
DirectX/[Inflearn_rookiss] Part2: DirectX12

19. Frustum Culling

by 헛둘이 2023. 1. 28.

- 절두체컬링이라고 불리며 최적화기법 중 하나이다.

 

출처 : https://m.blog.naver.com/canny708/221547085908

- 위 사진처럼 카메라가 찍는 범위 안의 물체들만 그려주고, 나머지는 스킵하는 기법

- 레스터라이저 단계에서 시계방향, 반시계방향인지에 따라 한쪽 면을 그려주지 않는 것

 

이 기법은 어떤 점이 평면을 기준으로 안인지, 밖인지를 검사하는 수식을 통해 구현할 수 있다.

 

- ax + by + cz + d = 0 이 식은 어떤 평면을 이루는 점을 나타낸다.

- 이 수식을 만족하면 그 점은 그 평면 안에 있다는 뜻

- 이 수식에서 n(a, b, c)는 노멀벡터(평면과 수직인 벡터)이고, d는 원점에서 평면까지의 거리이다.

 

왜 n(a, b, c)가 노멀벡터인가?

- 임의의 두 점 A(X, Y, Z), B(X', Y', Z')으로 이루어진 벡터 AB가 있다면,

- AB와 n을 내적한 결과가 0이 나온다면 n은 노멀벡터인게 증명된다.

- 실제로 내적해 보면 a(X'-X) + b(Y'-Y) + c(Z'-Z)가 되고, 한 번 더 정리하면

- (aX' + bY' + cZ') - (aX + bY + cZ)가 된다.

 

그런데 위 식에서 ax + by + cz + d = 0 이므로,

ax + by + cz = -d이다.

따라서 -d - (-d)이므로 결과는 0이다 (n은 노멀벡터가 맞다)

 

그렇다면 원점을 O로 잡고 임의의 점 M(X, Y, Z)을 두어 OM벡터를 n(a, b,c)와 내적한다면

그 결과는 aX + bY + cZ가 된다.

 

기하학적 의미로 OM과 노멀 벡터 n을 내적하면 노멀 n은 크기가 0이므로,

벡터 OM * cos𝜽가 되기 때문에 그 결과는 평면과 원점의 직선거리가 된다.

 

다시 ax + by + cz + d = 0에서 ax + by + cz는 내적의 결과가 되고, d는 그 결과에 마이너스를 곱한 값이 되므로,

ax + by + cz + d = 0을 만족하는 점은 그 평면에 속한 점이 되고,

ax + by + cz + d > 0이면 평면보다 더 간 점이고,

ax + by + cz + d < 0이면 평면보다 덜 간 점이다.

 

이 사실을 통해 어떤 점이 평면과 어떤 관계인지 파악할 수 있고,

절두체를 이루는 모든 평면과 비교해서 절두체 안에 있는 점인지 밖에 있는 점인지 판별할 수 있다.

 

void Frustum::FinalUpdate()
{
    // 매 프레임마다 두 행렬이 바뀌니까 FinalUpdate에서 처리
    Matrix matViewInv = Camera::S_MatView.Invert();
    Matrix matProjectionInv = Camera::S_MatProjection.Invert();
    Matrix matInv = matProjectionInv * matViewInv;

    // 투영좌표계의 역행렬과 뷰좌표계의 역행렬을 곱한 값을 곱해줌으로써
    // 투영 좌표계에 있던 물체를 월드좌표로 뿅 이동시킨다.

    vector<Vec3> worldPos =
    {
        // XMVector3TransfromCoord를 쓴 이유는 w요소를 1로 만들어 주기 위함
        ::XMVector3TransformCoord(Vec3(-1.f, 1.f, 0.f), matInv),
        ::XMVector3TransformCoord(Vec3(1.f, 1.f, 0.f), matInv),
        ::XMVector3TransformCoord(Vec3(1.f, -1.f, 0.f), matInv),
        ::XMVector3TransformCoord(Vec3(-1.f, -1.f, 0.f), matInv),
        ::XMVector3TransformCoord(Vec3(-1.f, 1.f, 1.f), matInv),
        ::XMVector3TransformCoord(Vec3(1.f, 1.f, 1.f), matInv),
        ::XMVector3TransformCoord(Vec3(1.f, -1.f, 1.f), matInv),
        ::XMVector3TransformCoord(Vec3(-1.f, -1.f, 1.f), matInv),
    };

    _planes[PLANE_FRONT] = ::XMPlaneFromPoints(worldPos[0], worldPos[1], worldPos[2]); // CW
    _planes[PLANE_BACK] = ::XMPlaneFromPoints(worldPos[4], worldPos[7], worldPos[5]); // CCW
    _planes[PLANE_UP] = ::XMPlaneFromPoints(worldPos[4], worldPos[5], worldPos[1]);
    _planes[PLANE_DOWN] = ::XMPlaneFromPoints(worldPos[7], worldPos[3], worldPos[6]);
    _planes[PLANE_LEFT] = ::XMPlaneFromPoints(worldPos[4], worldPos[0], worldPos[7]);
    _planes[PLANE_RIGHT] = ::XMPlaneFromPoints(worldPos[5], worldPos[6], worldPos[1]);
}

- Frustum에서 절두체에 해당하는 평면을 만들어준다.

 

bool Frustum::ContainsSphere(const Vec3& pos, float radius)
{
    for (const Vec4& plane : _planes)
    {
        // n = (a, b, c)
        Vec3 normal = Vec3(plane.x, plane.y, plane.z);

        // ax + by + cz + d > radius
        if (normal.Dot(pos) + plane.w > radius)
            return false;
    }

    return true;
}

- 위 식을 통해 절두체 밖에 있는지 안에 있는지 검사할 수 있다.

- ax + by + cz는 내적의 결과이고, d는 w요소

 

 

 

 

 

 

'DirectX > [Inflearn_rookiss] Part2: DirectX12' 카테고리의 다른 글

21. Quaternion - 로드리게스 회전  (0) 2023.01.31
20. Quaternion - 짐벌락 현상과 복소수에 대한 개념  (2) 2023.01.30
18. SkyBox  (0) 2023.01.27
17. Normal Mapping  (0) 2023.01.27
16. Lighting 구현  (0) 2023.01.26

댓글