Search
Duplicate

렌더링 파이프라인(Rasterization)

파이프라인이란?

여러 명령어가 중첩되어서 프로그램이나 하나의 작업을 실행하게 도와주는 과정(연산들의 집합)이다. 한 사이클 안에 들어가야 해서 과정들이 많기 때문에 한사이클이 복잡해지거나 사용자 입장에서 어려워질수 있는 단점이 존재한다.

렌더링 파이프라인(레스터라이제이션)

[Input Assembler]

3차원 모델 하나를 3차원 세상에 나타내기 위해서는 가장 먼저 해줘야 할것은 무언인가?
여기서 모델은 점(vertex)로 이루어져 있다. 이것을 우리는 폴리곤(점들의 집합)이라고 한다.
주로 게임에서는 삼각형을 가지고 3D 폴리곤을 정의하는데, 이떄 이 정점데이터들을 운반하는 자료구조를 우리는 Vertex Buffer라고 합니다.
정점 버퍼와 같이 등장하는 용어로 Index buffer라는 것이 있다.
인덱스 버퍼는 쉽게 생각하면 정점들의 인덱스를 저장하고 있는 버퍼라고 할수 있는데
사각형을 예를 들면 사각형을 (삼각형 기반으로) 그리기 위해서는 2*3 = 6개의 정점이 필요하다.
사각형을 구성하는 정점을 4개만 두고 4개를 한 붓그리기 처럼 중복해서 그려주는 방식을 사용하는 걸 인덱스 버퍼의 기능이라고 생각할 수 있다.(하지만 없어도 상관은 없다)
추가적으로 인덱스 버퍼를 사용하는데, 그것도 결국 메모리를 사용하니까 똑같다고 생각 할 수도 있습니다.
정점은 위치데이터 말고도, 색깔, 법선, 텍스처좌표(UV), 애니메이션에 필요한 정보등등 여러가지 프로그래머가 원하는 데이터를 추가하여 사용하기 떄문에 단순히 정수만 저장하는 인덱스버퍼가 훨씬 메모리적으로 효율적이다.
다시 정점버퍼로 돌아와서 정점버퍼는 그냥 정점들의 연속적인 메모리 구조에 불과 하기때문에 실제로 GPU에서는 이러한 정점들을 이용하여 어떤 도형을 만들어야 하는지 정보가 필요하다. 해당 도형정보를 Primitve Topology 라고 한다.
결론적으로 Input Assembler 는 이러한 정점들의 데이터를 읽고, 삼각형과 같은 도형으로 조립하는 단계의 일을 한다고 생각하면된다.

[Vertex Shader]

Input Assembler 에서 받은 정점정보들의 정보로 도형은 생성이 되었지만 로컬 좌표계에 있기 때문에 해당 데이터들을 화면에 그대로 출력해버리면 화면에 중심부에 전부 그대로 출력되어버려서 공간좌표계(World)로 변환할 필요가 있습니다. Local space에서 World space 로 변환이 필요하고, 실제 플레이어가 바라보는 카메라가 중심이 되는 공간 View Spcae변환을 해준다. 그리고 마지막으로 Projection 변환을 거쳐 최종적으로 정의된 ClipSpace라는 공간으로 변환을 해준다.

[월드공간 변환]

Local Space라고도 불리는 오브젝트 공간은 3차원 세상에서 효현될 각각의 개인의 공간에 정의된 영역이이다.

[카메라 공간 변환]

월드 변환이 완료되어 모든 물체가 한공간(World space)에 모아지면 이제 우리가 원하는, 시점에서 물체를 관찰 할 수 있게 해줘야합니다. 이 때 관찰자로써 가상의 카메라가 필요하고, 이 카메라가 볼 수 있는 영역의 공간을 뷰 공간이라고 합니다.
월드 공간의 모든 물체를 카메라 공간으로 변화하게 된다면 효율적으로 여러가지 효과나 렌더링등을 진행할수 있다.
여기서 잠깐 카메라가 바라보는 세상에 대해 생각해보자. 가상의 카메라는 컴퓨터의 성능의 한계 때문에 실제 세상과는 다르게 시야가 제한 될수 있다. FOV(시야각), ASPECT(종횡비)에 의해 결정되는데 이러한 가시영역을 뷰 볼륨이라고 한다. 이렇게 생성된 뷰볼륨에 Near, Far정보가 전달되어 절두체의 영역을 다시 정의 한다.

[View Frustrum]

