Posts 렌더링 파이프라인 간단 정리
Post
Cancel

렌더링 파이프라인 간단 정리

렌더링 파이프라인 구조 요약(DirectX 9 기준)


입력 조립

  • GPU가 CPU로부터 정점 데이터를 전달 받아서 프리미티브(삼각형)들을 만든다.

정점 쉐이더

  • Object Space에서 Clip Space까지 정점들의 공간 변환을 수행한다.

래스터라이저

  • Clip Space의 정점 데이터를 Viewport로 변환하고,
    정점 데이터를 기반으로 보간된 프래그먼트(픽셀 데이터)를 생성한다.

픽셀(프래그먼트) 쉐이더

  • 프래그먼트를 입력 받아 화면에 그려질 모든 픽셀의 색상과 깊이 값을 출력한다.

출력 병합

  • Z-Test, Stencil Test, Alpha Blending 등을 통해 최종적으로 화면에 그려질 색상을 결정한다.


참고 : DirectX 11 렌더링 파이프라인 구조


image

지오메트리 쉐이더(Geometry Shader)

  • DirectX10에서 새롭게 추가된 쉐이더
  • 입력 받은 정점 데이터에서는 존재하지 않았던 새로운 점, 선, 면(삼각형) 등을 새롭게 생성하거나 기존의 정점을 제거할 수 있다.
  • 기존의 프리미티브(삼각형)를 다른 모양으로 바꿀 수도 있다.

테셀레이션 쉐이더(Tessellation Shader)

  • DirectX11에서 새롭게 추가된 쉐이더
  • 프리미티브(삼각형)를 더 잘게 쪼개서 새로운 정점들을 생성한다.


1. 입력 조립(Input Assembly)


  • 렌더링 파이프라인의 첫 단계

  • 정점 데이터를 CPU에서 GPU로 운반하기 위한 자료 구조를 정점 버퍼라고 한다.

  • 정점 버퍼는 위치, 노말, 색상, UV를 담고 있는데,
    구조체로 이쁘게 담고 있는게 아니고 직렬화된 형태(배열)로 담고 있다.

  • 그리고 GPU에서는 이렇게 전달 받은 정점 버퍼를,
    미리 전달 받은 렌더 상태의 버텍스 명세를 통해 정점 데이터(구조체)로 조립한다.

  • 그런데 이렇게 정점 버퍼를 정점 데이터로 조립한다고 입력 조립이 아니고,
  • 정점들을 모아서 삼각형과 같은 기본 도형(프리미티브, Primitive)으로 조립해주기 때문에 입력 조립 단계라고 한다.

  • 이렇게 조립된 프리미티브가 정점 쉐이더의 입력이 된다.


커맨드 큐(Command Queue)

  • GPU는 CPU보다 훨씬 빠르다.
  • 그래서 CPU가 GPU에 처리를 요청할 때, 동기적으로 요청하면 반드시 병목이 발생할 수밖에 없다.

  • 따라서 CPU는 요청들을 커맨드 버퍼(Command Buffer)에 담아서 커맨드 큐에 넣어두고
    GPU는 큐에서 버퍼들을 꺼내어 처리해주는 방식으로 병목을 해결한다.


드로우 콜(Draw Call)

  • CPU에서 GPU에 전달할 명령을 커맨드 버퍼에 담아 커맨드 큐에 넣는 과정
  • 쉽게 말해, CPU가 GPU에게 메시 좀 그려 달라고 요청하는 것이다.

  • 종류 예시
    • Set Pass Call : 렌더 상태 변경 요청(메시를 그리기 위한 환경 설정)
    • DP Call(Draw Primitive Call) : 메시 그리기 요청
  • 일반적으로 Set Pass Call 이후 DP Call로 이어진다.


  • 참고 : 배치(Batch)
    • Set Pass Call과 DP Call을 합쳐서 지칭하는 드로우 콜
    • 흔히 말하는 드로우 콜이 바로 배치를 의미한다.
  • 참고2 : 배칭(Batching)
    • 여러 번 나누어 발생할 드로우 콜을 하나로 통합하는 것
    • 같은 마테리얼을 사용하는 경우 등등


