- 절두체컬링이라고 불리며 최적화기법 중 하나이다.
- 위 사진처럼 카메라가 찍는 범위 안의 물체들만 그려주고, 나머지는 스킵하는 기법
- 레스터라이저 단계에서 시계방향, 반시계방향인지에 따라 한쪽 면을 그려주지 않는 것
이 기법은 어떤 점이 평면을 기준으로 안인지, 밖인지를 검사하는 수식을 통해 구현할 수 있다.
- 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 |
댓글