본문 바로가기
최적화

튜닝<그래픽>

by JunHDev 2025. 6. 17.

 

 

 

1.해상도 조정

=============================================

● 세팅

◎ 렌더링 파이프라인 중 프래그먼트 셰이더의 비용은 렌더링하는 해상도에 비례하여 증가한다. 특히 최근 모바일 디바이스의 해상도가 높아서 적절한 값으로 조정해야 한다.

 

DPI 설정

◎ PlayerSettings의 해상도 관련 항목에 포함된 Resoultion ScalingMode를 Fixed DPI 로 설정하면 특정 DPI를 목표로 해상도를 낮출 수 있다.

 

 

최종적인 드로잉 해상도는 Target DPI의 값과 Quality Setings에 포함된 Resoultion Scalling DPI Scale Factor 의 값이 곱해져 결정된다. 

 

 

 

 

2. 반투명과 오버드로우 

============================================

● 오버드로우

 반투명 마테리얼 사용은 오버드로우를 증가시키는 주요 원인이다. 오버드로우는 화면의 한 픽셀에 대해 여러번 조각을 그리는 것으로, 조각 셰이더의 부하와 비례하여 성느에 영향을 미친다. 특히 파티클 시스템 등에서 반투명 파티클을 대량으로 생성하는 경우 오버드로우가 많이 발생하여 조심해야한다.

오버드로우로 인한 렌더링 부하 증가를 줄이기 위해서는 다음과 같은 방법을 진행하면 좋다.

 

◎불필요한 그리기 영역 줄이기

=>텍스처가 완전히 투명한 영역도 그리기 대상이 되므로 가급적 줄인다.

 

◎오버드로우가 발생할 수 있는 오브젝트에는 가급적 가벼운 셰이더를 사용한다.

 

◎  반투명 마테리얼은 가급적 사용하지 않는다.

=> 불투명 마테리어로 유사하게 반투명해 보이는 효과를 낼 수 있다. 디더링 기법도 고려한다. 

 

URP에서는 Scene Debug View Modes를 통해 오버드로우를 시각화할 수 있다. 

 

 

3. 드로우콜 감소  

============================================

드로우콜의 증가는 CPU 부하에 많은 영향을 미친다. 유니티에는 여러 방법으로 튜닝이 가능한 기능이 있다.

 

● 동적 배칭

◎ 동적배칭은 동적 오브젝트를 런타임에 일괄 처리를 할 수 있는 기능이다.

이 기능을 사용하면 동일한 머터리얼을 사용하는 다이나믹 오브젝트의 드로우콜을 통합하여 줄일 수 있다.

 

URP 에셋에서 Dynamic Batching 항목을 활성화한다.

단 URP에서는 사용을 권하지 않는다.

다이나믹 배칭은 CPU 비용을 사용하는 처리라서 오브젝트에 적용하기 위해서는 여러 가지 조건을 충족해야 한다.

1. 동일한 마테리얼 참조

2.메시렌더러또는 파티클시스템으로 렌더링 중

3.메시의 버텍스 수가 30개 이하인 겨우

4.실시간 그림자의 영향을 받지 않음.

 

● 정적 배칭

◎정척 배치는 씬에서 움직이지 않는 오브젝트를 배치하는 기능이다. 이 기능을 사용하면 동일한 마테리얼을 사용하는 정적 오브젝트의 드로우콜을 줄일 수 있다.

동적 배칭과 마찬가지로 플레이어 세팅에서 스테틱 배칭을 활성화하여 사용할 수 있다.

 

오브젝트를 스태틱 배칭 대상으로 설정하려면, 오브젝트의 static flag를 활성화해야 한다.

구채적으로는 static 플래그 중 Batching Static이라는 하위 플래그를 활성화해야한다. 

 

 

정적 배칭은 동적 배칭과 달리 런타임에 버텍스 변환 처리를 하지 않아 낮은 부하로 실행할 수 있으나...ㅇㄹ괄 처리로 결합된 메시 정보를 저장해서 메모리를 많이 소모하는 점에 주의해야한다.

 

4. GPU 인스턴싱

============================================

 

GPU 인스턴싱은 동일 메시,동일 마테리얼의 오브젝트를 적은 드로우콜로 그리기 위한 기능이다.

풀이나 나무처럼 같은 메시를 여러 번 그리는 경우 도르오쿨을 줄일 수 있다.

