일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 리팩토링
- unity
- 에러
- 깃허브
- 애니메이션
- tutorial
- 리깅
- Final IK
- 쓰는 법
- 2판
- 파이널 IK
- error
- 튜토리얼
- 속성
- 유니티 그래픽스 최적화 스타트업
- github
- 애님
- 프로퍼티
- 최적화
- NavMesh
- shader
- 쉐이더
- 유니티 그래픽 최적화
- c#
- 오류
- 유니티
- 사용법
- 메모리
- Effective C#
- 익명 타입
- Today
- Total
참치김밥은 최고의 한식이다
[유니티] 유니티 그래픽스 최적화 : 렌더링 파이프라인 본문
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 각각의 깊이값을 저장해놓고, 가장 그 값이 가까운 오브젝트만 렌더링하는 것이라고 생각하면 된다.
프래그먼트 쉐이더는 픽셀들의 최종 렌더링 색을 계산한다.
텍스처들로부터 색상을 읽어와 적용하거나, 그림자를 적용하는 등 색상에 관련된 작업을 처리한다.
알파 블렌딩은 투명도를 가지는 오브젝트가 뒷 배경의 색과 섞이도록(블렌딩) 하는 과정이다.
(알파 블렌딩은 모드가 다양하며, 각 모드에 따라 블렌딩 방법이 다르다.)
'Unity > 최적화' 카테고리의 다른 글
[유니티] 유니티 그래픽스 최적화 : 라이팅 (1) | 2024.02.26 |
---|---|
[유니티] 유니티 그래픽스 최적화 : 드로우콜과 배칭 (2) | 2024.02.26 |
[유니티] 유니티 그래픽스 최적화 스타트업 : 그래픽스 API (0) | 2024.02.23 |
[유니티] 메모리 최적화를 위한 에셋 관리 (0) | 2024.02.20 |
[유니티] 그래픽 최적화 : Shadow Map (0) | 2024.02.20 |