포스트

렌더링 파이프라인

렌더링은 한 번에 이루어지는 작업이 아닙니다. 렌더링 파이프라인에 대한 이해는 필수적입니다.

렌더링 파이프라인

GPU와 VRAM

GPU는 그래픽 카드 내부에 있는 부품으로 그래픽 및 병렬 연산을 담당하는 칩셋이다.

CPU가 RAM에서 데이터를 읽어오듯 GPU도 VRAM에서 데이터를 읽어온다.
VRAM에는 텍스처, 메시 데이터 등 렌더링에 필요한 데이터들과 렌더링 결과를 저장하는 버퍼들이 저장된다.
오브젝트를 렌더링할 때 여기에 저장된 데이터를 참고하여 렌더링한다.



게임 루프

플레이어가 게임을 실행하면 다음과 같은 일련의 과정을 거친다.

  1. 로딩: 초기화, 리소스 생성
  2. 게임 중: 매 프레임 렌더링
  3. 게임 종료: 리소스 해제

만약 60프레임이라고 한다면, 1초동안 60개의 장면을 그려낸다.
즉, 한 장면에 $1 / 60$초이므로 대략 $0.0166$초를 소비한다.

매 프레임마다 다음과 같은 작업들이 일어난다.

  1. 물리 연산
  2. 사용자 입력 처리
  3. AI
  4. 게임 로직 처리
  5. 애니메이션 모션 처리
  6. 네트워크 처리
  7. 오디오 처리

크게 과정을 나눠보면 다음과 같다.

1
2
3
4
5
6
7
Initialiation       Awake, OnEnable, Start 호출
↓
Update          Physics, Input Events, Game Logic 처리
↓
Render          Scene, GUI 렌더링
↓
Decommisioning



렌더링 루프

렌더링은 Update에서 PhysicsInput Events, Game Logic을 모두 처리한 다음에 시작된다.

RenderUpdate 후의 과정이냐면 Update에서 오브젝트의 위치나 모습이 결정되기 때문이다.
최종적으로 위치, 모습이 결정되어야 렌더링할 수 있다.

Update 이후에 오브젝트를 렌더링해야 하는데 렌더링에도 순서가 있다.
Scene을 제일 먼저 렌더링하고, 그 다음에 Gizmo, GUI 순서로 렌더링한다.

이렇게 모두 렌더링이 끝나야 비로소 화면에 오브젝트가 그려진다.



렌더링 파이프라인

오브젝트를 렌더링하기 위해서는 일련의 과정을 거친다.
이것을 렌더링 파이프라인이라고 한다.

오브젝트를 렌더링하기 위해서는 다음과 같은 정보들이 필요하다.

  • 형태를 나타내는 Mesh 정보
  • Albedo, Normal, Sepcular 등의 Texture 정보
  • 조명을 어떻게 처리할 것인지 결정하는 Shader 정보
  • Transform 정보

위의 정보들은 하나의 오브젝트가 렌더링되는 과정에서 사용된다.

Application

게임에서 오브젝트를 렌더링하기 전에 각종 연산을 처리하는 단계다.
매 프레임마다 오브젝트들의 트랜스폼과 애니메이션 정보를 계산한다.

Application 단에서 가장 핵심적인 연산은 컬링이다.
컬링은 카메라 바깥으로 벗어난 오브젝트를 잘라내는 연산이다.

이를 통해 렌더링할 오브젝트 수를 줄이기 때문에 렌더링 파이프라인의 부담을 줄일 수 있다.


Geomotry Processing

정점과 삼각형 별로 연산을 수행하며, 기하학적인 처리를 진행한다.
여기서 기하학적인 처리는 다음과 같은 계산을 포함한다.

  1. 무엇을 그릴 것인가?
  2. 어떻게 그릴 것인가?
  3. 어디에 그릴 것인가?

정점은 삼각형을 만들기 위한 점을 의미한다.
이 정점들을 이어서 만든 삼각형을 폴리곤이라고 한다.
그리고 폴리곤들을 모아 특정 오브젝트 모양을 표현한 것을 메시라고 한다.