GPU 인스턴싱을 사용하기 위해서는 마테리얼의 인스펙터에서 Enable Instancing을 활성화해야한다. 

 

GPU인스턴싱에서 셰이더 코드중에 대상 프로퍼티를 를 UNITY_I NSTANCING_BUFFER_START(Props) 과 UNITY_INSTANCING_BUFFER_END(Props)으로 둘러싸서 개별적으로 변경하는 프로퍼티로 설정할 수 있다.

 

이 프로퍼티를 C#에서 MaterialPropertyBlock의 API를 사용하여 변경하여 개별색상 등의 프로퍼티를 설정할 수 있다.

단 많은 인스턴스에 대해 해당 기능을 사용하면 CPU에 영향을 줄 수있다. 

 

즉 마테리얼 프로퍼티 블락은 마테리얼에 변화가 있어도 선언을 하면 배칭이 꺠지지 않도록 해준다. 

 

 

5. SRP Batcher

============================================

 

● SRP Batcher은 SRP 에서만 사용할 수 있는 렌더링의 CPU 비용을 절감하는 기능이다.

해당 기능을 사용하면 셰이더 번형을 사용하는 여러 셋패스 콜을 일괄적으로 처리할 수 있다.

SRP Bathcer을 사용하려면,SRP에셋의 인스펙터에서 해당 항목을 활성화한다. 

 

셰이더의 Inspector에서 SRP Bathcer 항목이 compatible로 되어 있으면 SRP Batcher을 지원 not compatible로 되어 있으면 지원을 하지 않는다.

 

6. SpriteAtlas

============================================

 

● 2D 게임이나 UI에서는 많은 스프라이트들을 사용하여 화면을 구성하는 경우가 많다.

이러한 경우, 많은 양의 드로우 콜을 발생시키지 않기 위한 기능이 바로 SpriteAtlas인데 해당 기능을 사용하면 여러개의 스프라이트를 하나의 텍스처로 묶어 드로우콜을 줄일수 있다.

 

주의할 점은 SpriteAtlas에 포함된 스프라이트를 하나 가지고오면 전체 텍스처를 불러와서 많은 메모리를 사용하게 된다.

따라서 SpriteAtlas를 적절히 분할하는 등 주의해서 사용한다.

 

7. 컬링

==========================================

 유니티에서는 최종적으로 화면에 표시되지 않는 부분의 처리를 미리 생략하기 위한 컬링이라는 처리가 이루어진다.

 

◎ 원추형 컬링

시스루 컬링은 카메라의 렌더링 범위가 되는 시준대 바깥에 있는 오브젝트를 렌더링 대상에서 제외하는 처리이다.

이를 통해 카메라 범위 밖에 있는 오브젝트는 렌더링 계산이 이루어지지 않게 된다.

 

시스루 컬링은 별도의 설정을 하지 않아도 기본적으로 수행된다. 버텍스 셰이더 부하가 높은 오브젝트 등의 경우,

메시를 적절히 분할하여 컬링 대상으로 삼아 렌더링 비용을 낮추는 방법도 효과적이다.

 

◎ 후면 컬링

백컬링은 카메라에서 보이지 않는 폴리곤의 뒷면을 렌더링에서 제외하는 처리입니다. 

대부분의 메시는 앞쪽의 폴리곤만 보이기 때문에 뒷면은 그리지 않아도 괜찮다.  

 

유니티에서는 셰이더에 명시하지 않으면 폴리곤의 뒷면이 컬링의 대상이 되지만, 명시하면

컬링 설정을 바꿀 수 있다. 

 

  

1: SubShader
 2: {
 3:	    Tags { "RenderType"="Opaque" }
 4:		LOD 100
 5:
 6:		Cull Back // Front, Off
 7:
 8:		 Pass
 9:		 {
 10:		 CGPROGRAM
 11:		 #pragma vertex vert
 12: 		#pragma fragment frag
 13: 		ENDCG
 14: 	}
 15: }

 

Back,Froint,Off에 세가지 설정이 있는데 각각의 효과는 아래와 같다.

• Back- 시점과반대쪽의폴리곤을그리지않는다.

• Front- 시점과같은방향의폴리곤을그리지않는다.

• Off- 뒷면컬링을비활성화하고모든면을그립니다.

 

8. 오클루전 컬링

==========================================

◎ 오클루전 컬링은 오브젝트에 가려져 카메라에 보이지 않는 오브젝트를 그리기 대상에서 제외하는 처리이다.

