공부/Graphics, DirectX, 포트폴리오 구조

렌더링 파이프라인(Rendering Pipeline)

sudo 2022. 4. 10. 14:44

렌더링 파이프라인을 검색해보면 두가지의 이미지를 발견할 수 있다. 사실 두개의 그림이 같은 내용인데 나는 다르다고 생각했다.

Vertex Processing : Vertex Shader -> Hull Shader -> Tessellation -> Domain Shader -> Geometry Shader

Rasterization : Rasterization Stage

Fragment Processing : Pixel Shader

Output Mering : Output Merger 

 

위의 그림이 DX11기준 세분화한 것이고, 아래 그림에서는 Computer Graphics 기준에서 좀 더 포괄적인 의미의 그림이므로 이런 의미이므로 사실상 같은 내용이다

 

 

입력 조립기 단계

3D 모델 하나를 출력하기 위해선 3D 모델에 대한 정보를 CPU에서 GPU로 넘겨야 한다. 이때 모델을 이루는 최소한의 단위가 정점(Vertex)이다. 삼각형도 정점 3개로 이루어져 있다. 입력 조립기 단계에서는 GPU에서 넘겨받은 정점 정보와 인덱스 정보를 이용해서 삼각형을 구성한다. CPU에서 GPU로 정보를 넘길 때, 정점들에 대한 정보 담아 놓은 자료 구조를 정점 버퍼(Vertex Buffer)라고 하며, 정점들간의 이어져있는 인덱스 정보를 담은 자료 구조를 인덱스 버퍼(Index Buffer)라고 한다. 

 

정점들에 대한 정보만 가지고 어떤 모델을 만들어야 하는지 모르므로, 우리는 Primitive라는 정보를 GPU에 전달해줘야 한다 Primitive Topology는 '위상 구조'라는 의미로, 예를 들면 point list, line list, line strip, triangle list, triangle strip 등이 있다. 예를 들어 입력 조립기 단계에 triangle strip(삼각형 띠) 위상 구조를 넘겨주면 삼각형을 이어 붙여서 모델을 형성 할 것이다.

https://docs.microsoft.com/ko-kr/windows/win32/direct3d9/triangle-strips

실제로 입력 조립기에 Primitive Topology 정보를 Set해주는 함수는 아래와 같다

void IASetPrimitiveTopology(
  [in] D3D11_PRIMITIVE_TOPOLOGY Topology
);

 

정점 처리 단계

1. 월드 변환

로컬 공간(물체의 중심을 기준으로 각 물체마다 갖고 있는 공간)에 존재하는 오브젝트들을 월드 공간이라는 하나의 공간에 모아주는 변환

2. 카메라 변환

쉽게 이야기하면 월드 공간에 배치된 카메라를 원점에서 z+ 방향을 바라보게 다시 바꾸는 변환. 카메라가 이동된 것과 같은 효과를 내기 위해, 카메라가 이동해야 할 변환 행렬의 역행렬을 월드에 배치된 다른 오브젝트들에게 적용하면 될 것 이다.

 

예를 들어, 카메라를 월드 공간에 원점에서 z+ 방향을 바라보기 위해서 (10, 10, 10)만큼 이동하고 45도 z축으로 회전 시켜야 한다면, 뷰 변환에 의해 카메라와 다른 오브젝트들은 (-10, -10, -10)만큼 이동하고, z축 기준 -45도 회전하게 될 것이다

 

월드 변환이 완료됐고, 월드 공간에 배치된 오브젝트들을 (가상의 카메라가 존재한다면) 가상의 카메라로 관찰 할 수 있는 영역을 '카메라 공간' 혹은 '뷰 공간' 이라고 한다. 이때 카메라로부터 무한히 커지는 피라미드 모양을 '뷰 볼륨'이라고 하고, near plane과 far plane에 의해 잘리는 영역를 'View Frustum(절두체)'이라고 부른다. 이때 절두체에 걸쳐져 있는 모델은 절두체 안에 들어온 부분만 그리고 밖으로 삐져 나간 부분은 그리지 않게 되는데 이를 래스터라이저 단계에서 하는 Cliping이라고 한다 

3. 투영 변환

뷰 프러스텀(= 절두체) 내의 물체들을 직육면체 공간(=뷰 볼륨)으로 변환시키는 것을 투영 변환(projection transform)이라 한다. 이러한 투영 변환은 뷰 프러스텀 내의 물체에도 동일하게 적용된다. 변환 후 직육면체 공간을 클립 공간(clip space)이라 한다. 이는 클리핑을 위한 공간이기 때문에 클립 공간이라 불린다. 중요한건 투영 변환은 3D 공간에서 3D 공간으로 변환한 것이지 3D->2D가 아니라는 것이다. 뷰 프러스텀도 3D이고 뷰 볼륨도 직육면체의 3D공간이다. 2D가 될 수 있는건 래스터화기 단계의 원근 나누기에 도달해야 되는 것이다.

 