절두체 공간 밖에 있는 물체는 그리지 않는데, 우리가 살고 있는 3차원 세상은 모든걸 보여준다. 이렇게 밖에 없는 이유는 계산상의 효율성을 위해 어쩔수 없이 도입된 개념이다. 만약 물체가 절두체의 경계를 걸치게 되면 바깥쪽 부분은 잘려서 버리게 된다. 이를 클리핑(Clipping)이라고 한다. 이 클리핑은 카메라변환에서 이뤄지지 않고 나중에 클립공간에서 ->레스터라이저로 넘겨질때 수행된다. 이에 관련해선 레스터라이저 스테이지에서 자세히 설명하겠다.

[투영 변환]

카메라 변환에서 월드의 모둔 물체를 카메라 공간으로 재배치하고, 이제 카메라 시점에서 세상을 바라볼수 있게 되었습니다. 우리가 카메라를 통해서 바라보는 가상의 공간 현실세계처럼 3차원이지만 최적으로 우리가 바라봐야할 공간은 모니터세상인 (2차원) 공간의 세계가 되어야 합니다. 3차원 공간을 어떻게 2차원으로 표현 할 수 있을까요?
3차원 세상은 2차원 평면에 표혀하는 방법은 다행이도 이미 화가들이 개발되었기 떄문에 우리도 이러한 방법을 쓰면된다. (원근법)
투영 변환은 이러한 원근 법을 구현하기 위해 카메라 공간에서 정의된 절두체를 3차원 클립공간으로 변환하는것을 의미한다.
여기서 투영변환이라는 이름과는 다르게 3차원 공간의 물체를 2차원 평면으로 바꾸는것이 아니라 3차원 물체로 변형됨에 주목해야한다.
이러한 투영 변환을 거친 물체들을 관찰해보면 절두체 뒤쪽에 있던 영역의 폴리곤은 상대적으로 작아지는 것을 볼 수 있는데 우리가 원했던 원근법을 적용 된 것이라고 볼수 있다.
추가적으로 한개더 생각해 보면 원근법을 3차원 공간에서 실현하기 위해 직육면체 볼륨으로 물체들을 변환 시켰는데 여기서 얻는 이점이 있다. 좀 더 간단한 공식으로 쉽게 Clipping(클리핑)작업을 할 수 있다.

[Tesselator 단계]

테셀레이터는 3단계로 구성되어 있다.
HullShader → Tesselation → DomainShader 단계를 거친다.
Hullshader는 테셀레이터 작업의 첫단계이다.
VertexShader에서 공간변환을 진행하지 않고 Hullshader 정점정보들을 전달해준다.
Hullshader 는 폴리곤을 어떻게 분활 할 것인가? 폴리곤을 얼마나 분활 할 것인가? 를 결정하는 단계이다.
아래 그림은 테셀레이션 적용 전과 후의 그림이다.
[적용전]
[적용후]
테셀레이터는 다시 말해 다각형을 겹치지 않고 작게 만들어 빈틈을 없애 게임등에서 사물이나 인물등을 실제에 보다 가깝게 표현할수 있게 도와주는 기술이다.
DomainShader는 테셀레이터가 출력한 정점마다 한 번씩 함수(셰이딩 언어) 호출을 해주게 된다.
테셀레이션이 활성화 되면 기존의 정점쉐이더에서 수행한 것들을 도메인 셰이더에서 수행하게 된다.
예를들어 공간변환(월드 → 뷰 → 투영)이 될수 있다.
하지만 자주 사용되는 기술은 아니다.
실제 게임을 제작 할떄는 정점이 적은 로우폴리곤과 정점이 많은 하이폴리곤 모델 두개를 따로 지원하는 경우 많다.

[Geometry Shader]

기본 폴리곤에서 정점을 추가하거나 또는 삭제 하거나 하는 연산을 할수 있다. 정점 정보를 추가하여 표현 할수 있는 모델이라면 그만큼의 정점정보를 뺴고 저장할수 있으니 메로리적으로 용량을 적게 차지 할수도 있고 GPU 도움을 받아서 정점을 추가해주기 때문에 연산속도가 빨라 질수 있다.

[Rasterization]

