DirectX 12 [5] The Rendering Pipeline

‍박성령·2025년 4월 10일

컴퓨터 그래픽스

목록 보기
5/5
post-thumbnail

Rendering Pipeline

모니터의 출력되는 화면은 2차원 평면이다. 따라서 3차원의 장면을 평면의 모니터에 출력하기 위해서는 일련의 단계들이 필요하다. 이를 렌더링 파이프라인(Rendering Pipeline)이라고 부른다.

렌더링 파이프라인은 IA 단계부터 VS, RS, OM의 과정을 거쳐 진행된다. 이때 HS와 Tessellator는 꼭 필요한 작업이 아닌 세부 기능을 넣어주는 작업들이므로 생략할 수 있다.


1️⃣ Input Assembler

Input assembler 단계에서는 메모리에서 기하 자료의 정보(vertex, index)를 읽어서 기본 도형(triangle, line 등)을 생성한다.

Vertex

삼각형에서 각 변이 만나는 점에 정점(vertex)이 생긴다. 선의 경우엔 양 끝 점이 정점이 되고 하나의 점인 경우 그 점 자체가 정점이다.

index


모든 물체는 삼각형으로 표현 가능하다. 따라서 다음 사각형은 6개의 정점으로 표현될 수 있다.

Vertex quad[6] = { 
	v0, v1, v2, // Triangle 0 
    v0, v2, v3, // Triangle 1 
};

이때 정점은 중복되서 나타나며, 이 정점들은 전부 좌표 정보를 갖고 있으므로 메모리 요구량이 늘어난다. 또한 하드웨어 처리량이 증가하여 성능이 떨어진다.

이런 삼각형 목록에서 중복 정점들을 제거하는데에 사용되는 것이 인덱스이다.

Vertex v[4] = {v0, v1, v2, v3}; 
UINT indexList[6] = { 
	0, 1, 2, // Triangle 0 
    0, 2, 3 // Triangle 1
};

사각형을 표현하기 위해 vertex 4개를 미리 정의한 후, index를 이용해 이들을 나타낸다.

index를 구성할 땐 normal vector의 방향에 주의해야 한다. 이는 RS 단계의 Backface culling에서 다루게 된다.

Primitive Topology

vertices는 vertex buffer에 bound되어 있다.

  • Point list: 각각 독립적인 점

  • Line strip: 선이 계속 이어지는거 (vertex 연결)

  • Line list: 선이 각각 독립적으로 존재

  • Triangle strip: 삼각형이 이어짐 (인접한 삼각형과 vertex 공유)

  • Triangle list: 각각의 독립적인 삼각형


2️⃣ Vertex Shader

Input Assembler 단계에서 기본 도형들의 정보를 조립한 후에 정점들의 정보가 정점 쉐이더(Vertex Shader) 단계로 넘어가게 된다.

점들을 가져와서 projection 시킬 때, 선들 그리고 반사효과 등을 렌더링 해야한다.
화면에 그려지는 모든 정점들은 VS를 거쳐가게 되며, VS 단계에서는 각 정점에 lighting, transformations, skinning, morphing같은 효과들을 적용한다.

이 단계에서는 3D 공간의 물체를 우리가 보는 2D 화면에 그릴 수 있도록 정점의 좌표를 조정한다. 3D 모델을 그릴 땐, 모델이 실제로 어디에 위치하고(W), 어떤 시점에서 바라보며(V), 어떤 방식으로 투영될지(P)를 고려해야한다. 이 변환해주는 행렬이 WVP 행렬이다.


World 행렬

먼저 3D 모델의 정점은 local space에 있다. 이들을 실제 장면 속에서 어디에 존재할지 결정해야 한다. 또 회전이나 scaling도 적용해야 할 수 있다. 이런 작업을 해주는 것이 World 행렬이다. 이러한 변환 행렬을 Transformation에서 다뤘었다. Transformation

이렇게 나온 World 행렬은 다음과 같다.


View 행렬