래스터화기 단계(Rasterization Stage)

 

(이미지 출처 : https://woo-dev.tistory.com/172#comment-5613319345)

 

렌더링 파이프라인중에 래스터화기 단계의 주된 임무는 투영된 3차원 삼각형으로부터 픽셀 색상들을 계산해내는 것이다.

 

래스터화기 단계에서 하는 일은 크게 5가지이다

1. 절단(Cliping)

말 그대로 절두체에서 벗어나거나 걸쳐 있는 도형들은 잘라낸다(Clip)는 의미이다. 위에서 언급했듯이, 투영 변환에서 절두체로 되어 있는 뷰 볼륨 공간에서 직육면체 공간으로 변환 시킨 이유는 Cliping을 더 쉽게 하기 위해서 이며, 그래서 투영 변환이후(원근 나누기는 아직 안된) 공간을 클리핑 공간 혹은 클립 공간(Clip Space)라고 부르는 것이다

 

2. 원근 나누기(Perspective Division)

정점 처리 단계에서 투영 변환 행렬을 곱해준 것 만으로 원근감 적용이 끝난것은 아니다. 래스터라이저에서 원근 나누기를 통해 직육면체의 클립 공간(Clip Space)을 NDC(Normalized Device Coordinate) 좌표 공간으로 변환시켜야 비로소 원근감 적용이 완전하게 된 것이다.

 

원근 나누기는 투영 변환 후 w에 저장되어 있던 z값을 x,y,z 성분에 나눠줌으로써 x,y는 [-1, 1] 범위에, z는 [0, 1] 범위에 존재하도록 한다(범위가 Normalized됐으니 NDC 좌표 공간이라고 부른다). 이제 완전한 원근감이 적용된 것이다. Z값은 이제 깊이 판정에만 쓰인다.

 

3. 후면 선별(Back-face Culling)

https://woo-dev.tistory.com/172#comment-5613319345

카메라가 불투명한 구를 바라보는 상황이라면 이때 n1 법선 벡터를 가진 t1 삼각형은 보이지 않을 것이고, 이때 t1 삼각형은 그려줄 필요가 없다. 이렇게 물체의 후면을 판별하는 작업을 후면 선별(Back-face Culling)이라고 한다.

 

DirectX에선 정점 감기는 방향이 시계 방향으로 감기면 그 면이 앞면이고 반시계 방향을 뒷면이라고 판단한다

https://lipcoder.tistory.com/119?category=843243

 예를 들어 위 삼각형이 v0, v1, v2 순으로 시계 방향으로 감긴다면, v1- v0 벡터와 v2 - v0 벡터를 외적해서 나온 벡터는 위로 뚫고 나오는 방향이다. 따라서 우리가 보고 있는 면이 앞면이다. 반대로 v2, v1, v0 방향으로 감긴다면 우리가 지금 보고 있는 면이 후면이 될것이다.

 

4. 뷰포트 변환(Viewport Conversion)

뷰포트(Viewport)란?

- 렌더 타겟에 렌더링을 하기위해서는 뷰포트의 설정도 필요하다. 뷰포트라는 것은 렌더 타겟의 렌더링 영역에 관한 설정입니다. 렌더타겟에 넓이와 높이, 그리고 깊이값으로 렌더링 영역을 설정할 수 있습니다. 그렇기 때문에 뷰포트는 각 렌더타겟별로 설정합니다. 즉 8개의 렌더타겟이 있으면, 뷰포트 설정 역시 8개를 설정해야 합니다

(출처 : https://vsts2010.tistory.com/518)

 

래스터화 단계에서 Cliping을 하고, 원근 나누기를 수행해서 삼각형 Mesh들이 NDC에 존재한다면, 이 Mesh들이 2차원 이미지를 형성하기 위해 x,y 좌표를 후면 버퍼(렌더 타겟이 후면 버퍼라고 가정)의 2D 직사각형 영역으로 맵핑된다. 이때 이 직사각형 영역이 뷰포트이며, 후면 버퍼의 영역으로 들어왔으니 이제부터 x,y좌표는 픽셀 단위가 된다. 

* 참고로 후면 버퍼는 다른게 아니라 렌더 타겟중 화면에 출력되는 특별한 렌더 타겟일 뿐이다(The back buffer is a special render target that is displayed on the screen - https://stackoverflow.com/questions/38685894/trouble-understanding-render-targets-and-back-buffers-in-monogame)

 

(아래 그림에는 뷰포트가 3D 직육면체로 나와있지만 사실 z값은 이제 깊이 판정에만 쓰이고 2D화면에 출력하게 되는 것이므로 2D로 봐도 될것이다.)

DX기준이라면 윗 방향으로 +y

위 그림처럼 스크린 공간내에 뷰포트가 정의되며, 스크린 공간 전체가 뷰포트가 되어야 할 필요는 없다. 이제 3가지 변환을 거쳐서 뷰포트 변환을 해줘야한다.

 

1. y축 뒤집기

 2. 뷰포트의 너비 조정

원래 NDC공간에서 x,y의 범위는[-1, 1] 였으며 z의 너비는 [0,1]이었으므로 각각의 너비를 2, 2, 1로 나누고 지금 뷰포트의 x,y,z 너비만큼 Scaling해주는 것이다

 

3. 뷰포트를 스크린 공간으로 Translation

(이 행렬은 DirectX 기준으로는 Transpose되어야 Translation이 될듯하다)

위 예시에선 뷰 볼퓸의 중앙으로 물체를 이동하는 변환 행렬을 만든 것이다. 위의 3개의 변환 행렬을 결합한 행렬은 아래와 같다.

(이것도 DirectX 기준으로는 Transpose되어야 할듯. 아래 방정식이 MS Document니까 정확할텐데 이걸 기준으로 하면 Transpose되는게 맞을듯 하다)

이걸 방정식으로 풀어서 쓰면 

X = (X + 1) * Viewport.Width * 0.5 + Viewport.TopLeftX
Y = (1 - Y) * Viewport.Height * 0.5 + Viewport.TopLeftY
Z = Viewport.MinDepth + Z * (Viewport.MaxDepth - Viewport.MinDepth)

이렇게 된다(https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-rasterizer-stage-getting-started)

 

5. 스캔 변환(Scan Conversion)

https://lipcoder.tistory.com/119?category=843243

뷰포트 변환을 거친 후에, 개별적인 삼각형 내부에 존재하는 픽셀들을 채워줘야 한다. 이때 픽셀들의 값들은 정점들이 가지고 있는 정보들(ex. Position, Color, UV, Normal Vector)을 가지고 보간(interpolation)을 통해 정점 내부의 픽셀들의 여러 특성 등을 할당해주는 과정이다. 이때 삼각형 내부의 픽셀들을 fragment라는 예비 픽셀들로 채워준다. 기울기를 통해 선형 보간을 해주는데 이는 하드웨어적으로 구현되어 있는 내용이다. 자세한 수학적인 내용은 아래 블로그에 나와있으니 참고해도 좋을 것 같다(https://woo-dev.tistory.com/172#comment-5613319345)

 

프래그먼트 처리(= 픽셀 셰이더)

화면에 출력할 최종 색상을 결정하는 단계.

 

래스터라이저에서 스캔 변환으로 결정한 프래그먼트라는 예비 픽셀들의 색상을 최종 결정해야한다. 스캔 변환에 색상을 그대로 출력할 수도 있고, 조명, 반사, 그림자를 적용하거나 텍스쳐를 입히는 등 변화를 줘서 최종 색상을 결정할 수 있다.

 

출력 병합

픽셀 셰이더에서 넘겨준 픽셀들을 입력받는다. 깊이 판정을 통해 일부 픽셀들을 폐기할 수도 있고, 픽셀 셰이더에서 결정한 색상들을 블렌드 처리 하기도한다.

 

Reference

https://jidon333.github.io/blog/Rendering-pipeline

 

3차원 물체를 그리기 위한 랜더링 파이프라인 요약

3차원 그래픽스에서 가장 핵심적인 내용으로 랜더링 파이프라인을 뺴놓을 수 없을 것입니다. DirectX와 같은 그래픽 라이브러리를 다루고 셰이더 프로그래밍을 하는 것은 모두 이 랜더링 파이프

jidon333.github.io

https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-rasterizer-stage-getting-started

 

Getting Started with the Rasterizer Stage - Win32 apps

This section describes setting the viewport, the scissors rectangle, the rasterizer state, and multi-sampling.

docs.microsoft.com

https://woo-dev.tistory.com/172#comment-5613319345

 

3 - 래스터화(Rasterization)

책을 보며 개인적으로 공부하는 내용이므로 틀린 부분이 있을 수 있습니다. 자료 및 내용 출처 <게임 프로그래밍을 위한 3차원 그래픽스> 래스터화(Rasterization) 렌더링 파이프라인의 2단계인 래스

woo-dev.tistory.com

https://vsts2010.tistory.com/518

 

[알콜코더의 미리 배워보는 DX11-입문편] 1.튜터리얼01:백버퍼의 설정 #2

참고 소스 : DirectX SDK – DirectX 11 Tutorial 02 위 소스를 기반으로 연재를 진행합니다. 연재외에 자세한 내용은 소스와 DX SDK 문서를 참조하시면 도움이 됩니다.   렌더타겟에 렌더링을 하기위해서

vsts2010.tistory.com