정점처리 단계를 지난 정점은 다음 단계인 레스터라이저 단계로 넘어갑니다.
우선 정점들은 삼각형으로 묶여있는데 이 시점부터는 하나의 독자적 도형으로 처리가 된다.
우선 화면에 그려질 2차원 삼각형의 세 정점이 결정되면 다음과 같은 일이 일어납니다.
1.
이 삼각형이 포함하는 모든 픽셀마다 pixelShader(fragment shader)가 실행된다.
2.
삼각형의 세 정점에 할당 되었던 여러 데이터(pos, uv, normal, color)가 보간되어 삼각형 내부에 각 픽셀셰이더로 넘어옵니다.
Directx에서는 이러한 과정을 통틀어서 레스터라이제이션 이라고 부르고 고정 파이프라인 단계로 프로그래머 이러한 로직들을 임의 바꿀수 없는 파이프라인 단계이다. 자체 알고리즘으로 알아서 동작을 한다.
대표적 레스터라이제이션의 역할을 나열해 보자면
1.
클리핑
2.
원긋 나눗셈(perpective division)
3.
뒷면제거 (backface culling)
4.
스캔변환 (ndc scan transform)
5.
뷰포트 변환

[클리핑]

클리핑은 투영변환 이후의 클립공간 볼륨 바깥에 놓인 폴리곤들을 잘라내는 작업을 말합니다. 이전 부터 언급되었던 이 작업이 바로 이 레스터라이저 단계에서 일어납니다.

[원근 나눗셈]

현재 단계에서 투영변환을 통해 원근법이 적용된 3차원 물체들을 직육면체 클리핑 공간에서 정의되어 있습니다. 우리가 최종적으로 필요한건 2차원 공간인데 그렇다면 어떻게 3차원공간을 2차원 공간으로 변환시킬수 있을까요?(수학적)
단순히 생각하면 3차원에서 2차원으로 차원을 줄이면 됩니다. 바로 Z좌표로 모든 성분을 나붜버리는거죠. 투영변환을 마친 정점데이터는 (x, y, z, w)에서 w성분에 z값이 저장된다. 원근 나눗셈이 적용 된 이후에는 x,y,z,w -> x,y,z의 좌표계로 변환되는데 이를 NDC(normailize device coordinate) 공간이라고 부릅니다. 여기서 정규화라는 이름이 붙는 이 좌표의 xy 범위는 [-1 ~ 1] z의 범위는 [0~1]이기 때문입니다.
//(x/z, y/z, z/z, w(z))

[뒷면 제거]

다음으로 레스터라이저에서 하는 기능으로 뒷면 제거가 있습니다. 카메라가 바라보고 있는 방향에 물체에 가려진 면적은 굳이 연산을 할 필요가 없다. 외적(Cross product) 삼각형의 바라보고있는 면의 방향을 구하여 뒷면일 경우에 연산에서 제외 시킨다.

[뷰포트 변환]

컴퓨터 화면상의 윈도우 스크린 공간을 갖는데 이 스크린 공간 내에 2차원 이미지가 그려질 뷰포트가 정의되는데 NDC공간의 물체들을 스크린 공간으로 이전시키는변환을 뷰포트 변환이라고 합니다.

[스캔 변환]

이전의 변환들은 자세한 사항을 몰라도 프로그래밍하는데 문제가 없었지만 이 스캔 변환은 렌더링 프로그램에서 직접적인 영향을 미치기 떄문에 꽤 중요하다. 삼각형 하나가 내부에 차지하는 모든픽셀(fragment)들을 생성하는 작업이다. 이때 정점데이터에 들어온 데이터들은 보간(선형 보간)되어서 픽셀셰이더로 넘어간다.

[Pixel Shader]

레스터화된 도형에 원하는 색을 입혀서 출력하게끔 도와주는 셰이더이다.
텍스처매핑 , 노말매핑, 등등 기법으로 색을 입혀서 표현도 가능하다.
조명 처리나 이미지 처리를 할 때 유용하게 사용된다.
정점 데이터가 보간된 값이 넘어온다.

[Output merger]

깊이 - 스텐실 테스트와 블렌딩이 일어나서 최종적인 화면(텍스처)에 물체를 그려준다.

[Compute shader]

컴퓨트 셰이더는 일반 렌더링 파이프라인과 별도로 그래픽카드를 사용할때 실행할수 있도록
도와주는 셰이더이다. 대량 병렬 GPGPU 알고리즘 또는 게임 렌더링의 일부를 가속시키기 위해서
사용 할수 있습니다. 효율적으로 사용 하려면 GPU 아키텍처와 병렬 알고리즘에 대한 지식뿐만 아니라 DirectxComput, Opengl Compute, CUDA, 또는 OpenCL에 대한 지식도 필요합니다.