개발하는 리프터 꽃게맨입니다.
[그래픽스] 깊이 구현 본문
동일한 크기의 상자를 Z값이 변경해서 배치해봤습니다.
텍스처를 입혀볼까요?
멀리있는 물체는 가까이 있는 물체에 가려져야할텐데
뒤에 있는 물체가 앞에 있는 물체를 뚫고 나오고 있습니다.
그 이유는 오브젝트 사이에 그려지는 순서가 정해져있지 않기 때문입니다.
해결 방법은 두 가지 입니다.
1. Z값을 기준으로 오브젝트를 정렬하여, Z값이 큰 것부터 먼저 그린다. (깊이 정렬)
2. '깊이' 라는 개념을 추가하여, 그리고자 하는 스크린 픽셀에 자기보다 깊이가 얕은 픽셀이 존재한다면, 픽셀을 찍지 않는다. (Z-Buffer 알고리즘)
저는 두 번째 방법을 사용하겠습니다.
이런 방법을 Depth Buffer, Z-Buffer 알고리즘 등으로 부릅니다.
이때까지 설계했던 원근투영 행렬을 보겠습니다.
최종적으로 얻은 클립좌표계의 z값은, 카메라에서 정점 사이의 거리를 의미합니다.
해당 값은 NDC 좌표계로 변환하면서, 무조건 1로 변합니다.
깊이에 대한 정보를 사용하려면, 계산 과정에서 사용하지 않는 하나의 행을 이용하는 것을 고려할 수 있습니다.
앞으로 카메라~정점거리를 w라고 하겠습니다.
그러기 위해서 NDC 평면에 깊이에 대한 개념을 첨가하면 위와 같은 공간이 됩니다.
카메라의 옆쪽에서 보면 NDC 평면이 위와 같이 확장되는 것으로 이해할 수 있습니다.
근평면의 w값을 n이라고 하고
원평면의 w값을 f라고 해봅시다.
근평면의 뷰 좌표 (x, y, -n, 1) 을 클립좌표로 변환시켰을 때,
(x', y', -n, n) 으로 변환되어야 합니다.
최종적으로 모든 좌표를 거리인 n으로 나눴을 때
z값은 -1 로 변환되어야 하기 때문입니다.
원근 투영행렬을 다시 계산합니다.
x와 y값은 깊이에 영향을 줄 수 없으므로 e1 e2 둘 다 0입니다.
행렬 곱 연산을 통해서 2개의 방정식을 만들 수 있습니다.
f 에 대한 식도 위와 동일한 논리로 구했습니다.
n, f는 상수고 미지수는 2개이므로
e3, e4 값을 구할 수 있습니다.
최종적인 원근 투영 + 깊이 처리 행렬을 만들었습니다.
근평면과 원평면 속성을 추가합니다.
원근 투영 행렬을 수정해줍니다.
최종적으로 변환된 Z값에는
[-1, 1]의 범위를 가지는 깊이값이 저장되어 있습니다.
그렇다면 깊이 값을 어떻게 이용하냐
먼저 스크린 크기만큼 float 버퍼를 만들어줍니다.
그 다음 스크린 버퍼를 초기화 해줍니다.
깊이 최대값이 1이기 때문에 초기화 변수는 1만 초과하면 됩니다.
깊이 테스트를 위한 판단식을 세워줍니다.
만약, 내가 그릴려고 하는 픽셀의 깊이값이 나보다 크다?
그러면 그 자리에 픽셀을 찍고, 깊이값을 갱신합니다.
아닐 경우에는 픽셀을 찍지 않습니다.
픽셀을 찍는 것은 생각보다 느린 작업이기 때문에, 깊이 값을 비교하는 것만으로 쓸데없는 렌더링을 줄이고 빠르게 렌더링을 수행할 수 있게 됩니다.
텍스처 매핑 코드에 깊이 테스트를 추가했습니다.
깊이 값은 보간을 수행해서 얻어야하고
깊이 값 역시 원근 투영 전 정점을 기준으로 보간을 수행해야하기에, 원근 보정 보간을 수행합니다.
스왑체인 (더블버퍼링) 코드에
깊이를 다시 매우 큰 값으로 초기화해주는 코드를 추가합니다.
결과
깊이 버퍼 있는거 없는거 차이
'컴퓨터 그래픽스' 카테고리의 다른 글
[그래픽스] 삼각형 클리핑 (1) | 2024.08.19 |
---|---|
[그래픽스] 가시부피와 절단 (0) | 2024.08.15 |
[그래픽스] 3D 카메라 설계 수정, 원근법 구현 (0) | 2024.08.12 |
[그래픽스] 백 페이스 컬링 (0) | 2024.08.11 |
[그래픽스] 가볍게 구현해본 3D 와이어 프레임 랜더링 (0) | 2024.08.11 |