참치김밥은 최고의 한식이다

[유니티] 유니티 그래픽스 최적화 : 렌더링 파이프라인 본문

Unity/최적화

[유니티] 유니티 그래픽스 최적화 : 렌더링 파이프라인

l__j__h 2024. 2. 23. 14:51
GPU의 의미

 

컴퓨터의 핵심 구동장치에 중앙 처리 장치 CPU가 존재하듯이, 그래픽을 처리하기 위한 그래픽 처리 장치 GPU가 존재한다.

CPU가 전용 메모리(RAM)로부터 데이터를 읽어와서 처리하듯이, GPU는 GPU의 메모리인 VRAM을 사용한다.

이 GPU 메모리에는 텍스처 및 메시 데이터 등 렌더링에 필요한 데이터들이 포함되어 있다. GPU는 이 데이터들을 이용하여 그래픽 처리를 수행하며, 렌더링 결과를 저장하는 버퍼들 또한 이 메모리에 존재한다.

(단, 모바일 기기에서는 하나의 물리적 RAM에 CPU 메모리와 GPU 메모리를 나누어서 사용하기도 한다.)

 

이전 포스팅에서 설명했듯이, 유니티는 그래픽스 API를 이용해서 화면을 렌더링하는데, 이 그래픽스 API 간에는 그래픽스 렌더링 파이프라인 이라는, 공통적인 기반이 존재한다. 그래픽스 API들은 그래픽스 데이터를 활용하여 렌더링 파이프라인을 거치며 화면을 렌더링한다.

 


 

게임 루프

 

먼저, 게임 루프 가 무엇일까??
: 유저 게임 실행 -> 데이터 로딩 등의 초기화 -> 데이터 업데이트 (물리, 애니메이션 등) -> 화면에 렌더링 -> 리소스 해제하며 게임 종료

위 과정이 바로 게임 루프이다.

 

 

화면에 캐릭터가 그려지려면, 캐릭터들의 위치나 애니메이션 포즈 등이 먼저 정해져야 한다. 실제로 프레임 단위로 뜯어보면, 캐릭터의 위치가 조금씩 다른 것을 볼 수 있다. 한 프레임마다 화면 구성요소들의 연산이 수행된 후, 렌더링 될 사물들의 모습이 결정난 후, 화면에 그려진다. 

 

매 프레임마다 연산되어야 할 요소 예시
- 오브젝트 간 물리 연산
- 입력 장치 신호 (키보드, 마우스, 화면 터치 등)
- 적이나 NPC의 AI
- 게임 로직 처리 (코드) 연산
- 캐릭터나 적, NPC 등의 애니메이션 모션 처리
- 네트워크 처리
- 오디오 처리

 

프레임마다 게임이 연산되고 렌더링되는 것을 반복하는 것이다. 이 과정을 다음과 같이 요약할 수 있다.

1. 리소스가 생성되는 초기화(Initializing) 과정
2. 물리, 입력, 로직 등이 연산되는 업데이트(Update) 과정
3. 오브젝트들을 화면에 그려 주는 렌더(Render) 과정
4. 종료할 때 리소스 해제(Decommissioning) 과정

 

 


 

렌더링 루프

 

오브젝트들의 위치 및 포즈 등이 결정되고 나면 그것에 밪게 오브젝트들을 렌더링해줄 차례이다.

이때, 오브젝트들이 화면에 뿅! 하고 나타나는 것 같지만, 그렇지 않다.

(실제로 유니티의 Frame Debugger를 사용해보면, 오브젝트들이 순차적으로 렌더링되는 것을 볼 수 있다!!!! Custom scriptable rendering feature 연습할 때 많이 쓴 기능. 그냥 써 봐도 흥미로움)

 


 

렌더링 파이프라인

 

오브젝트 하나를 렌더링하기 위해서는 아래와 같이 많은 데이터가 필요하다.

- Mesh
- 텍스처에 대한 정보 (Albedo, Normal, Specular 등)
- 쉐이더
- Transform
등등

 

