카메라의 절두체 영역 밖 물체들을 그릴 필요가 없기 때문에 CPU 단계에서 필터링하는 최적화 기술
평면
평면의 방정식
ax + by + cz + d = 0
normalize(a,b,c) = 노멀 벡터
평면 위의 점
=>
AB 벡터와 normal 벡터 내적 시 0이 나와야 함
(ax'+by'+cz'), (ax+by+cz) => 평면 위이 점이기 때문에 -d를 만족
따라서
d -> 원점에서 평면까지의 거리
(normal 벡터와 평면위의 점으로 가는 벡터와 내적 시 해당 점으로 향하는 벡터의 크기가 나옴)
특정 좌표가 ax + by + cz + d = 0을 만족하지 않을 시
해당 평면보다 앞이나 뒤의 점이라는 것을 알 수 있음
절두체가 가지는 6개의 평면을 가지고 물체들이 절두체 내부에 있는지 판단 가능
=>
물체들이 좌표를 가지고 있긴 하지만 실제 부피가 어떨지는 알 수 없음
평면에 일부가 걸쳐 있는 물체가 표현되지 않게 됨
=>
물체를 대략적으로 구체처럼 생겼다고 가정하고 r을 이용 (추후 좀더 세밀한 추정 필요)
평면을 구하기 위해서는 최소 3개의 점을 필요로 함
투영 공간에서 만들어질 절두체의 좌표를 역변환 행렬을 사용하여 월드 공간 좌표로 변환
Frustum 클래스
enum PLANE_TYPE : uint8
{
PLANE_FRONT,
PLANE_BACK,
PLANE_UP,
PLANE_DOWN,
PLANE_LEFT,
PLANE_RIGHT,
PLANE_END
};
class Frustum
{
public:
void Frustum::FinalUpdate();
bool ContainsSphere(const Vec3& pos, float radius);
private:
array<Vec4, PLANE_END> _planes;
};
FinalUpdate 함수가 불릴 시 절두체의 평면들이 어떻게 구성될지 계산
평면들을 가지고 특정 좌표가 절두체 내부에 있는지 판단하는 ContainsSphere 함수를 정의
void Frustum::FinalUpdate()
{
...
Matrix matInv = matProjectionInv * matViewInv;
vector<Vec3> worldPos =
{
::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]);
_planes[PLANE_BACK] = ::XMPlaneFromPoints(worldPos[4], worldPos[7], worldPos[5]);
_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]);
}
view space에서 wolrd space로 역변환 하는 행렬을 matInv에 저장
XMVector3TransformCoord 함수를 통하여 view space에서 절두체의 좌표들을 world로 변환
XMPlaneFromPoints 함수로 좌표 3개로 평면을 계산
bool Frustum::ContainsSphere(const Vec3& pos, float radius)
{
for (const Vec4& plane : _planes)
{
...
if (normal.Dot(pos) + plane.w > radius)
return false;
}
return true;
}
ax + by + cz + d > radius 일 경우 평면 밖에 있기 때문에 false 반환
DX12에서 BoundingFrustum struct를 지원
camera 클래스 수정
void Camera::FinalUpdate()
{
...
_frustum.FinalUpdate();
}
void Camera::Render()
{
...
for (auto& gameObject : gameObjects)
{
if (gameObject->GetMeshRenderer() == nullptr)
continue;
if (gameObject->GetCheckFrustum())
{
if (_frustum.ContainsSphere(
gameObject->GetTransform()->GetWorldPosition(),
gameObject->GetTransform()->GetBoundingSphereRadius()) == false)
{
continue;
}
}
gameObject->GetMeshRenderer()->Render();
}
}
카메라가 frustum을 들고 프레임이 끝날 때마다 절두체의 평면을 계산
그리고 게임 오브젝트들을 렌더링할 때 체크하여
절두체의 내부를 만족하는 경우만 렌더
skybox의 경우 절두체 포함 여부와 상관 없이 렌더 되야 하기 때문에
GetCheckFrustum 시 false를 반환할 수 있도록 구현