이 기능은 미리 구운 차폐 판정용 데이터를 바탕으로 런타임에 오브젝트가 차폐되어 있는지 판단하여 차폐된 오브젝트를 그리기 대상에서 제외하는 기능이다.

 

오브젝트를 오클루전 컬링 대상으로 설정하려면, Inspector의 static 플래그에서 Occluder Static 또는 Occlude Static을 활성화한다.

Occluder Static을 비활성화하고 Occludee Static을 활성화하면 오브젝트를 오클루딩하는 측으로 판단하지 않고

오클루딩을 받는 측으로만 처리하게 된다.

 

오클루전 컬링을 위한 사전 베이킹을 해야한다. Occlusion Culling창 을 표시한다.

이 창에서는 각오브젝트의 스태틱 플래그 변경,베이크 설정 변경 등을 할 수 있다.

 

오클루전 컬링은 렌더링 비용을 줄이는 대신 컬링 처리를 위해 CPU에 부하를 주기 떄문에 프로파일러를 통해서

지표를 확인한 뒤 적절히 설정을 해야한다.

 


9. 셰이더

==========================================

◎ 셰이더는 그래픽 표현에 매우 효과적이지만, 종종 성능 문제를 야기합니다.

 

◎ 부동소수점 유형 정확도 저하

GPU(특히 모바일 플랫폼)의 계산 속도는 큰 데이터 유형보다 작은 데이터 유형이 더 빠르다. 따라서 대체 가능한 경우,

부동소수점 유형을 float형(32bit)에서 half형 (16bit)로 대체하는 것이 효과적이다.

 

깊이 계산 등 정밀도가 필요한 경우 float 타입을 사용해야 하지만, Color 계산 등에서는 정밀도를 떨어뜨려도 결과물의 외형에 큰 차이가 나지 않는다.

 

◎  버텍스 셰이더로 계산하기 

 버텐스 셰이더의 처리는 메시의 버텍스 수만큼만 실행되고, 프래그먼트 셰이더 처리는 최종적으로 쓰여지는 픽셀 수 만큼 실행이된다.

일반적으로 버텍스 셰이더의 실행횟수는 프래그먼트 셰이더보다 적은 경우가 많아서 복잡한 계산은 가급적 버텍스 셰이더로 하는게 좋다.

 

그렇다면 왜 버텍스 셰이더의 연산이 더 가벼운지 확인해보자

 

프래그먼트 셰이더만 사용한다면 

1. 각 픽셀마다 복잡한 계산을 반복 수행

2. normalize,dot,reflect등 연산이 수십만 번 반복되며 조명, 백터 연산에서 비용이 큰 작업은

성능에 매우 큰 부담이 된다.

 

위에서 부정적인 연산등을 효율적으로 하기 위해서는 보간이 된 데이터가 필요하다.

 

보간이 된 데이터가 생성되는 과정은 

 

버텍스 셰이더에서 필요한 정점에 대한 정보를 계산 ( 예: 색상, 텍스처 좌표, 조명 값 등)

 

해당  정보를 보간할 수 있는 형태(out)로 출력 해서 GPU에게 전달

 

GPU는 삼각형 내부 픽셀 위치를 기준으로, 정점들의 값을 픽셀마다 선형 보간

 

후에 프래그먼트 셰이더에 전달 후 보간된 값을 받아 최종 연산을 진행한다. 

 

복잡한 계산이 보간된 값을 바탕으로 수행되므로 연산량이 절감된다. 

 

조금더 깊게 들어가 보자면 프래그먼트 셰이더는 기존에 각 픽셀마다 

 

// 프래그먼트 셰이더에서 복잡한 계산을 반복
vec3 viewDir = normalize(cameraPos - fragPos);
vec3 lightDir = normalize(lightPos - fragPos);
float diffuse = max(dot(normal, lightDir), 0.0);

 

등의 연산을 진행한다. 픽셀에 1,000,000개면 픽셀의 수에 맞게 연산이 실행되어 많이 무거운데

버텍스 셰이더 같은 경우에는 각 정점마다 

// 복잡한 계산은 여기서 3번만 실행 (삼각형 기준)
vec3 lightEffect = calcLighting(normal, lightDir);
out vec3 vLight = lightEffect;

 

해당 로직을 실행해서 GPU에게 넘긴 후 보간을 진행하고

 

