컴퓨터 프로그램을 사용하여 Model 또는 Scene으로 부터 영상을 만들어 내는 과정을 Rendering이라고 하고, 한 데이터 처리 단계의 출력이 다음 단계의 입력으로 이어지는 형태로 연결된 구조를 Pipeline이라고 한다.
그림 - Pipeline
즉, Rendering Pipeline 이라는 것은 메모리 자원들을 GPU로 처리해서 하나의 렌더링된 이미지를 만들어 내는 데 쓰이는 메커니즘을 뜻한다.
DirectX11의 Rendering Pipeline을 대략적으로 보면 다음과 같다.
그림 - 대략적인 Rendering Pipeline
대략적인 Rendering Pipeline의 그림을 보면 우선 Rendering 과정이 초록색과 빨간색으로 나뉘어져 있다.
초록색은 Fixed Function(고정 기능 단계)로 미리 정해진 특정한 연산들만 수행이 가능하고, 실행 시점에서 기능은 바꿀 수 없지만, 상태 객체라는 개념을 이용하여 설정들의 변경이 가능하며, 이 Fixed Function의 단계는 임의로 실행을 하지 않는 방법은 없다.
빨간색은 Programmable(프로그래밍 가능 단계)로 HLSL(High Level Shading Language, 고수준 셰이딩 언어)로 셰이더 프로그래밍이 가능한 단계이다. 물론 특정 Programmable 단계를 비활성화 하는 것도 가능하다.
Rendering Pipeline의 진입점은 입력 조립기(Input Assembler)이다. 이 단계는 자원들로 부터 입력 자료를 읽어 들여서 파이프라인의 이후 단계들이 사용할 정점들을 긁어 모으는 작업을 한다. 또한 입력 자원들에 기초해서 정점들 사이의 연결성을 파악하고 바람직한 렌더링 구성을 결정한다. 이 단계는 취합한 정점들과 기본도형(Primitive) 연결성 정보를 다음 단계인 정점 셰이더로 넘겨준다.
다음 단계인 정점 셰이더(Vertex Shader) 단계는 입력 조립기가 넘겨준 정점 자료의 정점들을 한 번에 하나 씩 처리한다. 각 입력 정점마다 현재 지정된 정점 셰이더 프로그램이 적용된다. 이 정점 셰이더에서 하는 일은 저의 경험으로 비추어 볼때 대체로 변환 행렬을 적용하거나, 정점 별 조명계산 수행 등 Pixel Shader에서의 불필요한 연산들을 미리 할 때 사용한다.
다음 세 단계는 하드웨어의 테셀레이션(Tessellation) 기능을 활용하기 위해서 최근에 파이프라인에 추가된 것으로 반드시 세 단계 (Hull Shader, Tessellatior, Domain Shader)을 함께 사용하거나, 사용하지 않아야 한다.
테셀레이션(Tessellation)의 첫 번째 단계인 덮개 셰이더(Hull Shader)는 정점 셰이더가 넘겨준 기본 도형들을 받아서 두 가지 작업을 수행한다. 첫 작업은 각 기본 도형마다 실행되는 것으로, 여기서 덮개 셰이더는 기본도형에 대한 일단의 테셀레이션 계수들을 결정한다. 이 계수들은 이후 과정에서 해당 기본 도형을 얼마나 세밀하게 분할해야 하는지를 파악하는데 쓰인다. 두 번째 작업은 바람직한 출력 제어 패치 구성의 각 제어점마다 실행되는 것으로, 여기서 덮개 셰이더는 이후 영역 셰이더 단계(Domain Shader)에서 기본도형을 실제로 분할하는 데 사용할 제어 점들을 만들어낸다.
테셀레이션의 두 번째 단계인 테셀레이터(Tessellator)는 현재 기본 도형 종류에 적합한 표본 추출 패턴(sampling pattern)을 결정한다. 테셀레이터 단계는 테셀레이션 계수들과 자신만의 구성을 이용해서, 현재 기본 도형의 정점들 중 기본 도형을 더 작은 조각으로 분할하기 위한 표본으로 사용할 정점들을 결정한다. 그 정점들로부터 산출한 일단의 무게 중심 좌표들을 다음 단계인 영역 셰이더(Domain Shader)에게 넘긴다.
테셀레이션의 세 번째 단계인 영역 셰이더(Domain Shader)는 무게 중심 좌표(barycentric coordinates)들과 덮개 셰이더가 생성한 제어점들을 입력으로 받아서 새 정점들을 생성한다. 이 단계에서 현재 기본 도형에 대해 생성된 제어점들 전체와 텍스처, 절차적 알고리즘 등을 이용해서, 테셀레이션된 각 점마다 무게중심 '위치'들을 출력 기하구조(geometry)로 변환해서 다음 단계로 넘겨준다. 테셀레이션 단계에서 증폭된 정점 자료들로부터 출력 기하구조를 생성하는 부분의 유연성 덕분에 파이프라인 안에서 좀 더 다양한 테셀레이션 알고리즘을 구현할 수 있는 여지가 생긴다.
다음 단계인 기하 셰이더(Geometry Shader)는 완성된 형태의 기본 도형(다각형)들을 처리하거나 생성한다. 이 단계에서는 파이프라인에 새로운 자료 요소를 추가하거나 제거할 수 있으며, 덕분에 전통적인 렌더링 파이프라인에서는 불가능했던 흥미로운 응용이 가능해진다.
이제 래스터화기 단계(Rasterizer Stage)에서는 주어진 기하 구조가 렌더 대상의 어떤 픽셀들을 덮는지 파악해서 그 픽셀들에 대한 단편 자료를 산출한다. 각 단편은 모든 정점별(per-vertex) 특성을 해당 픽셀 위치에 맞게 보간한 값들을 가진다. 이렇게 생성된 단편은 픽셀 셰이더로 넘어간다.
픽셀 셰이더(Pixel Shader)는 연결된 각 렌더 대상을 위한 색상 값을 출력한다.
마지막 단계인 출력 병합기(Output Merger)는 픽셀 셰이더 출력을 파이프라인에 연결된 깊이/스텐실 자원 및 렌더 대상 자원에 제대로 '합치는' 작업을 담당한다. 이 과정에서 출력 병합기는 깊이 판정과 스텐실 판정, 혼합 함수 적용 등을 수행하며, 최종적으로는 출력을 해당 자원에 실제로 기록한다.
또 다른 셰이더가 한개 더 존재한다. 그것은 계산 셰이더(Compute Shader)이다. 이 단계는 통상적인 렌더링 패러다임에서 벗어나는 계산을 수행하기 위해 만들어진 것으로, 그런 만큼 통상적인 렌더링 파이프라인과 개별적으로 수행된다고 생각하면 된다. 계산 셰이더 단계는 전적으로 범용 계산에만 쓰이는 하나의 독립적인 파이프라인이라고 할 수 있다. 이전에는 셰이더 프로그램의 호출(실행)이 특정 단계에서 입력이 처리되는 방식에 제약을 받았다. 그리고 스레드가 쓰이는 방식을 개발자가 직접 제어하는 것이 불가능했다. 하지만 계산 셰이더 단계에서는 더 이상 그렇지 않다. 또 다른 기능은 '그룹 공유 메모리' 블록이다. 한 스레드 그룹에 속하는 모든 스레드는 그 그룹의 공유 메모리에 접근할 수 있다. 실행 도중 스레드들은 이 메모리 블록을 통해서 서로 자료를 주고받을 수 있는데, 전통적인 렌더링 파이프라인에서는 이러한 소통이 불가능했다. 그런 소통이 가능해지면, 적재한 자료나 중간 계산 결과를 공유함으로써 효율성을 더욱 개선할 여지가 생긴다.
마지막으로 계산 셰이더 단계에서는 자원들에 대한 임의 읽기 접근과 쓰기 접근이 동시에 가능하다.
'Visualization Programming > DirectX11' 카테고리의 다른 글
DirectX11 - DirectCompute의 스레드 (0) | 2016.03.09 |
---|---|
DirectX11 - 계산 파이프라인란? (0) | 2016.03.08 |
DirectX11 - 자원 뷰 (0) | 2016.03.08 |
DirectX11 - 자원들의 개요 (2) | 2016.02.18 |
DirectX11 - Device와 DeviceContext (0) | 2015.12.07 |