이 글은 Unity 프로젝트를 위한 최적화 팁을 소개하는 시리즈 중 두 번째 글입니다. 더 적은 리소스로 더 높은 프레임 속도로 실행하기 위한 가이드로 활용하세요. 이러한 모범 사례를 시도해 본 후에는 시리즈의 다른 페이지도 확인해 보세요:
유니티의 그래픽 툴을 사용하면 모바일부터 하이엔드 콘솔, 데스크톱에 이르기까지 다양한 플랫폼에서 어떤 스타일로든 최적화된 그래픽을 제작할 수 있습니다. 이 프로세스는 일반적으로 아트 디렉션과 렌더 파이프라인에 따라 달라지므로 시작하기 전에 사용 가능한 렌더 파이프라인을 검토하는 것이 좋습니다.
렌더 파이프라인을 선택할 때는 다음 사항을 고려하세요. 파이프라인을 선택하는 것과 함께 렌더링 경로를 선택해야 합니다.
렌더링 경로는 조명 및 음영과 관련된 특정 일련의 작업을 나타냅니다. 렌더링 경로를 결정하는 것은 애플리케이션의 요구 사항과 대상 하드웨어에 따라 다릅니다.
포워드 렌더링 경로
포워드 렌더링은 유니버설 렌더 파이프라인 (URP)과 빌트인 렌더 파이프라인. 포워드 렌더링에서는 그래픽 카드가 지오메트리를 투영하여 버텍스로 분할합니다. 이러한 정점은 다시 조각 또는 픽셀로 세분화되어 화면에 렌더링되어 최종 이미지를 생성합니다.
파이프라인은 각 오브젝트를 한 번에 하나씩 그래픽 API로 전달합니다. 포워드 렌더링에는 각 라이트에 대한 비용이 발생하므로 씬에 라이트가 많을수록 렌더링 시간이 더 오래 걸립니다.
내장된 파이프라인의 포워드 렌더러는 오브젝트당 별도의 패스로 각 조명을 그립니다. 여러 개의 라이트가 동일한 게임 오브젝트에 닿으면 겹치는 영역이 같은 픽셀을 두 번 이상 그려야 하는 상당한 오버드로가 발생할 수 있습니다. 오버드로를 줄이려면 실시간 라이트의 수를 최소화하세요.
URP는 라이트당 하나의 패스를 렌더링하는 대신 오브젝트별로 조명을 컬링합니다. 이를 통해 단일 패스로 조명을 계산할 수 있으므로 빌트인 렌더 파이프라인의 포워드 렌더러에 비해 드로 콜 수가 줄어듭니다.
빌트인 렌더 파이프라인, URP, 고해상도 렌더 파이프라인 (HDRP)도 디퍼드 셰이딩 렌더링 경로를 사용합니다. 디퍼드 셰이딩에서는 오브젝트별로 조명이 계산되지 않습니다.
대신 디퍼드 셰이딩은 조명과 같은 무거운 렌더링을 나중 단계로 연기하고 두 번의 패스를 사용합니다. 첫 번째 패스에서 G-버퍼 지오메트리 패스라고도 하는 첫 번째 패스에서 Unity는 게임 오브젝트를 렌더링합니다. 이 패스는 여러 유형의 지오메트리 프로퍼티를 검색하여 텍스처 세트에 저장합니다.
G-버퍼 텍스처에는 다음이 포함될 수 있습니다:
- 확산 및 스페큘러 색상
- 표면 매끄러움
- 오클루전
- 월드 스페이스 노멀
- 방출 + 앰비언트 + 반사 + 라이트맵
두 번째 패스, 즉 라이팅 패스에서 Unity는 G-버퍼를 기반으로 씬의 조명을 렌더링합니다. 각 픽셀을 반복하고 개별 오브젝트가 아닌 버퍼를 기반으로 조명 정보를 계산한다고 상상해 보세요. 디퍼드 셰이딩에서 그림자를 드리우지 않는 라이트를 더 추가해도 포워드 렌더링과 같은 성능 저하가 발생하지 않습니다.
렌더링 경로를 선택하는 것 자체가 최적화는 아니지만 프로젝트를 최적화하는 방법에 영향을 줄 수 있습니다. 이 섹션의 다른 기술과 워크플로는 선택한 렌더링 파이프라인과 경로에 따라 달라집니다.
HDRP와 URP는 모두 셰이더 생성을 위한 노드 기반 시각적 인터페이스인 셰이더 그래프를 지원합니다. 셰이더 프로그래밍 경험이 없는 사용자도 복잡한 셰이딩 효과를 만들 수 있습니다.
현재 셰이더 그래프에서 150개 이상의 노드를 사용할 수 있습니다. 또한 API를 사용하여 자신만의 커스텀 노드를 만들 수도 있습니다.
셰이더 그래프의 각 셰이더는 그래프의 출력을 결정하는 마스터 노드에서 시작됩니다. 시각적 인터페이스 내에서 노드와 연산자를 추가하고 연결하여 셰이더 로직을 구성합니다.
그런 다음 셰이더 그래프는 렌더 파이프라인의 백엔드로 전달됩니다. 최종 결과물은 HLSL 또는 Cg로 작성된 셰이더와 기능적으로 유사한 ShaderLab 셰이더입니다.
셰이더 그래프 최적화는 기존 HLSL 또는 Cg 셰이더에 적용되는 많은 규칙을 따르며, 중요한 규칙 중 하나는 셰이더 그래프 처리량이 많을수록 애플리케이션의 성능에 더 많은 영향을 미친다는 점입니다.
CPU를 사용하는 경우 셰이더를 최적화해도 프레임 속도가 향상되지는 않지만 모바일 플랫폼의 경우 배터리 수명이 향상될 수 있습니다.
GPU를 사용하는 경우 셰이더 그래프 성능 향상을 위한 다음 지침을 따르세요:
사용하지 않는 노드를 제거합니다: 필요한 경우가 아니라면 기본값을 변경하거나 노드를 연결하지 마세요. 셰이더 그래프는 사용하지 않는 기능을 자동으로 컴파일합니다. 가능하면 값을 텍스처로 굽습니다. 예를 들어 노드를 사용하여 텍스처를 밝게 하는 대신 텍스처 에셋 자체에 추가 밝기를 적용합니다.
가능하면 더 작은 데이터 형식을 사용하세요: 프로젝트에서 허용하는 경우 Vector3 대신 Vector2를 사용하거나 정밀도를 낮추는 것(예: float 대신 절반)을 고려하세요.
수학 연산을 줄입니다: 셰이더 연산은 초당 여러 번 실행되므로 가능하면 수학 연산자를 최적화하는 것이 좋습니다. 논리적 분기를 만드는 대신 결과를 혼합하는 것을 목표로 합니다. 벡터를 적용하기 전에 상수를 사용하고 스칼라 값을 결합합니다. 마지막으로 인스펙터에 표시할 필요가 없는 프로퍼티는 모두 인라인 노드로 변환합니다. 이러한 모든 점진적인 개선 사항은 프레임 예산에 도움이 될 수 있습니다.
미리 보기를 분기합니다: 그래프가 커질수록 컴파일 속도가 느려질 수 있습니다. 현재 미리 보고 싶은 작업만 포함된 별도의 작은 브랜치로 워크플로를 간소화하세요. 그런 다음 원하는 결과를 얻을 때까지 이 작은 분기에 대해 더 빠르게 반복합니다. 브랜치가 마스터 노드에 연결되어 있지 않은 경우, 그래프에서 미리보기 브랜치를 안전하게 그대로 두어도 됩니다. 유니티는 컴파일 중에 최종 출력에 영향을 주지 않는 노드를 제거합니다.
수동으로 최적화합니다: 숙련된 그래픽 프로그래머도 셰이더 그래프를 사용하여 스크립트 기반 셰이더를 위한 상용구 코드를 작성할 수 있습니다. 셰이더 그래프 에셋을 선택한 다음 컨텍스트 메뉴에서 셰이더 복사를 선택합니다. 새 HLSL/Cg 셰이더를 만든 다음 복사한 셰이더 그래프에 붙여넣습니다. 이 작업은 단방향 작업이지만 수동 최적화를 통해 추가 성능을 끌어올릴 수 있습니다.
그래픽 설정(편집 > 프로젝트 설정 > 그래픽)에 있는 항상 포함된 셰이더라는 목록에서 사용하지 않는 셰이더를 모두 제거합니다. 애플리케이션의 수명에 필요한 셰이더를 여기에 추가합니다.
셰이더 컴파일 프래그마 지시문을 사용하여 셰이더 컴파일을 각 대상 플랫폼에 맞게 조정할 수 있습니다. 그런 다음 셰이더 키워드(또는 셰이더 그래프 키워드 노드)를 사용하여 특정 기능을 활성화 또는 비활성화한 셰이더 배리언트를 생성합니다.
셰이더 배리언트는 플랫폼별 기능에 유용할 수 있지만 빌드 시간과 파일 크기를 증가시킬 수도 있습니다. 셰이더 배리언트가 필요하지 않다는 것을 알고 있다면 빌드에 포함되지 않도록 설정할 수 있습니다.
먼저 Editor.log 를 파싱하여 셰이더 타이밍과 크기를 확인합니다. 그런 다음 컴파일된 셰 이더와 압축된 셰이더로 시작하는 줄을 찾습니다.
이 예제 로그는 다음 통계를 보여줍니다:
31.23초 만에 컴파일된 셰이더 'TEST 표준(스페큘러 설정)'
d3d9(총 내부 프로그램: 482, 고유: 474)
3D11(총 내부 프로그램: 482, 고유: 466)
메탈(전체 내부 프로그램: 482, 고유: 480)
GL코어(총 내부 프로그램: 482, 고유: 454)
d3d9의 압축 셰이더 'TEST 표준(스페큘러 설정)'을 1.04MB에서 0.14MB로 줄였습니다.
d3d11의 압축 셰이더 'TEST 표준(스페큘러 설정)'을 1.39MB에서 0.12MB로 줄였습니다.
메탈의 압축 셰이더 'TEST 표준(스페큘러 설정)'을 2.56MB에서 0.20MB로 줄였습니다.
glcore의 압축 셰이더 'TEST 표준(스페큘러 설정)'을 2.04MB에서 0.15MB로 줄였습니다.
이 통계는 셰이더에 대한 몇 가지 정보를 알려줍니다:
- 프라그마 멀티 컴파일과 셰이더 기능으로 인해 482개의 변종으로 확장됩니다.
- 유니티는 게임 데이터에 포함된 셰이더를 대략적으로 압축된 크기의 합으로 압축합니다: 0.14+0.12+0.20+0.15 = 0.61 MB.
- 런타임 시 Unity는 압축된 데이터를 메모리(0.61MB)에 보관하며, 현재 사용 중인 그래픽스 API의 데이터는 압축되지 않은 상태로 유지합니다. 예를 들어 현재 사용 중인 API가 Metal인 경우 2.56MB를 차지합니다.
빌드 후 프로젝트 감사자 (실험적)는 Editor.log를 구문 분석하여 프로젝트에 컴파일된 모든 셰이더, 셰이더 키워드 및 셰이더 배리언트 목록을 표시할 수 있습니다. 또한 Player.log 를 분석할 수도 있습니다. 애플리케이션이 런타임에 실제로 어떤 변형을 컴파일하고 사용했는지 보여줍니다.
이 정보를 사용하여 스크립트 가능한 셰이더 스트리핑 시스템을 구축하고 배리언트 수를 줄일 수 있습니다. 이렇게 하면 빌드 시간, 빌드 크기, 런타임 메모리 사용량을 개선할 수 있습니다.
이 프로세스를 자세히 알아보려면 스크립터블 셰이더 배리언트 스트리핑 문서를 읽어보세요.
안티앨리어싱은 들쭉날쭉한 가장자리를 줄이고 스페큘러 앨리어싱을 최소화하여 이미지 품질을 더욱 선명하게 만듭니다.
빌트인 렌더 파이프라인과 함께 포워드 렌더링을 사용하는 경우, 멀티샘플 안티 에일리어싱 (MSAA)는 품질 설정에서 사용할 수 있습니다. MSAA는 고품질 앤티 앨리어싱을 생성하지만 비용이 많이 들 수 있습니다. 드롭다운 메뉴의 MSAA 샘플 수라는 설정은 렌더러가 효과를 평가하는 데 사용하는 샘플 수(없음, 2배, 4배, 8배)를 정의합니다. URP 또는 HDRP와 함께 포워드 렌더링을 사용하는 경우, MSAA를 활성화할 수 있습니다. URP 에셋 또는 HDRP 에셋 에서 각각 활성화하면 됩니다.
또는 후처리 효과로 앤티 앨리어싱을 추가할 수도 있습니다. 카메라 컴포넌트( 앤티 앨리어싱 아래)에 몇 가지 옵션과 함께 표시됩니다:
- 빠른 근사 안티앨리어싱 (FXAA)은 픽셀 단위로 가장자리를 부드럽게 처리합니다. 리소스를 가장 적게 사용하는 앤티 앨리어싱 유형입니다. 최종 이미지가 약간 흐릿해집니다.
- 서브픽셀 형태학적 앤티 앨리어싱 (SMAA)은 이미지의 테두리를 기준으로 픽셀을 블렌딩합니다. FXAA보다 훨씬 선명한 결과물을 제공하며 평면적이고 만화 같거나 깔끔한 아트 스타일에 적합합니다.
HDRP에서는 추가 옵션을 사용하여 카메라의 포스트 프로세싱 안티 에일리어싱에서 FXAA 및 SMAA를 사용할 수도 있습니다:
- TAA(템포럴 앤 티 앨리어싱 )는 히스토리 버퍼의 프레임을 사용하여 가장자리를 부드럽게 처리합니다. FXAA보다 더 효과적으로 작동하지만 작동하려면 모션 벡터가 필요합니다. TAA는 앰비언트 오클루전 및 볼류메트릭스를 개선할 수도 있습니다. 일반적으로 FXAA보다 품질이 높지만 추가 리소스가 필요하고 간혹 고스트 아티팩트가 발생할 수 있습니다.
가장 빠르게 조명을 생성하는 옵션은 프레임별로 계산할 필요가 없는 옵션입니다. 라이트매핑을 사용하면 스태틱 조명을 실시간으로 계산하는 대신 한 번만 베이크할 수 있습니다.
다음을 사용하여 스태틱 지오메트리에 극적인 라이팅을 추가합니다. 글로벌 일루미네이션 (GI)을 사용하여 극적인 조명을 추가하세요. 오브젝트에 대한 GI 기여 옵션을 체크하면 고품질 조명을 라이트맵 형태로 저장할 수 있습니다.
라이트맵 환경을 생성하는 프로세스는 씬에 조명을 배치하는 것보다 시간이 오래 걸리지만 다음과 같은 주요 이점을 제공합니다:
- 픽셀당 2개 조명의 경우 2~3배 빠르게 실행
- 사실적인 직간접 조명을 계산할 수 있는 글로벌 일루미네이션을 통해 비주얼을 개선하고, 라이트매퍼가 결과 맵을 부드럽게 하고 노이즈를 제거합니다.
- 구운 그림자와 조명은 일반적으로 실시간 조명과 그림자에서 발생하는 성능 저하 없이 렌더링됩니다.
복잡한 장면일수록 베이크 시간이 오래 걸릴 수 있습니다. 하드웨어에서 지원하는 경우 프로그레시브 GPU 라이트매퍼 (미리보기에서)를 지원하는 경우 이 옵션을 사용하면 CPU 대신 GPU를 사용하여 라이트맵 생성 속도를 크게 높일 수 있습니다.
이 가이드를 따라 Unity에서 라이트매핑을 시작하세요.
리플렉션 프로브는 사실적인 리플렉션을 만들 수 있지만, 배치 측면에서 비용이 많이 들 수 있습니다. 따라서 성능에 미치는 영향을 최소화하려면 다음 최적화 팁을 사용해 보세요:
- 저해상도 큐브맵, 컬링 마스크, 텍스처 압축을 사용하여 런타임 성능을 개선하세요.
- 입력합니다: 구운 프레임별 업데이트를 피하기 위해.
- 를 사용하는 경우 입력합니다: URP에서는 실시간 이 필요하므로 가능한 한 모든 프레임을 피하세요. 조정 새로 고침 모드 및 시간 분할 설정을 조정하여 업데이트 속도를 줄이세요. 스크립팅을 통해 옵션을 사용하여 새로 고침을 제어하고 사용자 지정 스크립트에서 프로브를 렌더링할 수도 있습니다.
- 를 사용하는 경우 입력합니다: HDRP에서 실시간 이 필요한 경우 주문형 모드를 선택합니다. 프로젝트 설정 > HDRP 기본 설정에서 프레임 설정을 수정할 수도 있습니다.
- 실시간 반영에서 품질과 기능을 줄여 성능을 개선하세요.
그림자 드리우기는 메시 렌더러와 라이트별로 비활성화할 수 있습니다. 그림자 그리기 호출을 줄이려면 가능하면 그림자를 비활성화하세요. 캐릭터 아래에 간단한 메시 또는 쿼드에 적용된 흐릿한 텍스처를 사용하여 가짜 그림자를 만들 수도 있습니다. 그렇지 않으면 커스텀 셰이더로 블롭 그림자를 만들 수 있습니다.
특히 포인트 라이트에 그림자를 활성화하지 마세요. 그림자가 있는 각 포인트 라이트에는 라이트당 6개의 섀도 맵 패스가 필요하며, 이는 스포트라이트의 섀도 맵 패스 1개와 비교됩니다. 동적 그림자가 꼭 필요한 경우 포인트 조명을 스팟 조명으로 교체하는 것이 좋습니다. 동적 그림자를 피할 수 있다면 큐브맵을 Light.cookie 를 포인트 라이트와 함께 사용하세요.
경우에 따라 여러 개의 조명을 추가하는 대신 간단한 트릭을 적용할 수 있습니다. 예를 들어 림 조명 효과를 내기 위해 카메라에 직접 비추는 조명을 만드는 대신 셰이더를 사용하여 림 조명을 시뮬레이션합니다(HLSL에서 이를 구현하려면 표면 셰이더 예제를 참조하세요).
조명이 많은 복잡한 씬의 경우 레이어를 사용하여 오브젝트를 분리한 다음 각 조명의 영향을 특정 컬링 마스크에 한정합니다.
라이트 프로브는 씬의 빈 공간에 대한 베이크된 조명 정보를 저장하는 동시에 고품질 조명(직접 및 간접 조명 모두)을 제공합니다. 다이나믹 라이트에 비해 빠르게 계산되는 구형 고조파를 사용합니다. 이는 일반적으로 베이크된 라이트매핑을 받을 수 없는 움직이는 오브젝트에 특히 유용합니다.
라이트 프로브는 스태틱 메시에도 적용할 수 있습니다. 메시 렌더러 컴포넌트에서 글로벌 조명 받기 드롭다운 메뉴를 찾아 라이트맵에서 라이트 프로브로 토글합니다.
눈에 띄는 레벨 지오메트리에는 라이트매핑을 계속 사용하되, 작은 디테일을 조명할 때는 라이트 프로브로 전환하세요. 라이트 프로브 조명에는 적절한 UV가 필요하지 않으므로 메시를 풀어야 하는 추가 단계를 줄일 수 있습니다. 프로브는 라이트맵 텍스처를 생성하지 않기 때문에 디스크 공간도 절약할 수 있습니다.
자세한 내용은 라이트 프로브를 사용한 스태틱 라이팅 포스팅과 Unity에서 사실적인 비주얼 만들기 포스팅을 참조하세요.
가장 포괄적인 가이드 중 하나로, PC 및 콘솔용 게임을 최적화하는 방법에 대한 80개 이상의 실행 가능한 팁을 모았습니다. 유니티의 성공 및 가속화 솔루션 전문 엔지니어가 작성한 이 심층적인 팁은 Unity를 최대한 활용하고 게임 성능을 향상하는 데 도움이 될 것입니다.