보간된 값으로 프래그먼트 셰이더는 그대로 적용한다.

// 보간된 값만 사용 (계산 안 함)
in vec3 vLight;
fragColor = vec4(vLight * baseColor, 1.0);

즉 연산량이 엄청나게 줄어들어 버텍스 셰이더를 사용하는게 좋다는 의미.

 

 

◎   ShaderVariantCollection

 

ShaderVariantCollection을 사용하면 셰이더를 사용하기전에 셰이더 Variant를 미리 컴파일하여 

스파이크를 방지할 수 있다.

ShaderVariantCollection는 게임 내에서 사용할 셰이더 변형 목록을 에셋으로 보관할수 있다.

 

생성한 ShaderVariantCollection의 Inspector 뷰에서 Add Shader를 눌러 대상 셰이더를 추가하고,

해당 셰이더에 대해 어떤 변형을 추가할 것인지 선택한다.

ShaderVariantCollection 을 Graphics Settings 의 Shader preloading 항목의 Preloaded Shaders 에 추가하여 애플리케이션 시작 시컴파일할셰이더변형을설정 할수있다.

 

스크립트에서ShaderVariantCollection.WarmUp() 를 호출하여 해당 ShaderVari antCollection 에 포함된 셰이더 변형을명시적으로미리컴파일할수도있다.

 

즉 ShaderVariantCollection은 셰이더를 처음 실행시킬떄 Variant에 의해 프레임 드랍이 발생하는걸 사전에 빌드하여 막아주고

유니티 자체에서 안쓰는 ShaderVariantCollection을 자동으로 필터링 해주지만 정확하지는 않아서

명시적으로 설정을 해주어 빌드 시간등에 큰 도움을 준다.

 

 


10. 라이팅

==========================================

라이팅은 게임에서 중요한 요소이며, 퍼포먼스에 큰 영향을 미치는 경우가 많다.

 

◎ 실시간 그림자

 

실시간 그림자 생성은 드로우 콜과 필레이트에 큰 영향을 미친다.

 

그림자 생성의 드로우콜을 줄이기 위해서는 다음과 같은 방법을 고려할 수 있다.

● 그림자를 드리우는 오브젝트 수 줄이기

● 일괄 처리로 드로우콜을 통합

 

그림자를 드리우는 오브젝트를 줄이는 방법은 여러가지가 있지만, 가장 간단한 방법은 MeshRender의

Cast Shadows 설정을 해제하는 것이다.

이렇게 하면 오브젝트를 그림자 그리기 대상에서 제외할 수 있다.

 

또는 QualitySettings의 Shadow Distance 항목에서 섀도맵의 최대 그리기 거리를 변경할 수 있다.

이 설정을 통해 그림자를 드리우는 오브젝트 수를 필요한 최소값으로 줄일 수 있다.

 

필레이트 절약

 

그림자에 의한 필레이트는 그림자 맵의 드로잉과 그림자의 영향을 받는 오브젝트의 드로잉 모두에 영향을 받는다.   

 

Shadows 항목에서는 그림자 형식을 변경할 수 있다.

HardShadows는 그림자 경계가 뚜렷하게 나타나지만 상대적으로 부하가 적고

SoftShadows는 그림자 경계를 흐리게 표현할 수 있지만 부하가 높다.

 

Hard Shadows 그림자의 가장자리가 뚜렷하고 날카로움
Soft Shadows 그림자의 가장자리가 부드럽게 퍼짐, 거리감에 따라 블러 처리됨

 

Shadow Resoultion의 Shadow Cascades 항목은 섀도맵의 해상도에 영향을 주는 항목으로, 크게 설정하면 섀도맵의 해상도가 높아져 필레이트 소모가 커진다. 

하지만 이 설정은 그림자의 퀄리티와도 관련이 깊어서 퍼포먼스와 퀄리티의 균형을 잘 맞춰서 조정해야한다.

일부 설정은 Light 컴포넌트 인스펙터에서 일부 설정을 변경할 수 있어서, 개별 라이트마다 설정을 변경할 수 있다.

 

'최적화' 카테고리의 다른 글

오브젝트 풀 (Object Pool)  (5) 2025.08.13
튜닝 - 물리(Physics)  (1) 2025.06.15
에셋 관련 최적화  (1) 2025.06.01
튜닝 기본 상식(2)  (0) 2025.05.17
최적화<튜닝>의 기본상식1  (4) 2025.05.11