렌더 상태(Render States)

  • 메시를 그리기 위해 필요한 데이터들

  • 정점 데이터(위치, 노말, 색상, UV)
  • 쉐이더
  • 알파 블렌딩 연산, Z-Test 여부 등


2. 정점 쉐이더(Vertex Shader)


  • 정점 데이터를 입력 받아 공간 변환을 수행한다.


공간 변환 과정

  • 공간 변환은 각각의 행렬 연산을 통해 이루어진다.
  • 변환 행렬은 총 3가지로, 각각 Model, View, Projection이다.
  • M, V, P는 각각 변환 행렬 이름이기도 하고, 변환 자체를 가리키기도 한다.

image


M (Model)

  • 오브젝트 공간(Model Space, Object Space) -> 월드 공간(World Space)

  • 각 오브젝트마다 자신의 피벗 위치를 원점(0, 0, 0)으로 하는 좌표 공간을 갖고 있다.
  • 게임 내의 월드는 단 하나의 위치를 원점으로 하는 좌표 공간을 갖고 있다.
  • 모델 변환은 각 오브젝트의 좌표 공간을 변환하여 하나의 월드 공간에 통합하는 과정이다.

  • 이 과정에서 이동(Translate), 회전(Rotate), 크기(Scale) 변환이 이루어진다.
  • 이 세가지 변환은 각각 행렬을 통해 수행되고, 이를 하나의 행렬로 만든 것을 TRS 행렬이라고 한다.

  • TRS 행렬은 좌측부터 우측으로 S, R, T 순서로 곱해진다.
  • 그리고 벡터와 곱할 때 벡터가 우측에 온다.
  • 따라서 각각 따로 곱할 때는 행렬을 좌측에 놓고 T, R, S 순서로 곱한다.


V (View)

  • 월드 공간(World Space) -> 카메라 공간(View Space)

  • 카메라 공간은 카메라의 위치가 원점(0, 0, 0)이고, 카메라가 바라보는 방향이 +Z축인 공간을 의미한다.
  • 뷰 변환은 모든 오브젝트를 화면에 그려내기 쉽도록 카메라 기준으로 공간을 변환하는 과정이다.


P (Projection)

  • 카메라 공간(View Space) -> 클립 공간(Clip Space)

  • 카메라 기준의 정점 위치를 화면에 보이기 위한 정점 위치로 변환한다.

  • 화면에 렌더링될 수 있는 영역을 나타내는 절두체(Frustum)가 정의된다.
  • 절두체는 Near Clipping Plane, Far Clipping Plane, Field of View를 통해 정의한다.

  • 절두체를 완전히 벗어나는 폴리곤들은 모두 버려지고
    절두체의 경계에 걸쳐 있는 폴리곤들은 일단 유지한다.

  • 원근감이 없는 직교 투영(Orthographic Projection) 또는
    원근감이 있는 원근 투영(Perspective Projection)이 행해진다.

  • 클립 공간의 좌표계는 사실 3D가 아닌 4D이다.
  • 클립 공간의 모든 X, Y 좌표는 -1 ~ 1 범위에 존재하며,
    Z 좌표는 0 ~ 1 범위에 존재한다.
  • W 값은 카메라에서 멀수록 커지며, 추후 NDC로의 변환에 사용된다.

  • 클립 공간의 4D 좌표계를 동차 좌표계(Homogeneous Coordinates)라고 한다.

  • 보통 클립 공간과 NDC를 혼용하는 경우가 많은데,
    엄밀히 말하자면 버텍스 쉐이더의 최종 출력은 클립 공간의 정점 데이터이다.


3. 래스터라이저(Rasterizer)


  • 하드웨어 자체 알고리즘을 통해 동작한다.
  • 프로그래밍이 불가능한, 고정 파이프라인 단계

  • 클립 스페이스의 정점 데이터를 전달받아 프래그먼트를 구성하고,
    화면에 출력할 픽셀들을 찾아낸다.

  • 픽셀 색상 등의 데이터는 정점의 데이터를 기준으로 보간된다.

  • 프래그먼트(Fragnemt)?
    • 픽셀 하나의 색상을 화면에 그려내기 위한 정보를 담고 있는 데이터


래스터라이저의 역할