이 정보들은 하나의 오브젝트가 렌더링되는 과정에서 일련의 순서에 필요하다. 이 과정을 렌더링 파이프라인 (혹은 그래픽스 파이프라인) 이라고 하는 것이다.

이 렌더링 파이프라인을 아주 큰 단위로 구분해 보면 다음과 같다.

어플리케이션 ➡️ 지오메트리 ➡️ 래스터라이저

 

그럼 이제 각 순서가 무엇인지 살펴보자~~!

 

 

(1) 어플리케이션 단계

어플리케이션 단계에서는 데이터 업데이트 및 처리가 이루어진다.

사실, 렌더링 파이프라인은 보통 GPU 파이프라인을 의미하므로 CPU에서 연산 되는 어플리케이션 단계는 보통 렌더링 파이프라인에서 떼어놓는다.

하지만, 본격적인 렌더링 파이프라인에 진입하기 전에 CPU에서 필요한 연산을 처리해야 하기 때문에, 큰 의미로는 렌더링 파이프라인의 일부가 될 수 있다.

특히, 어플리케이션 단계에서 컬링 연산을 통해 현재 프레임에서 렌더링할 오브젝트들을 결정한다!! + 배칭 처리를 위한 연산도 이 단계에서 이루어진다.

 

(2) 지오메트리 단계

지오메트리 단계에서는 버텍스와 폴리곤 처리를 담당한다. 이 단계에서 처리되는 기능은 아래와 같다.

- 월드-뷰-프로젝션 변환 (각 공간의 좌표를 행렬로 변환하는 그거 맞다. 쉐이더 코드 작성할 때, 버텍스 쉐이더에서 오브젝트 좌표 -> 월드 좌표 -> 뷰 좌표 를 따로 다룰 수 있었다.)
- 클리핑 (Clipping)
- 버텍스 쉐이더

 

(3) 래스터라이저 단계

래스터라이저 단계에서는 오브젝트를 그릴 픽셀들을 추리고, 그 픽셀의 색을 결정한다. 버텍스 쉐이더(+ 지오메트리 쉐이더)에서 지오메트리들이 구축되면, 메시의 폴리곤에 속한 영역을 픽셀로 매칭시킨다.

이 래스터라이저 단계에서 이용되는 개념은 아래와 같다.

- 뎁스 버퍼 (= Z 버퍼)
- 프래그먼트 쉐이더
- 알파 블렌딩

 

뎁스 버퍼에는 각 픽셀의 깊이값이 저장되어 있다. 깊이값은 카메라로부터의 거리를 뜻한다.

Z 버퍼는 각 픽셀마다 존재한다. 이게 무슨 말이냐면, 만약 오브젝트 3개(A,B,C)가 카메라 앞에 수직으로 나열되어있다고 치자. (카메라 - A - B - C 이렇게 나열되어 있다고 가정)
A, B, C가 렌더링될 픽셀은 동일할 것이다. (일렬로 쭉 서있으니깐)
그렇다고 A,B,C를 한 픽셀에 모두 렌더링할수는 없다.
즉, 카메라로부터 각 오브젝트까지의 거리를 비교해서, 렌더링할 오브젝트를 결정해야 한다.
이때 사용되는 게 깊이버퍼다. 한 픽셀에 A,B,C 각각의 깊이값을 저장해놓고, 가장 그 값이 가까운 오브젝트만 렌더링하는 것이라고 생각하면 된다.

 

프래그먼트 쉐이더는 픽셀들의 최종 렌더링 색을 계산한다.

텍스처들로부터 색상을 읽어와 적용하거나, 그림자를 적용하는 등 색상에 관련된 작업을 처리한다.

 

알파 블렌딩은 투명도를 가지는 오브젝트가 뒷 배경의 색과 섞이도록(블렌딩) 하는 과정이다.

(알파 블렌딩은 모드가 다양하며, 각 모드에 따라 블렌딩 방법이 다르다.)

728x90