이 정점 정보들은 VRAM에 저장되어 있으며, 메시에 대한 위치 데이터만 가지고 있다. 즉, 로컬 공간에 존재한다.
왜 로컬 공간이냐고 하면 이 오브젝트가 어디에 배치될지는 모르기 때문에 메시에 대한 정보만을 가지고 있는 것이다.

렌더링 시 해당 메시를 사용하는 오브젝트의 트랜스폼 정보를 반영하여 월드 공간의 데이터로 변환한다.
이 과정을 월드 트랜스폼이라고 한다.

또, 오브젝트를 화면에 출력할 때는 카메라가 기준이 된다.
월드에 배치한다고 해도 카메라가 배치한 곳을 보고 있지 않으면 안보인다.

따라서 월드 공간 데이터를 다시 뷰 공간 데이터로 변환해야 한다. 이 과정을 뷰 트랜스폼이라고 한다.

마지막으로 카메라로 보는 공간은 3D가 아닌 2D이기 때문에 뷰 공간을 투영하여 2D 공간으로 변형해야 한다.

위의 트랜스폼 변환 과정은 모두 정점 셰이더에서 처리된다.
위치뿐만 아니라 Normal과 색상도 정점 셰이더에서 결정한다.

정점 셰이더를 거치고 나면 정점들은 서로 연결되어 메시를 이룬다.
그래픽스에선 이 메시를 지오메트리라고 한다.


Rasterizer

메시를 화면에 매칭하여 픽셀을 결정하는 과정을 Rasterizing이라고 한다.

Rasterizer에서는 래스터라이징을 하고 최종적으로 픽셀에 색상을 더한다.
이 과정은 프래그먼트 셰이더 또는 픽셀 셰이더라고 불리는 셰이더에서 수행한다.

여기서 결정된 최종 픽셀 정보는 색상 버퍼에 저장되며, RGBA 형태로 4가지 채널 정보가 저장된다.

추가적으로 각 픽셀의 오브젝트와 카메라와의 거리값에 대한 정보가 필요한데, 이는 Z버퍼에 저장된다.
여기에 저장된 Z값을 이용하여 오브젝트의 픽셀 렌더링 여부를 결정한다.

요약하면 특정 픽셀에 오브젝트가 겹쳐있을 때 Z값을 이용하여 가려지는 부분을 걸러내어 렌더링한다.

다만 카메라에 가까운 오브젝트가 반투명한 경우에는 추가 과정이 필요하다.
뒤에 있는 오브젝트도 비쳐 보이기 때문에 해당 픽셀도 렌더링해야 한다.

또한, 겹치는 부분에 의해 알파값과 색상이 달라지게 된다.
따라서 최종 색상을 결정하는 알파 블렌딩 과정도 거친다.

투명 오브젝트를 많이 사용하게 되면, Z버퍼를 이용한 비용 절감 효율이 감소하고 알파 블렌딩으로 인한 비용이 증가한다.



더블 버퍼링

렌더링 파이프라인의 일련의 과정들을 살펴보면서 렌더링은 한 번에 이루어지지 않음을 확인했다.
실제로 렌더링에는 약간의 시간이 필요하기 때문에 연속적으로 끊김이 없게 하기엔 무리가 있다.

이를 해결하기 위해 버퍼를 2개를 사용하는 전략을 취한다.

버퍼1이 시선을 끄는 동안 버퍼2에 프레임을 렌더링한다.
또 버퍼2가 시선을 끄는 동안 버퍼1에 프레임을 렌더링한다.

각 역할에 따라 Front buffer, Back buffer라고 한다.



정리

  1. 프레임이 시작되면 Update가 실행된다.
  2. 오브젝트를 렌더링한다.
  3. 포스트 프로세싱을 처리한다.
  4. 버퍼가 교체되면서 화면에 보여진다.
  5. 1~4를 반복한다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.