오늘의 학습 키워드

Static Batching, Dynamic Batching, Gpu Instancing

 

공부한 내용

Static Batching

Static Batching은 드로우 콜을 줄이기 위한 배칭의 한 기법으로 움직이지 않는 여러 오브젝트가 같은 Material을 공유할 때 메시를 통째로 메모리에 같이 올려 한 번에 그려주는 기능이다.

특징

- 정적인 오브젝트 대상 (인스펙터에서 static 체크)

- 여러 메시를 통째로 미리 메모리에 올리기 때문에 추가적인 메모리 필요

- 런타임 전에 상태 변경 명령을 수행

- 드로우콜 감소 -> CPU 병목 완화

*드로우 콜 : CPU가 GPU에게 상태 변경 명령부터 렌더링까지 명령하는 것

Static Batching 전
Static Batching 후

 

Dynamic Batching

Static Batching은 드로우 콜을 줄이기 위한 배칭의 한 기법으로 움직일 수 있는 동일한 메쉬를 가진 여러 오브젝트가 같은 Material을 공유할 때 런타임 중에 버텍스를 모아 한 번에 그려주는 기능이다.

특징

- Skinned Mesh에는 적용 불가

- 버텍스 수가 일정 수치보다 높으면 적용 불가

- 런타임 중에 버텍스 정보를 읽어오므로 오버헤드 증가

- 드로우 콜 감소 -> CPU 병목 완화

Dynamic Batching 전
Dynamic Batching 설정
Dynamic Batching 후

 

GPU Instancing

GPU Instancing은 동일한 메시의 복사본들을 만들어 별도의 메시를 생성하지 않고 GPU에서 원본 메시를 가져다가 여러 오브젝트를 한 번에 처리해서 렌더링한다.

특징

- GPU에서 인스턴싱 처리 -> 오버헤드나 메모리 이슈에서 자유로움 (버텍스 수로부터 자유로움)

- 동일한 모양의 오브젝트들이 많이 렌더링 되어야 할 때 유용한 기법

GPU Instancing 전
Inspector 설정
GPU Intancing 후

 

Mesh.CombineMeshes

Mesh.CombineMeshes는 동일한  Material을 공유하는 메시끼리 스크립트를 통해서 합쳐주는 메서드이다.

- 런타임 동안 파츠가 조합되어 오브젝트가 만들어져야 하는 경우라면 고려할만 하다.

Mesh Combine 전
Mesh Combine 후

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 모델링 과정에서 하나의 오브젝트로 만들어져 생성하는 것이 좋겠지만
// 같은 Material을 공유하고 파츠별로 바꿔서 조립하며 버텍스 수가 많아 다이나믹 배칭이 어려운 상황에서는
// CombineMesh도 고려할만 하다.(배치와 SetPass 감소)

public class CombineMesh : MonoBehaviour
{
    void Start()
    {
        CombineMeshs();
    }

    private void CombineMeshs()
    {
        // 자식들의 메쉬 필터와 렌더러를 가져옴
        MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
        MeshRenderer[] meshRenderers = GetComponentsInChildren<MeshRenderer>();

        // 컴바인인스턴스 배열을 메쉬 필터의 길이만큼 생성
        CombineInstance[] combineInstances = new CombineInstance[meshFilters.Length];
        if (CanMeshCombine(meshRenderers))
        {
            // 메쉬 필터, 렌더러 정보를 컴바인인스턴스에 저장하고 게임 오브젝트 다 끄기(자식)
            for (int i = 0; i < combineInstances.Length; i++)
            {
                combineInstances[i].mesh = meshFilters[i].sharedMesh;
                combineInstances[i].transform = meshFilters[i].transform.localToWorldMatrix; // 트랜스폼 로컬 좌표를 월드 좌표로 변환
                meshFilters[i].gameObject.SetActive(false);
            }

            // 새 메쉬 필터와 렌더러 컴포넌트 추가
            MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>();
            MeshRenderer meshRenderer = gameObject.AddComponent<MeshRenderer>();

            // 메쉬 렌더러 Material 초기화
            meshRenderer.sharedMaterial = meshRenderers[0].sharedMaterial;

            // 새 메쉬 생성 후 메쉬 필터에 넣어놓고 메쉬를 합침
            Mesh newMesh = new Mesh();
            meshFilter.mesh = newMesh;

            //메쉬 합치기
            newMesh.CombineMeshes(combineInstances);

            // 이후 오브젝트 켜주기(부모)
            transform.gameObject.SetActive(true);
        }
    }

    // Material이 같다면
    private bool CanMeshCombine(MeshRenderer[] renderers)
    {
        Material material = renderers[0].sharedMaterial;
        for (int i = 1; i < renderers.Length; i++)
        {
            if (material != renderers[i].sharedMaterial)
            {
                return false;
            }
        }

        return true;
    }
}

 

 

오늘의 회고

  오늘은 팀 프로젝트 제출과 발표가 있었다. 나는 오늘 오전에 버그를 잡고 시연 영상을 찍는 작업을 했는데 시연 영상을 찍을 때마다 새로운 버그가 나와서 바로바로 고쳐서 겨우 제출할 수 있었다. 오후에는 개인 공부 시간을 가졌는데 최적화 관련해서 배칭과 드로우콜 부분을 공부하게 되었다. CPU 병목일 때 드로우콜을 낮추기 위해 static batching, Dynamic Batching, GPU Instancing을 고려해봐야겠다는 생각이 들었다.

 내일도 최적화 부분을 좀 더 공부하지 않을까 싶다. 내일도 파이팅!

+ Recent posts