티스토리 뷰
기존 Arm & Unity Presents: 3D Art Optimization for Mobile Applications에 없는 내용 위주로 정리
developer.arm.com/documentation/100140/0401/Preface
Documentation – Arm Developer
developer.arm.com
Unity 퀄리티 설정
약간의 성능 절충으로 게임의 이미지 품질을 향상시킬 수있는 여러 옵션이 있습니다. 예를 들어 게임의 프레임 속도가 낮은 경우 복잡한 그래픽 효과를 수행 할 때 GPU가 너무 많은 정보를 처리 할 수 있습니다. 그래픽 품질에 비교적 적은 영향을주기 위해 그림자 및 조명과 같은 덜 복잡한 버전의 그래픽 효과를 수행 할 수 있습니다. 더 단순한 효과는 GPU의 부하를 크게 줄여 더 높은 프레임 속도를 제공합니다.
라이팅의 기본 설정은 모바일 장치에 비해 너무 복잡 할 수 있으므로 모바일 플랫폼 용으로 작성된 일부 게임은 복잡한 기술을 피하거나 게임별 기술을 사용합니다. 여기에는 조명을 라이트맵에 미리 베이킹하거나 그림자를 드리우는 대신 텍스처를 투영하는 것과 같은 기술이 포함될 수 있습니다
Pixel Light Count
Pixel Light Count는 주어진 픽셀에 영향을 줄 수있는 조명의 수입니다. 픽셀 라이트 수가 많으면 많은 계산이 필요합니다. 대부분의 게임은 이미지 품질에 미치는 영향을 최소화하면서 동적 및 실시간 조명을 거의 사용할 수 없습니다. 조명으로 인해 성능 문제가 발생하는 경우 게임에서 라이트맵 및 투영된 텍스처(projected textures)와 같은 기술을 사용하는 것이 좋습니다.
Texture Quality
텍스처 품질은 GPU에 로드 걸 수 있지만 일반적으로 성능 문제를 일으키지 않습니다. 텍스처 품질을 낮추면 게임의 시각적 품질에 부정적인 영향을 미칠 수 있으므로 필요한 경우에만 품질을 낮추십시오. 텍스처가 성능 문제를 일으키는 경우 mipmap을 사용해보십시오. Mipmapping은 이미지 품질에 영향을주지 않고 컴퓨팅 및 대역폭 요구 사항을 줄입니다.
AntiAliasing
앤티 앨리어싱은 삼각형 가장자리 주변의 픽셀을 혼합하는 가장자리를 부드럽게하는 기술입니다. 이를 통해 게임의 시각적 품질이 눈에 띄게 향상됩니다. 앤티 앨리어싱에는 여러 가지 방법이 있지만이 경우 토글은 MSAA ( Multi-Sampled Anti-Aliasing ) 용입니다. 4xMSAA는 Mali GPU에서 매우 저렴한 작업이므로 가능하면 항상 사용하십시오.
Soft Particles
Soft Particles는 depth 텍스처로 렌더링하거나 디퍼드 모드에서 렌더링해야합니다. 이로 인해 GPU의 부하가 증가하지만 파티클에서 사실적인 시각적 효과를 얻을 수 있다는 점에서 그만한 가치가 있습니다. 모바일 플랫폼에서 depth텍스처를 렌더링하고 읽는 것은 귀중한 대역폭을 사용하고 deferred path를 사용하는 렌더링은 MSAA에 액세스 할 수 없음을 의미합니다. 소프트파티클이 게임에서 사용하기에 충분히 중요한지 고려하십시오. (끄라는말)
Anisotropic Textures
비 등방성 텍스처는 높은 그라데이션으로 그려진 텍스처에서 왜곡을 제거하는 기술입니다. 이것은 이미지 품질을 향상 시키지만 값 비싼 기술입니다. 왜곡이 특히 눈에 띄지 않는한 이 기술을 사용하지 마십시오.
Shadows
그림자는 고품질 인 경우 계산 집약적 일 수 있습니다. 그림자로 인해 성능 문제가 발생하면 단순한 그림자를 사용하거나 끄십시오. 게임에서 그림자가 중요한 경우 투영된 텍스처(projected textures)와 같은 간단한 동적 그림자 기법을 사용하는 것이 좋습니다.
Realtime Reflection Probes
실시간 반사 프로브 옵션은 런타임 성능에 상당한 부정적인 영향을 미칠 수 있습니다.
반사 프로브가 렌더링 될 때 큐브 맵의 모든면은 프로브의 원점에서 카메라에 의해 개별적으로 렌더링됩니다. 상호반사가 고려되면이 프로세스는 모든 반사 바운스 레벨에 대해 발생합니다. 광택 반사의 경우 큐브 맵 밉맵도 블러링 프로세스를 적용하는 데 사용됩니다.
다음 요소는 큐브 맵의 렌더링에 영향을줍니다.
* Cubemap Resolution
고해상도 큐브 맵은 렌더링 시간을 증가시킵니다. 필요한 품질에 대해 가능한 최저 해상도 큐브 맵을 사용하십시오.
* Culling Mask
큐브 맵을 렌더링 할 때 컬링 마스크를 사용하여 반사와 관련없는 지오메트리를 렌더링하지 않도록합니다.
* Cubemap Updating (큐브 맵의 업데이트 빈도를 정의)
- The Every Frame
모든 프레임에서 큐브 맵을 렌더링합니다. 이것은 계산 비용이 가장 많이 드는 옵션이므로 필요하지 않는 한 사용하지 마십시오.
- On Awake : 씬이 시작될 때 런타임에 큐브 맵을 한 번 렌더링합니다.
- The Via Scripting :
큐브맵이 업데이트되는 시기를 스크립트로 제어 할 수 있습니다.
이 옵션을 사용하면 업데이트가 발생할 때 조건을 지정하여 런타임 리소스 사용을 제한 할 수 있습니다.
애플리케이션 프로세서 최적화
Invoke() 대신 코루틴 사용
이 Monobehaviour.Invoke()메서드는 시간 지연이있는 클래스에서 메서드를 호출하는 빠르고 편리한 방법이지만 다음과 같은 제한이 있습니다.
- 호출 할 메서드를 찾기 위해 C #에서 리플렉션을 사용합니다.이 방법은 메서드를 직접 호출하는 것보다 느릴 수 있습니다.
- 메서드 서명에 대한 컴파일 타임 검사는 없습니다.
- 추가 매개 변수를 제공 할 수 없습니다.
Monobehaviour.Invoke()메서드에서 코루틴 사용으로 변경하면 애니메이션 상태를 처리하는 함수에 전달되는 매개 변수에 대해 더 많은 유연성을 제공합니다.
public void Function()
{
[...]
}
Invoke("Function", 3.0f);
public IEnumerator Function(float delay)
{
yield return new WaitForSeconds(delay);
[...]
}
StartCoroutine(Function(3.0f));
Use coroutines for relaxed updates
게임에 특정간격마다 작업이 필요한 경우 모든 프레임에(MonoBehaviour.Update())서 작업을 수행하는 대신 MonoBehaviour.Start()에서 코루틴을 시작해보십시오.
void Update()
{
// Perform an action every frame
}
IEnumerator Start()
{
while(true)
{
// Do something every quarter of second
yield return new WaitForSeconds(0.25f);
}
}
Tag에 하드 코딩 된 문자열을 사용하지 마십시오.
태그에 대해 하드 코딩 된 값은 게임의 확장 성과 견고성을 제한하므로 사용하지 마십시오. 예를 들어 태그 이름을 사용하여 문자열로 직접 이름을 참조하면 쉽게 수정할 수 없으며 맞춤법 오류에 노출 될 수 있습니다.
// 이렇게 말고
if(gameObject.CompareTag("Player"))
{
[...]
}
// 아래와 같이
public class Tags
{
public const string Player = "Player";
[...]
}
if(gameObject.CompareTag(Tags.Player))
{
[...]
}
Fixed timestep을 변경하여 물리 계산 횟수를 줄입니다.
fixed timestep를 변경하여 물리 계산의 계산 부하를 줄일 수 있습니다. 일반적으로 대부분의 물리 계산은 고정된 시간 단계에서 발생하며이 단계의 길이를 늘리거나 줄일 수 있습니다.시간 단계를 늘리면 애플리케이션 프로세서의 부하가 줄어들지 만 물리계산의 정확도가 떨어집니다.
매 프레임마다 GameObject.Find()를 부르지 마십시오.
GameObject.Find()장면의 모든 객체를 반복하는 함수입니다. 이로 인해 코드의 잘못된 부분에서 사용되는 경우 주 스레드 크기가 크게 증가 할 수 있습니다.
대신 GameObject.Find()을 Start()시 호출 하고 결과를 캐시하는 것입니다 (예 : Start() or Awake()함수).
다른 대안은 다음을 사용하는 것입니다 GameObject.FindWithTag().
void Update()
{
GameObject playerGO = GameObject.FindWithTag("Player");
playerGO.transform.Translate(Vector3.forward * Time.deltaTime);
}
tag속성 대신 CompareTag() 메서드 사용
GameObject.tag 대신 GameObject.CompareTag()메서드를 사용하십시오. 이 CompareTag()방법은 더 빠르고 추가 메모리를 할당하지 않습니다.
GameObject mainCamera = GameObject.Find("Main Camera");
// Gameobject.tag property
if(mainCamera.tag == "MainCamera")
{
// Perform an action
}
// Gameobject.CompareTag() method
if(mainCamera.CompareTag("MainCamera"))
{
// Perform an action
}
컴포넌트 검색후 캐싱
GameObject.GetComponent<Type>()에서 반환 한 구성 요소 인스턴스를 캐시합니다.
관련된 함수 호출은 상당히 비쌉니다.
GameObject.camera, GameObject.renderer 또는 GameObject.transform과 같은 속성은 GameObject.GetComponent<Camera>(), GameObject.GetComponent<Renderer>(), GameObject.GetComponent<Transform>()에 대한 숏컷 입니다.
private Transform _transform = null;
void Start()
{
_transform = GameObject.GetComponent<Transform>();
}
void Update()
{
_transform.Translate(Vector3.forward * Time.deltaTime);
}
Transform.position의 반환 값을 캐싱하는 것이 좋습니다. C# getter 속성 인 경우에도 전역 위치를 계산하기 위해 transform계층 구조에 대한 반복과 관련된 오버 헤드가 있습니다.
(In Unity 5 and higher, the transform component is automatically cached.)
OnBecameVisible () 및 OnBecameInvisible () 콜백 사용
MonoBehaviour.OnBecameVisible () 및 MonoBehaviour.OnBecameInvisible ()과 같은 콜백은 관련 게임 개체가 화면에 표시되거나 보이지 않는 경우 스크립트에 알립니다.
예를 들어, 이러한 호출을 사용하면 게임 오브젝트가 화면에 렌더링되지 않을 때 계산량이 많은 코드 루틴 또는 효과를 비활성화 할 수 있습니다.
벡터 크기 비교를 위해 sqrMagnitude 사용
응용 프로그램에 벡터 크기 비교가 필요한 경우 Vector3.Distance () 또는 Vector3.magnitude 대신 Vector3.sqrMagnitude를 사용합니다.
Vector3.sqrMagnitude는 근을 계산하지 않고 제곱 성분을 더하지만 비교에 유용합니다. 다른 호출은 계산 비용이 많이 드는 제곱근을 사용합니다.
다음 코드는 공간에서 두 위치를 비교하는 데 사용되는 세 가지 기술을 보여줍니다.
// Vector3.sqrMagnitude property
if ((_transform.position - targetPos).sqrMagnitude < maxDistance * maxDistance)
{
// Perform an action
}
// Vector3.Distance() method
if (Vector3.Distance(transform.position, targetPos) < maxDistance)
{
// Perform an action
}
// Vector3.magnitude property
if ((_transform.position - targetPos).magnitude < maxDistance)
{
// Perform an action
}
Use planes as collision targets
씬에 바닥이나 벽과 같은 평면 오브젝트와의 파티클 충돌 만 필요한 경우 파티클 시스템 충돌 모드를 평면으로 변경합니다. 평면을 사용하도록 설정을 변경하면 필요한 계산이 줄어 듭니다.
메시콜라이더 대신 복합 기본 콜라이더 사용
메시 충돌체는 개체의 실제 형상을 기반으로합니다. 충돌 감지에는 매우 정확하지만 계산 비용이 많이 듭니다.상자, 캡슐 또는 구와 같은 모양을 원래 메시의 모양을 모방 한 복합 충돌체로 결합 할 수 있습니다. 이를 통해 훨씬 낮은 계산 오버 헤드로 유사한 결과를 얻을 수 있습니다.
렌더링 순서 지정
씬에서 오브젝트 렌더링 순서는 성능에 매우 중요합니다.
오브젝트가 임의의 순서로 렌더링 된 경우 오브젝트가 렌더링 된 후 그 앞에있는 다른 오브젝트에 의해 가려 질 수 있습니다. 이것은 가려진 개체를 렌더링하는 데 필요한 모든 계산이 낭비됨을 의미합니다.
Mali-T600 시리즈부터 Arm Mali GPU에서 사용할 수있는 낭비되는 계산을 줄이기위한 하드웨어 기술 중 하나는 Early-Z입니다. Early-Z는 조각 셰이더가 실제로 처리되기 전에 Z 테스트를 수행하는 관점에서 볼 때 완전히 투명한 시스템입니다. GPU가 Early-Z 최적화를 활성화 할 수없는 경우 조각 셰이더 후에 깊이 테스트가 실행됩니다. 이것은 계산적으로 비용이 많이 들고 조각이 가려지면 계산이 낭비 될 수 있습니다. Early-Z 시스템은 처리중인 픽셀의 깊이가 더 가까운 픽셀이 이미 차지하고 있지 않은지 확인합니다. 점유 된 경우 조각 셰이더를 실행하지 않습니다. 이 시스템은 성능상의 이점을 제공하지만 조각 셰이더가에 작성하여 깊이를 수정하는 경우와 같은 경우에는 자동으로 비활성화됩니다. gl_FragDepth변수, 조각 셰이더 호출 discard 또는 투명 개체와 같은 개체에 대해 블렌딩 또는 알파 테스트가 활성화 된 경우. 이 시스템이 최대의 효율성을 달성하도록 지원하려면 불투명 한 개체가 앞에서 뒤로 렌더링되는지 확인하십시오. 이렇게 하면 불투명 한 오브젝트 만있는 장면에서 오버 드로 요소를 줄이는 데 도움이됩니다.
각 프레임의 렌더링 순서를 앞뒤로 조절하는 것은 비용이 많이들 수 있으며 동일한 패스에서 투명 개체를 렌더링하는 경우에도 올바르지 않습니다. T620 이후의 Arm Mali GPU는 PFK (Pixel Forward Kill)라는 메커니즘을 제공합니다. Mali GPU는 파이프 라인이므로 동일한 픽셀에 대해 여러 스레드가 동시에 실행될 수 있습니다. 스레드가 실행을 완료하면 PFK 시스템은 현재 픽셀이 해당 픽셀을 덮을 경우 해당 픽셀에 대한 다른 모든 스레드를 중지합니다. 그 효과는 낭비되는 계산의 감소입니다.
Unity는 셰이더 내부 또는 머티리얼에서 렌더링 순서를 지정하는 대기열 옵션을 제공합니다. 셰이더에서 설정할 수 있으므로 해당 셰이더를 사용하는 머티리얼이 있는 오브젝트가 함께 렌더링됩니다. 이 렌더링 그룹 내에서 렌더링 순서는 Transparent와 같은 일부 경우를 제외하고 무작위입니다.
기본적으로 Unity는 다음 순서로 처음부터 마지막까지 렌더링되는 몇 가지 표준 그룹을 제공합니다.
이름 | 값 | Note |
Background | 1000 | - |
Geometry | 2000 | Default, Opaque에 사용 |
AlphaTest | 3000 | 불투명이후에 그려짐 (ex. 잎) |
Transparent | 4000 | 이 그룹은 올바른 결과를 제공하기 위해 뒤에서 앞으로도 렌더링됨 |
Overlay | 5000 | 오버레이효과 - UI, 렌즈플레어 등 |
문자열 이름 대신 정수 값을 사용할 수 있습니다. 이것들은 사용 가능한 유일한 값이 아닙니다. 표시된 항목 사이의 정수 값을 사용하여 다른 대기열을 지정할 수 있습니다. 숫자가 높을수록 나중에 렌더링됩니다.
예를 들어 다음 지침 중 하나를 사용하여 도형 대기열 뒤, AlphaTest 대기열 이전에 셰이더를 렌더링 할 수 있습니다.
Tags { "Queue" = "Geometry+1" }
Tags { "Queue" = "2001" }
렌더링 순서를 사용하여 성능 향상 예
Ice Cave 데모에서 동굴은 화면의 대부분을 덮고 있으며 쉐이더는 비쌉니다. 가능한 경우 일부 렌더링을 피하면 성능이 향상 될 수 있습니다.
렌더링 순서 최적화는 Unity 프레임 디버거 및 그래픽 분석기와 같은 기타 도구를 사용하여 프레임 버퍼의 구성을 살펴본 후 포함되었습니다. 이를 통해 렌더링 순서를 볼 수 있습니다.
Unity 프레임 디버거를 열려면 메뉴 옵션 창> 프레임 디버거를 선택합니다. 에디터 모드에서는 올바로 보이지만 실행할 때 제대로 작동하지 않을 수 있기 때문에 유용합니다. 예를 들어 런타임 전용 설정이 있거나 다른 카메라에서 텍스처로 렌더링하는 경우에 해당됩니다. 플레이 모드에서 데모를 시작하고 카메라를 배치 한 후 프레임 디버거를 활성화하고 Unity에서 실행되는 일련의 드로잉을 가져올 수 있습니다.
Ice Cave 데모에서 드로우 콜을 아래로 스크롤하면 동굴이 먼저 렌더링되었음을 알 수 있습니다. 그런 다음 오브젝트는 이미 렌더링 된 동굴의 일부를 가리고 씬으로 렌더링됩니다. 또 다른 예는 일부 장면에서 동굴에 의해 가려지는 반사 크리스탈입니다. 이러한 경우 더 높은 렌더링 순서를 설정하면 가려진 크리스탈에 대해 조각 셰이더가 실행되지 않기 때문에 계산이 감소합니다.
Use depth pre-pass
오버드로를 방지하기 위해 개체의 렌더링 순서를 설정하는 것은 유용하지만 항상 각 개체의 렌더링 순서를 지정할 수있는 것은 아닙니다.
예를 들어 계산 비용이 많이 드는 셰이더가있는 Object Set가 있고 카메라가 자유롭게 회전 할 수있는 경우 뒤쪽에 있던 일부 개체가 앞으로 이동할 수 있습니다. 이 경우 이러한 객체에 대해 정적 렌더링 순서가 설정되어 있으면 일부는 가려져 있어도 마지막에 그려 질 수 있습니다. 물체가 그 자체의 일부를 덮을 수있는 경우에도 발생할 수 있습니다.
이 경우 depth 프리 패스를 사용하여 오버 드로를 줄일 수 있습니다. 깊이 프리 패스는 프레임 버퍼에 색상을 쓰지 않고 지오메트리를 렌더링합니다. 가장 가까운 가시 객체의 깊이로 각 픽셀의 깊이 버퍼를 초기화합니다. 이 프리 패스 후에는 지오메트리가 평소와 같이 렌더링되지만 Early-Z 기술을 사용하면 최종 장면에 기여하는 오브젝트 만 실제로 렌더링됩니다. 이 기법에는 추가 정점 셰이더 계산이 필요합니다. 그 이유는 정점 셰이더가 각 개체에 대해 두 번 계산되기 때문입니다. 한 번은 깊이 버퍼를 채우고 다른 하나는 실제 렌더링을 위해 계산되기 때문입니다. 이 기술은 게임이 fragment 바운드이고 vertex 셰이더에 여유가있는 경우 매우 유용합니다.
Unity에서 커스텀 셰이더가 있는 오브젝트에 대한 렌더링 pre-패스를 수행하려면 셰이더에 추가 패스를 추가합니다.
// extra pass that renders to depth buffer only
Pass {
ZWrite On
ColorMask 0
}
이 패스를 추가 한 후 프레임 디버거는 개체가 두 번 렌더링되었음을 보여줍니다. 처음 렌더링 될 때 색상 버퍼에 변경 사항이 없습니다.
'Unity > 최적화' 카테고리의 다른 글
Unity 개발자를 위한 ARM 설명서 v4.1 - part 3 (0) | 2021.04.06 |
---|---|
Unity 개발자를 위한 ARM 설명서 v4.1 - part 2 (0) | 2021.04.06 |
Unity Particle Draw call 절약 (0) | 2021.04.06 |
Diagnosing Performance Problems - 2019.3 (0) | 2021.03.26 |
Fixing Performance Problems - 2019.3 (0) | 2021.03.22 |
- Total
- Today
- Yesterday