게임 관련

그리픽 랜더링 파이프라인

k-codestudy 2025. 2. 2. 21:23

파이프 라인이란?

여러 명령어가 중첩되어서 프로그램이나 하나의 작업을 실행하게 도와주는 과정( 연산들의 집합 )이다.

한 사이클 안에 들어가야 해서 과정들이 많기 떄문에 한사이클이 복잡해지거나 사용자 입장에서 어려워질수 있는 단점이 존재한다.

 

Input Assembler

3차원 모델 하나를 3차원 세상에 나타내기 위해서는 가장 먼저 해줘야 할것은 무엇인가?

여기서 모델은 점(vertex)로 이루어져 있다. 이것을 우리는 폴리곤(점들의 집합)이라고 한다.

주로 게임에서는 삼각형을 가지고 3D 폴리곤을 정의하는데, 이때 이 정점데이터들을 운반하는 자료구조를 우리는 Vertex Buffer라고 한다

 

GPU 

컴퓨터 시스템에서, 그래픽 연산을 빠르게 처리하여 결과값을 모니터에 출력하는 연산 장치

 

CPU 

컴퓨터의 운영 체제와 앱을 실행하고 기타 다양한 컴퓨터 작업을 관리하는 전자 회로의 집합체, 본질적으로 컴퓨터의 활성 두뇌에 해당

 

Vertex Buffer

RAM에 정점데이터가 존재하는데 GPU로 옮겨 작업을 해야하기에 GPU에 정점데이터의 크기만큼 메모리 공간을 만들어주는 과정

 

정점 버퍼와 같이 등장하는 용어로 Index Buffer라는 것이 존재한다.

인덱스 버퍼는 쉽게 생각하면 정점들의 인덱스를 저장하고 있는 버퍼라고 할 수 있는데

사각형을 예로 들면 사각형을 (삼각형 기반으로) 그리기 위해서는 2 * 3 = 6 개의 정점이 필요하다

 

사각형을 구성하는 정점을 4개만 두고 4개를 한 붓그리기 처럼 중복해서 그려주는 방식을 사용하는 걸 인덱스 버퍼의 기능이라고 생각 할 수 있다. ( 하지만 없어도 상관없다 )

 

Index Buffer

정잠들의 인덱스를 저장하고 있는 버퍼 ( 버택스의 집합 )

추가적으로 인덱스 버퍼를 사용하는데, 그것도 결국 메모리를 사용하니까 똑같다고 생각 할 수도 있다.

 

정점은 위치데이터 말고도, 색깔, 법선, 텍스쳐좌표(UV), 애니메이션에 필요한 정보등등 여러가지 프로그래머가 원하는 데이터를 추가하여 사용하기 때문에 단순히 정수만 저장하는 인덱스버퍼가 훨씬 메모리적으로 효율적이다.

 

다시 정점버퍼로 돌아와서, 정점버퍼는 그냥 정점들의 연속적인 메모리 구조에 불과 하기때문에 실제로 GPU에서는 이러한 정점들을 이용하여 어떤 도형을 만들어야 하는지 정보가 필요하다. 해당 도형정보를 Primitive Topology라고 한다.

 

 

Primitive Topology

어떤 도형을 만들어야 하는지 필요한 정보

 

결론적으로 input Assembler는 이러한 정점들의 데이터를 읽고, 삼각형과 같은 도형으로 조립하는 단게의 일을 한다고 생각하면 된다.

 

 

Vertex Shader

input Assembler 에서 받은 정점정보들의 정보로 도형은 생성이 되었지만 로컬 좌표계에 있기 때문에 해당 데이터들을 화면에 그래도 출력해버리면 화면에 중심부에 전부 그대로 출력되어버려서 공간좌표계(Wolrd)로 변환할 필요가 있다. 

Local Space에서 Wolrd space로 변환이 필요하고, 실제플레이어가 바라보는 카메라가 중심이 되는 공간 View Space변환을 해준다. 

그리고 마지막으로 projection 변환을 거쳐 최종적으로 정의된 ClipSpace라는 공간으로 변환해 준다.

 

월드공간 변환

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

 

카메라 공간 변환

월드 변환이 완료되어 모든 물체가 한공간(world Space)에 모아지면 이제 우리가 원하는 시점에서 물체를 관찰 할 수 있게 해줘야합니다.

이때 관찰자로써 가상의 카메라가 필요하고, 이 카메라가 볼 수 있는 영역의 공간을 뷰 공간이라고 한다.

월드 공간의 모든 물체를 카메라 공간으로 변화하게 된다면 효율적으로 여러가지 효과나 랜더링등을 진행 할 수 있다.

