공부/Rendering

렌더링 파이프라인

Lero God 2023. 4. 28. 17:48

참고

https://woo-dev.tistory.com/168

https://rito15.github.io/posts/rendering-pipeline/

 

렌더링 파이프라인 이미지

입력 조립기 - 버텍스 정보를 받아와 정점을 연결해 삼각형 모양 같은 프리미티브(표현 가능한 가장 작은 단위)들을 만든다.

 

정점 쉐이더 - 정점 데이터를 받아 공간 변환을 한다. MVP(Model, View, Projection) 변환을 한다.

 

Model 변환은 Local -> World 스페이스로 변한다

로컬 스페이스는 각 오브젝트마다 자신만의 원점을 가지는 공간입니다.

월드 스페이스는 하나의 원점을 가지는 공간입니다.

각 오브젝트 좌표 공간을 변환하여 하나의 월드 공간으로 통합하는걸 Model 변환이라고 합니다.

 

View 변환은 World -> View 스페이스로 변한다

카메라 뷰 공간은 카메라의 위치가 월드 공간의 원점이고 바라보는 방향은 z 축의 양의 방향인 공간이다.

월드 스페이스에서 뷰 스페이스로 변환하는 과정은 다른 오브젝트들에 카메라라가 이동하고 회전하는 값을 적용시켜서 변환하는 과정입니다.

 

Projection 변환은 View -> Clip 스페이스로 변환

클립 공간에서는 절두체(Frustrum)을 생성해서 보이는 오브젝트와 안 보이는 오브젝트가 생기게 되는데 이것을 절두체(프러스텀) 컬링이라고 합니다.

 

그런데 뷰 프러스텀에 약간의 문제점이 있다. 위와 같이 피라미드 형태의 프러스텀에서 영역 밖의 폴리곤을 검사하는 것은 쉽지 않다. 피라미드는 비스듬한 평면이 많기 때문이다. 따라서 아래와 같이 뷰 프러스텀 내의 물체들을 모든 면이 좌표계와 평행한 직육면체와 같은 공간 내로 이동시킨다.

위와 같이 뷰 프러스텀 내의 물체들을 직육면체 공간(뷰 볼륨)으로 변환시키는 것을 투영 변환(projection transform)이라 한다. 이러한 투영 변환은 뷰 프러스텀 내의 물체에도 동일하게 적용된다. 변환 후 직육면체 공간을 클립 공간(clip space)이라 한다. 이는 클리핑을 위한 공간이기 때문에 클립 공간이라 불린다.

 

투영을 하게 되면 비로소 클립 스페이스로 변환됩니다.

투영에는 원근 투영, 직교 투영이 있는데 

원근 투영은 원근감이 있어 멀리 있는 물체가 더 작아지게 되고,

직교 투영은 거리에 따라 크기가 변하지 않습니다.

직교 투영은 ui 를 표현할 때 주로 씁니다.

오브젝트들이 클립 공간으로 변환되고나면 x,y 좌표는 -1~1 사이, z 좌표는 0~1 사이의 값을 가지게 됩니다.

 

뷰 프러스텀 컬링과 클리핑의 차이?

컬링은 메쉬 자체를 제외시키는 것이고, 클리핑은 컬링에서 1차적으로 걸러지고 난 후의 메쉬에 대해 수행하며, 주로 메쉬의 일부분인 폴리곤을 잘라내는 작업을 한다. 컬링은 해당 메쉬가 시야를 완전히 벗어나는지만 검사하면 되지만, 클리핑은 추가로 어느 영역이 벗어나는지를 검사해야 하기 때문에 컬링에 비해 더 많은 오버헤드를 필요로 한다. 또한 컬링은 주로 정적인 물체에 적용한다면, 클리핑은 동적인 물체에 적용한다. 

 

테셀레이션(생략 가능)

directx11 에 새롭게 추가된 셰이더

기존 삼각형들을 쪼개서 더 작은 삼각형을 만들어낸다.

더 세밀한 메시를 표현하기 위해서 사용하거나,

메모리에는 low-poly 메시를 로드하고 테셀레이션을 이용하여 세밀한 메시 표현을 하면 메모리를 절약할 수 있다.

 

기하(Geometry) 셰이더(생략 가능)

기본 도형에서 정점을 추가하거나 삭제해서 모양을 변형 시키는 단계

 

래스터라이저

클립 공간에서 정점 데이터를 가져와 화면에 출력할 픽셀들을 찾아내는 단계이다.

프로그래밍이 불가능한 고정 파이프라인이다.

래스터라는 뜻은 픽셀로 이루어진 이미지라는 뜻이다.

래스터라이저라는 건 프리미티브를 픽셀로 채워서 이미지로 만드는 걸 의미한다.

근데 해당 단계에선 픽셀이 아닌 프래그먼트라는 자료로 채운다.

프래그먼트란 색상이 채워지기 전 픽셀, 픽셀의 뼈대 같은 느낌이다.(근데 색상이 있을 수도 있다)

 

클리핑 - 절두체 경계에 걸쳐져있는 오브젝트를 잘라내어 절두체 외부에 있는 부분은 출력을 안 하도록 처리한다.

원근 나눗셈 - 

후면 추리기 - 렌더링 되지 않도록 후면을 잘라낸다.