우리는 물체를 볼 때, 눈으로 본다. 화면에서 이러한 역할을 해주는 것이 카메라이다. 카메라가 바라보는 위치와 방향이 달라지면, 물체가 보이는 모습도 달라지게 된다. View 행렬은 물체들을 world space에서 view space로 바꿔주는 역할을 한다. 쉽게 말해서 “세상을 움직여서 카메라를 원점에 두는 것“과 같다. 카메라를 기준으로 모든 물체가 상대적으로 어떻게 배치되어 있는지를 계산한다. 따라서 View 행렬은 카메라의 위치, 물체의 위치, up vector만 알면 행렬을 구할 수 있다.

World space에 있는 T가 타깃이고, Q가 카메라의 위치이다. 먼저 w (Z축)를 카메라가 타깃을 바라보는 방향으로 설정한다.

그 다음 ground 방향인 up vector j와 w를 외적 하여, u (X축)를 계산해준다.

그리고 w와 u를 다시 외적하여 v (Y축)를 계산해준다.

아까 카메라를 기준으로 모든 물체가 상대적으로 어떻게 배치 되어있는지를 계산한다고 했는데 이는 모든 물체들을 카메라의 local space로 끌고 온다고 생각할 수 있다. 즉, 카메라가 world space로 가는 W의 역행렬인 를 구해서 적용한다면 View Space를 구할 수 있을 것이다.


Projection 행렬

이제 마지막으로 3D 물체를 우리가 보는 2D 화면에 투영해야 한다. 투영하는 방법중 원근 투영을 선택하여 투영을 진행한다. (원근 투영은 멀리 있는 것은 작아 보이고 가까운 것은 크게 보이는, 현실적인 시점이다.) Projection 행렬은 3D 좌표를 Clip Space로 변환하여, 화면에 그릴 수 있게 만든다.

먼저 모든 물체의 상이 맺히는 가상의 평면인 투영 평면은 상하좌우가 [-1, 1]의 범위를 갖는 지점으로 결정한다. 이 때, 카메라에서 투영 평면까지의 거리가 d이고 화각을 α\alpha로 둔다.

그러면 tan(α/2)=1/dtan(\alpha/2) = 1/d로 둘 수 있다.

그 다음 화면의 종횡비를 맞추기 위해서 종횡비 기호인 r을 다음과 같이 정의한다.

r=W/Hr=W/H, 높이에 대한 폭의 비율이다.

xpx_pypy_p는 화면에 projection 시킨 것을 의미한다.

이제 투영 평면에 위치한 점과 View 공간의 점을 닮은꼴 관계를 이용해서 를 구해보도록 한다.

상하와 좌우의 카메라 시야각이 동일하기 때문에 도 동일한 방식으로 구할 수 있다.


화면의 종횡비를 맞추기 위해서 종횡비의 역수를 곱해주었다.

그런데 이 식엔 한가지 문제가 있다. 행렬 연산의 가장 큰 장점은 모든 변환의 행렬을 미리 곱해둔 행렬을 사용하여 연산을 줄이는 것인데, 변환할 점의 z값이 행렬에 사용되어 변환할 점마다 새로운 행렬을 사용해야 하므로 이가 불가능해진 것이다.

z를 P 행렬이 아니라 연산이 완료된 좌표에서 나눈다면 이를 해결할 수 있을 것이다. 그러면 P 행렬을 다음과 같이 설정할 수 있을 것이다.

이제 거의 마지막 단계이다. 화면에 물체를 그릴땐 카메라의 가장 가까이 있는 물체일수록 나중에 그려야 한다. 카메라와 가까운 평면을 Near plane 카메라와 먼 평면을 Far plane이라 한다. 이 깊이 정보도 포함하기 위해서 P 행렬의 3열을 4열로 옮기고 3열은 깊이 값을 구하는 용도로 변경한다.

깊이 값은 view space의 x축과 y축에 각각 직교하므로 영향을 받지 않아 3열의 1, 2행을 0이다. A, B는 다음과 같이 유도할 수 있다.
View 좌표x,y,z,1{x, y, z, 1}를 P와 곱하면zp=Az+B/zz_p = Az + B / z 가 된다. z의 범위는 [n, f]이고 는 이에 따라 [0, 1]이 된다. 이 점을 이용해 다음과 같이 A와 B를 구할 수 있다.

이렇게 최종적으로 만들어진 P 행렬은 다음과 같다.