여기서 잠깐 카메라가 바라보는 세상에 대해 생각해보자. 

가상의 카메라는 컴퓨터의 성능의 한게 때문에 실제 세상과는 다르게 시야가 제한 될 수 있다.

FOV(시야각), ASPECT(종횡비)에 의해 결정되는데 이러한 가시영역을 뷰 볼륨이라고 한다.

이렇게 생성된 뷰 볼륨에 Near, Far정보가 전달되어 절두체의 영역을 다시 정의한다.

 

 

View Frustrum

절두체 공간 밖에 있는 물체는 그리지 않는데, 우리가 살고 있는3차원 세상은 모든걸 보여준다.

이렇게 밖에 없는 이유는 계산상의 효율성을 위해 어쩔수 없이 도입된 개념이다.

만약 물체가 절두체의 경계를 걸치게 되면 바깥쪽 부분은 잘려서 버리게 된다. 이를 클리핑(Clipping)이라고 한다.

이 클리핑은 카메라변환에서 이뤄지지 않고 나중에 클립공간에서 -> 레스터라이저로 넘겨질때 수행된다.

이예 관련한 레스터라이저 스테이지에서 자세히 설명하겠다.

 

투영 변환 

카메라 변환에서 월드의 모든 물체를 카메라 공간으로 재배치하고, 이제 카메라 시점에서 세상을 바라볼수 있게 되었다.

우리가 카메라를 통해서 바라보는 가상의 공간 현실세계처럼 3차원이지만 최적으로 우리가 봐라봐야할 공간은 모니터세상인 (2차원) 공간의 세계가 되어야 한다. 3차원 공간을 어떻게 2차원으로 표현 할 수 있을까?

 

투영 변환은 이러한 원근 법을 구현하기 위해 카메라 공간에서 정의된 절두체를 3차원 클립공간으로 변환하는것을 의미한다.

 

여기서 투영변환이라는 이름과는 다르게 3차원 공간의 물체를 2차원 평면으로 바꾸는것이 아니라 3차원 물체로 변형됨에 주목해야한다.

이러한 투영 변환을 거친 물체들을 관찰해보면 절두체 뒤쪽에 있던 영역의 폴리곤은 상대적으로 작아지는 것을 볼 수 있는데 우리가 원했던 원근법을 적용 된 것이라고 볼 수 있다.

 

추가적으로 한개 더 생각해 보면 원근법을 3차원 공간에서 실현하기 위해 직육면체 볼륨으로 물체들을 변환 시켰는데 여기서 얻는 이점이 존재한다. 좀 더 간단한 공식으로 쉽게 클리핑(Clipping)작업을 할 수 있다.

 

 

Tesslator 단계

다각형을 겹치지 않고 작게 만들어 빈틈을 없애 게임들에서 사물이나 인물등을 실제에 보다 가깝게 표현할 수 있게 도와주는 기술이다.

테셀레이터는 3단계로 구성되어있다.

Hullshader - Tesslation - DomainShader 단계를 거친다.

 

Hullshader

Tesslator 작업의 첫 단계이다.

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 Culiing)
  4. 스캔 변환 (NDC Scan Transform)
  5. 뷰포트 변환

클리핑

클리핑은 투영변환 이후에 클립공간 볼륨 바깥에 놓인 폴리곤들을 잘라내는 작업을 말한다.

이전부터 언급되었던 이 작업이 바로 이 레스터라이저 단계이다.

 

원근 나눗셈(Perpective Division)

현재 단게에서 투영변환을 통해 원근법이 적용된 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 ]이기 때문이다.

 

뒷면 제거 (Backface Culiing)

다음으로 레스터라이저에서 하는 기능으로 뒷면 제거가 존재한다.

카메라가 바라보고 있는 방향에 물체에 가려진 면적은 굳이 연산을 할 필요가 없다.

외적 (Cross Product) 삼각형의 바라보고있는 면의 방향을 구하여 뒷면일 경우 연산에서 제외 시킨다.

 

스캔 변환 (NDC Scan Transform)

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

이때 정점데이터에 들어온 데이터들은 보간(선형 보간)되어서 픽셀셰이더로 넘어간다.

 

뷰포트 변환

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

 

 

Pixel Shader

레스터화된 도형에 원하는 색을 입혀서 출력하게끔 도와주는 셰이더이다.

택스처매핑, 노말매핑, 등등 기법으로 색을 입혀서 표현도 가능하다

조명 처리나 이미지 처리를 할 때 유용하게 사용되며 정점 데이터가 보간된 값이 넘어온다.

Output Merger

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

 

'게임 관련' 카테고리의 다른 글

DirectX tutorial 2  (1) 2025.02.03