뷰포트 변환 - 뷰포트는 화면 전체의 일부분을 나타낸다. 뷰포트에 오브젝트를 출력하기 위해선 NDC 공간에서 2D 스크린 좌표로 변환해야 한다. x,y(-1~1) 의 범위가 스크린 좌표로 변한다. z 값은 유지한다. 뷰포트 변환 이후 클립 공간에서 스크린 공간으로 변하게 된다.

스캔 변환 - 스크린 공간의 물체들을 2차원 평면으로 나타내어, 물체를 구성하는 삼각형마다 프래그먼트로 채운다.

프래그먼트의 정보는 정점 정보를 보간하여 채운다. 보간되는 값 - 색상, 노멀, 텍스쳐 좌표, 깊이 

 

프래그먼트 처리

 

각 프래그먼트에 대해 색상을 결정한다. -> 색상을 정하고 난 이후는 픽셀이라고 부르는 건가?

프래그먼트 셰이더의 연산을 통해서 프래그먼트의 색상을 결정짓는다. (픽셀 셰이더라고도 하는데 프래그먼트 셰이더가 더 정확한 단어이다.)

프래그먼트 처리 단계에서 하는 중요한 두 작업은 텍스쳐링과 라이팅이다.

폴리곤 메시에 이미지를 입히는 걸 이미지 텍스쳐링이라고 한다.

각 정점에 이미지의 좌표 정보가 있는데, 이것을 uv 좌표라고 한다.

텍스쳐를 입혔을 때 왜곡이 되는 현상을 최소화하기 위해 메시를 각 부분별로 잘라놓는다(각 부분을 차트라고 함). 하지만 잘라놓은 부분마다 텍스쳐를 관리하게 되면 메모리를 많이 사용한다는 단점이 있다.

그렇기 때문에 (b) 처럼 부분들을 합쳐서 텍스쳐를 관리하는데, 이 텍스쳐를 아틀라스라고 부른다.

 

전에 래스터라이저의 스캔 변환을 통해서 각 프래그먼트는 텍스쳐 좌표를 가지고 있을 것이다.

아틀라스 텍스쳐에서 해당 좌표에 있는 텍셀의 컬러값을 가져와서 프래그먼트에 입히는 작업을 한다.

 

 

 

 

출력 병합(output merge)

 

이전 프래그먼트 처리 단계에서 각 프래그먼트의 RGB 값을 구했다. 하지만 이 색상에는 Alpha 값과 Z 값도 포함되어야 한다. 이것을 RGBAZ 값이라고 한다.

출력 병합 단계에서는 불투명도와 깊이를 추가로 비교하여 최종적으로 프래그먼트 색상을 결정한다.

 

z-버퍼링

프래그먼트의 z 값만을 저장하고 있는 버퍼를 z 버퍼라고 한다.(근데 버퍼는 스크린 사이즈 크기만한가? 궁금쓰)

그리고 프래그먼트의 색상값을 저장하고 있는 버퍼를 컬러 버퍼 라고 한다. (스텐실 버퍼도 있는데 이건 나중에)

컬러 버퍼와 z 버퍼를 이용해 각 프래그먼트에 어떤 색상을 출력할지 정하는 것을 z-버퍼링이라고 한다.

 

알파 블렌딩

위에 과정들은 모두 프래그먼트가 불투명하다고 가정하고 진행했었다. 만약 프래그먼트가 반투명하다면 z 버퍼링을 통해 나온 값과 달라질 것이다. 예를 들어 한 프래그먼트에 파란색과 빨간색이 겹칠 때, 앞 쪽에 있는 파란색 프래그먼트가 불투명하면 파란색만 나올 것이다. 하지만 파란색 프래그먼트가 반투명하면 빨간색도 섞여서 보여야 할 것이다. 

즉 알파값(불투명도)에 따라 색들이 혼합되어서 나와야 한다. 

예를 들어서 파란색 불투명도가 40이라고 하면 파란색의 40%, 빨간색의 60%가 혼합돼야 한다.

이렇게 색상을 혼합해서 불투명도를 나타내는 방식을 알파 블렌딩이라고 한다. 

 

z-컬링(ZCULL, Early Z, Hierarchical Z)

z 버퍼링 이전에 프래그먼트를 제외하고 싶을 때 쓰는 기술이다.

다만 그래픽 카드에서 지원해줘야 함. 

프래그먼트 처리 단계에서 색상 결정, 라이팅 연산 같은 무거운 처리를 하기 때문에 이전 단계인 래스터라이징 단계에서 연산에서 제외할 프래그먼트들을 결정하는 작업을 z컬링이라고 한다. 

 

z컬링 알고리즘 중에 프리-z-pass 라는 알고리즘에 대해 알아보자.

프리zpass 는 2 개의 pass 를 이용해 수행하는 알고리즘이다.

먼저 래스터라이징 이후에 프래그먼트 셰이더 단계에서 색상, 라이팅 연산을 제외한 z 버퍼만 얻고 첫번째 pass 를 종료한다.

그다음 z 버퍼를 이용해 두번째 패스에서 렌더링을 한다. 

그러면 프래그먼트 처리 단계 이전에 프래그먼트들을 걸러내서 연산 비용을 아낄 수 있다.(근데 pass 가 뭐지? 그리고 알파블렌딩은 어떡하지?)

 

z 컬링이 오히려 비효율적인 때도 있으니 주의해서 사용해야 한다.

프래그먼트 셰이더에서 z 값을 변경할 때, 연산 비용이 작아 오히려 2개의 pass 를 이용해 렌더링하는게 비용이 더 클 때, 등이다.