: 3D 공간에서 카메라 시야범위 안에 있는 물체만 렌더링하고 나머지는 렌더링하지 않는 기법으로 드로우콜을 줄여 속도가 빨라진다.
: 투영행렬을 계산할 때 범위의 모양으로 삼각뿔을 자른 모양

ax + by + cz + d =0
- a,b,c 는 평면의 법선 벡터, d는 평면과 원점사이의 거리를 나타내는 값
- 수식을 만족하는 점 a,b,c는 평면 위에 있다.
- ax+by+cz+d > 0 라면, 원점을 기준으로 점이 평면 뒤에 있다.
- ax+by+cz+d < 0 라면, 원점을 기준으로 점이 평면 앞에 있다.
위와 같은 평면의 방정식의 성질을 이용하여 절두체의 여섯면에 대해 한 점이 모두 안에 있다면 절두체 안에있다고 판정할 수 있고, 이를 이용해 Culling을 한다.
//"Frustum.h"
#pragma once
#include "CEntity.h"
enum class FACE
{
F_NEAR,
F_FAR,
F_TOP,
F_BOT,
F_LEFT,
F_RIGHT,
END,
};
class CCamera;
class CFrustum :
public CEntity
{
private:
CCamera* m_Owner;
Vec3 m_ProjPos[8];
Vec4 m_arrFace[(UINT)FACE::END];
public:
bool FrustumCheck(Vec3 _Center);
bool FrustumCheck(Vec3 _Center, float _Radius);
public:
void finaltick();
private:
void SetOwner(CCamera* _Cam) { m_Owner = _Cam; }
public:
CLONE(CFrustum);
CFrustum();
~CFrustum();
friend class CCamera;
};
#include "pch.h"
#include "CFrustum.h"
#include "CCamera.h"
CFrustum::CFrustum()
: m_Owner(nullptr)
, m_ProjPos{}
, m_arrFace{}
{
// 투영좌표계 초기 좌표 설정
// 4 -- 5
// / | /|
// / 7 -/ 6
// 0 -- 1 /
// | / | /
// 3 -- 2
m_ProjPos[0] = Vec3(-1.f, 1.f, 0.f);
m_ProjPos[1] = Vec3(1.f, 1.f, 0.f);
m_ProjPos[2] = Vec3(1.f, -1.f, 0.f);
m_ProjPos[3] = Vec3(-1.f, -1.f, 0.f);
m_ProjPos[4] = Vec3(-1.f, 1.f, 1.f);
m_ProjPos[5] = Vec3(1.f, 1.f, 1.f);
m_ProjPos[6] = Vec3(1.f, -1.f, 1.f);
m_ProjPos[7] = Vec3(-1.f, -1.f, 1.f);
}
CFrustum::~CFrustum()
{
}
void CFrustum::finaltick()
{
Matrix matInv = m_Owner->GetProjInvMat() * m_Owner->GetViewInvMat();
Vec3 vWorld[8] = {};
for (int i = 0; i < 8; ++i)
{
vWorld[i] = XMVector3TransformCoord(m_ProjPos[i], matInv);
}
// WorldSpace 상에서 카메라의 시야를 표현하는 6 개의 평면 제작
// 4 -- 5
// / | /|
// / 7 -/ 6
// 0 -- 1 /
// | / | /
// 3 -- 2
m_arrFace[(UINT)FACE::F_NEAR] = XMPlaneFromPoints(vWorld[0], vWorld[1], vWorld[2]);
m_arrFace[(UINT)FACE::F_FAR] = XMPlaneFromPoints(vWorld[5], vWorld[4], vWorld[7]);
m_arrFace[(UINT)FACE::F_TOP] = XMPlaneFromPoints(vWorld[0], vWorld[4], vWorld[5]);
m_arrFace[(UINT)FACE::F_BOT] = XMPlaneFromPoints(vWorld[2], vWorld[6], vWorld[7]);
m_arrFace[(UINT)FACE::F_LEFT] = XMPlaneFromPoints(vWorld[0], vWorld[7], vWorld[4]);
m_arrFace[(UINT)FACE::F_RIGHT] = XMPlaneFromPoints(vWorld[1], vWorld[5], vWorld[6]);
}
bool CFrustum::FrustumCheck(Vec3 _vWorldPos)
{
// ->
// N dot P + D > 0
for (int i = 0; i < 6; ++i)
{
float dot = _vWorldPos.Dot(Vec3(m_arrFace[i].x, m_arrFace[i].y, m_arrFace[i].z));
if ( dot + m_arrFace[i].w > 0 )
{
return false;
}
}
return true;
}
bool CFrustum::FrustumCheck(Vec3 _Center, float _Radius)
{
// ->
// N dot P + D > 0
for (int i = 0; i < 6; ++i)
{
float dot = _Center.Dot(Vec3(m_arrFace[i].x, m_arrFace[i].y, m_arrFace[i].z));
if (dot + m_arrFace[i].w > _Radius)
{
return false;
}
}
return true;
}
평면의 방정식을 구하기 위해서는 평면위의 점 세개가 필요하다.
이를 쉽게 구하기 위해 World Pos가 카메라의 View, Projection 을 곱해 ndc 좌표계로 이동하는 것을 거꾸로 하여, ndc공간에서의 좌표를 기본 좌표로 잡고, 카메라의 ProjInv, ViewInv를 곱하여 평면의 world좌표를 구한다.
구한 점 3개와 XMPlaneFromPoints함수를 통해 평면의 방정식을 알아낸다.
평면의 방정식의 성질을 이용해서 물체의 WorldPos가 평면의 안쪽에 있는지 바깥쪽에 있는지 구분하여 만약 평면의 바깥쪽에있다면 렌더링하지 않는다.