이렇게 만든 WVP 행렬을 정점에 곱하면 화면상의 위치로 정확히 위치하게 된다.

설명을 적고나니 이해하기 힘들어 보이는데 다음 글을 참고하면 이해하기 수월할 것이다. 원근 투영이란?


3️⃣ Tessellation

Tessellation(테셀레이션)은 메시의 삼각형들을 더 작은 삼각형으로 세분화하여 새로운 삼각형들을 추가하는 것을 말한다. (정밀하게 표현)

이는 추가적인 옵션으로 꼭 필요한 단계는 아니다.

장점

  1. 카메라에 가까운 삼각형들은 Tessellation을 통해 더 많은 디테일을 추가하고, 멀리 있는 삼각형들은 Tessellation을 하지 않는 level-of-detail(LOD) 기법을 구현할 수 있다. 이렇게 하면 추가된 디테일이 눈에 띄는 부분에만 삼각형을 더 사용하게 된다.
  1. 메모리에는 단순한 low-poly(low-poly, 낮은 삼각형 수) mesh만 저장하고, 필요한 삼각형은 실시간으로 추가함으로써 메모리를 절약할 수 있다.

3.애니메이션이나 물리 연산 등은 단순한 로우 폴리 메시에서 수행하고, 렌더링할 때만 Tessellation된 하이 폴리(high-poly) 메시를 사용함으로써 성능을 최적화할 수 있다.


4️⃣ Clipping

시야 절두체 바깥에 있는 정점들은 렌더링할 이유가 없기에 폐기해주어야 한다.

바라보는 방향에 있는 polygon들을 남겨주고 반대 방향에 있는 polygon들은 지워준다.


5️⃣ Rasterizer

Rasterizer(RS) 단계에서는 정점(vertex) 들로 구성된 Primitive(예: 삼각형)픽셀 단위의 조각(fragment)으로 변환하는 단계이다.

즉, 화면에 실제로 표시될 픽셀 정보를 만들어내는 과정이다.

VS에서 WVP 연산 후, 정점들은 클립 공간에 있다. 이 정점들은 나중에 RS 단계에서 화면 픽셀로 투영되게 되는 것이다!

즉, VS에서 “어디에 있을지” 계산하고, RS에서 “어떻게 보일지” 픽셀 단위로 결정하는 것이다.

RS의 역할은 이처럼 도형을 픽셀 단위로 바꾸고, Backface Culling을 수행하며, Interpolation(보간)을 수행한다.


Backface Culling

위에서 index를 구성할 땐 normal vector의 방향에 주의해야 한다고 했다.

{v1, v2, v3}의 시계 방향 형태로 index가 매겨져 있으면 normal vector의 방향은 앞으로 향하여 우리에게 보이기 때문에 rendering이 된다.

반대로 {v1, v3, v2}의 반시계 방향으로 index가 매겨져 있으면 normal vector의 방향은 뒤로 향하여 rendering하지 않는다.

기본 값은 Clockwise(시계 방향)이고 Rasterizer stage에서 Counter Clockwise(반시계 방향)으로 변경할수 있다.


Interpolation

정점에서 계산된 색상, 텍스처 좌표, 노멀 등 여러 속성들을 픽셀 단위로 자연스럽게 이어주는 작업이 이루어진다.

예: 정점 A의 색은 빨강, B는 파랑이면, 그 사이 픽셀은 보라색처럼 만들어짐



6️⃣ Pixel Shader

Pixel Shader (PS) 단계에서는 pixel의 색상을 결정한다. 화면에 그려지는 각각의 모든 픽셀들이 PS를 거쳐가게 되며, 조명, 반사, 그림자 등의 작업을 수행할 수 있다.


7️⃣ Output Merge

Output Merger (OM) 단계에서는 최종 픽셀을 화면에 출력한다. PS가 생성한 pixel들은 OM 단계로 이동한다. 일부 pixel들은 Depth, Stencil Test에 의해 그려지지 않을 수 있다. 그려질 픽셀들은 back buffer에 기록되며, 기존의 back buffer 내용에 완전히 덮어쓰는 대신 blending 될 수 있다. 투명도같은 효과는 blending을 통해 구현한다.

profile
게임 개발을 좋아하는 개발자입니다.

0개의 댓글