[1] 클리핑(Clipping)

  • 버텍스 쉐이더의 마지막 단계에서, 절두체를 완전히 벗어나는 폴리곤은 버려졌지만
    절두체 경계에 걸쳐 있는 폴리곤들은 아직 버려지지 않았다.

  • 래스터라이저에서 이렇게 걸쳐 있는 폴리곤들을 잘라내어,
    절두체 내부와 외부 영역을 분리하여 절두체 외부 영역은 버린다.


[2] 원근 분할(Perspective Division)

  • 클립 스페이스(동차 좌표계, 4D) 좌표의 모든 요소를 w 값으로 나누게 되는데,
    이를 통해 모든 원근법 구현이 완료되며 이를 원근 분할이라고 한다.

  • 원근 분할을 마친 좌표계를 NDC라고 한다.

  • NDC(Normalized Device Coordinates)

    • X, Y 좌표는 모두 -1 ~ 1, Z 좌표는 0 ~ 1에 위치하는 좌표계
    • 스크린 좌표로 손쉽게 변환할 수 있도록 하기 위한 3D 공간 변환 상의 마지막 좌표계
    • 클립 스페이스의 (x, y, z)를 w로 나눈 결과이다.


[3] 후면 컬링(Back-face Culling)

  • View 벡터와 Normal 벡터의 관계를 통해 후면을 찾아내어
    렌더링되지 않도록 면을 제거한다.


[4] 뷰포트 변환(Viewport Transformation)

  • 3D NDC 공간 상의 좌표를 2D 스크린 좌표로 변환한다.
  • -1 ~ 1 범위였던 (X, Y) 좌표를 화면 해상도 범위로 변환한다.
  • 2D 공간으로 변환한다고 하지만, 실제로는 Z값을 깊이 값으로 사용하기 위해 그대로 유지한다.


[5] 스캔 변환(Scan Transformation)

  • 프리미티브(기본 도형, 삼각형 등)를 통해 프래그먼트를 생성하고
    프래그먼트를 채우는 픽셀들을 찾아낸다.
  • 각 픽셀마다 정점 데이터(위치, 색상, 노멀, UV)들을 보간하여 할당한다.


4. 픽셀 쉐이더(Pixel Shader)


  • DirectX에서는 픽셀 쉐이더, OpenGL과 유니티 엔진에서는 프래그먼트 쉐이더(Fragment Shader)라고 부른다.

  • 픽셀 쉐이더는 모델이 화면에서 차지하는 픽셀의 개수만큼 실행된다.
  • 쉐이더를 통해 색상을 변화시키는 것은 모두 픽셀 쉐이더의 역할이라고 보면 된다.

  • 투명도를 결정 하는 것도 픽셀 쉐이더,
    라이팅과 그림자를 적용하는 것도 픽셀 쉐이더,
    텍스쳐 색상을 메시에 입히는 것도 모두 픽셀 쉐이더에서 하는 역할이다.

  • 픽셀 쉐이더는 각 픽셀들의 색상과 깊이 값을 출력으로 전달한다.
  • 깊이 값은 Z-Buffer에, 색상 값은 Color Buffer에 저장된다.

  • 이 때 버퍼는 텍스쳐라고 생각하면 된다.
  • 그리고 이런 버퍼들을 통칭하여 스크린 버퍼(Screen Buffer)라고 한다.


5. 출력 병합(Output Merge)


  • 렌더링 파이프라인의 마지막 단계

  • 픽셀들을 화면에 출력하기 위한 마지막 연산들을 수행한다.
    • Z-Test
    • Stencil Test
    • Alpha Blending
  • 각각의 픽셀 위치마다 여러 오브젝트의 픽셀이 겹쳐 있을 수 있다.
  • 출력 병합 단계에서는 이렇게 겹치는 픽셀들을 연산 및 판단하여 픽셀의 최종적인 색상을 결정한다.


프레임 버퍼(Frame Buffer)

  • 한 프레임의 스크린 버퍼들

  • 구조

    • Color Buffer : 색상 값 텍스쳐
    • Z-Buffer : 깊이 값 텍스쳐
    • Stencil Buffer : 픽셀을 렌더링 또는 폐기하기 위한 마스크 텍스쳐


References


This post is licensed under CC BY 4.0 by the author.