수업에서 사각뿔이나 정육면체를 출력하기 전, 렌더링 파이프라인의 각 단계에 대해서 이론적으로 간단히 배우고 코드로 넘어갔다.
이 포스팅에서는 수업에서 배운 렌더링 파이프라인의 각 단계에 대해서 자세히 알아볼 예정이다.
다음 포스팅에서는 오브젝트 그리고, 정육면체 묶어보고, 조명, 그림자맵, 애니메이션... 으악 할 게 너무 많아!!!
렌더링 파이프라인의 개요
렌더링 파이프라인은 카메라에 비친 3차원의 장면을 2차원의 이미지로 생성하는데 필요한 일련의 단계이다.
입력 조립기 단계부터 래스터라이저, 출력 병합기까지 거치면 3차원의 공간을 2차원의 이미지로 표현할 수 있게 된다. 여기서 헐 셰이더, 테셀레이터는 생략이 가능하다고 한다.
나는 렌더링할 때 정점 셰이더, 픽셀 셰이더를 만들어서 도형을 렌더링했다.
입력 조립기
입력 조립기 단계에서는 기하 자료(정점, 인덱스 버퍼)를 읽어 기본 도형(삼각형, 선분 등)을 조립하는 단계이다. 그림판에 정육면체를 그리며 어떻게 묶을까 생각하고 인덱스 버퍼를 구성하고 GPU로 보낸 행동도 다 이 단계를 위해서이다.
Primitive Topology
이때 기본 도형 위상 구조(Primitive Topology)를 설정할 수 있는데, 보통 삼각형으로 묶어 TRIANGLELIST로 설정한다. 다양한 위상 구조가 있다.
Vertex, Index
위 그림에서 본 것처럼, GPU는 토폴로지에 따라 정점을 연결한다. 만약, 쿼드의 정점들을 모은 Vertex Buffer를 구성한다면 어떻게 될까?
6개의 정점을 가진 버퍼가 나오게 된다! 대각선을 이루는 정점의 중복이 생기는 것이다! 메모리의 요구량도 늘어나고 그래픽 하드웨어의 처리량도 증가하게 된다! 이 중복 문제를 해결하는 방벙으로, 각 정점의 인덱스를 사용하는 것이다.
이렇게 정점 버퍼를 가리키는 인덱스로 만들게 되면...
아까 쿼드의 6개의 정점을 4개의 정점과 길이가 6인 인덱스 버퍼로 만들 수 있다.
정육면체의 경우는 36개의 정점들을 8개의 정점과 36개의 인덱스 버퍼로 만들 수 있는 것이다!
프로그래머가 정점과 인덱스를 묶어 메쉬를 만들 일은 많이 없겠다만, 정점을 시계 방향으로 묶어야 삼각형의 전면으로 보이게 된다.
{1, 3, 2} (시계방향) 순서대로 인덱스 버퍼에 넣었을 때는 정상적으로 아래 그림처럼 나오겠지만, {1, 2, 3}으로 묶을 경우 삼각형의 후면이 보이게 되어 카메라를 뒤쪽으로 옮기지 않는 이상 아무것도 보이지 않을 것이다.
Vertex Shader
정점 셰이더는 화면에 그려질 모든 정점이 거쳐가는 곳인데, 변환 조명, 변위 매핑 등 수많은 특수 효과를 정점 셰이더에서 수행할 수 있다. 변환 행렬, 라이트 정보 등 GPU 메모리에 담긴 다른 자료들 또한 접근 가능하다.
변환 행렬
원래는 같이 있던 내용인데, 너무 길어서 따로 새 포스팅을 만들었다.
절단
시야 절두체 바깥에 있는 기하 구조를 폐기하는 연산. 시야 절두체는 여섯 개의 평면인데, 각 평면의 양의 공간에 있는 부분은 렌더링하고 음의 공간에 있는 부분은 폐기한다. 이 연산은 하드웨어가 수행해준다고 한다.
래스터화
투영된 3차원 삼각형으로부터 픽셀 색상들을 계산하는 단계이다.
뷰포트 변환
원근 투영변환 후, 정규화된 좌표 공간의 점들은 2차원 x, y 좌표 성분들이 백 버퍼의 한 직사각형 영역으로 변환된다고 하는데, 그 영역이 바로 뷰포트라고 한다.
뷰포트 변환을 마치면 x, y 성분은 픽셀 단위의 값이 된다.
후면 선별
시계 방향이면 전면, 반대면 후면을 판단해준다. 벡터의 외적 연산을 이용한다.
만약 삼각형의 모든 법선이 물체의 바깥쪽을 향한도록 물체가 있다고 가정해보자. 카메라는 후면 삼각형을 그릴 필요가 없다. 이때 렌더링 파이프라인에서 후면 삼각형을 골라 폐기하는 과정이 후면 선별(Backface Culling)이다.
후면 선별을 하면 파이프라인이 처리할 삼각형의 수가 정말 많이 줄어들게 된다.
정점 특성의 보간
뷰포트 변환 후 삼각형을 덮는 각 픽셀들에 대해 보간하는 작업을 거친다. 각 정점의 색이 있다면, 삼각형에서 비어있는 각 픽셀들에 대해서 보간해준다고 한다.
Pixel Shader
프로그래머가 작성하고 GPU에서 실행한다. 단색 출력부터 조명, 반사, 그림자 등등 복잡한 작업을 수행할 수 있다.
Output Merger
출력 병합기! 그 전 포스팅에서도 OMSetRenderTargets 함수로 Back Buffer와 Depth Stencil Buffer를 보내준 기억이 있다. 깊이 혹은 스텐실 판정에 의해 일부 픽셀 단편들이 폐기될 수 있으며, 남은 픽셀들은 후면 버퍼에 기록된다.
으아 진짜... 렌더링 파이프라인 배울 때 졸지 말 걸... 수학 얘기가 나오니 눈뜨고 졸았던 내 자신이 후회가 된다. 솔직히 여기 있는 내용이 정말 모두 이해되지는 않는다. 특히 투영행렬은... 정말 어렵고 이해도 잘 안 되는 것 같다 ㅠ_ㅠ
다음 학기에 배울 때는 조금 더 업그레이드 해서 포스팅을 올려야겠다...
오늘 포스팅은 어쩐지 수업 내용 + 외부 블로그를 거의 따라쓰기 수준으로 인용해서 내가 완벽하게 이해한 건 아닌 것 같지만 말이다... 그래도 구현부를 다시 복습하며 코드상으로도 이해하는 시간을 가져야겠다.
참고
'Direct X' 카테고리의 다른 글
[DX12] 정육면체의 정점을 묶고 렌더링해보자! (0) | 2023.02.27 |
---|---|
[DX12] 로컬 > 월드 > 뷰 > 투영 이론 (1) | 2023.02.27 |
[DX12] 빈 화면을 렌더링해보자! (1) | 2023.02.26 |
[DX12] 장치를 초기화 해보자! (3) (RTV, DSV, Viewport) (0) | 2023.02.26 |
[DX12] 장치를 초기화 해보자! (2) (Command Object, Swap Chain) (0) | 2023.02.13 |