Unity GPU Instancing과 Draw Call 최적화
GPU Instancing이란
섹션 제목: “GPU Instancing이란”동일한 메시와 머티리얼을 가진 오브젝트를 하나의 Draw Call로 렌더링하는 기법입니다. CPU→GPU 통신 횟수를 줄여 CPU 병목을 해소합니다.
셰이더 설정
섹션 제목: “셰이더 설정”// Surface Shader에서 GPU Instancing 활성화Shader "Custom/InstancedShader" { Properties { _Color("Color", Color) = (1,1,1,1) } SubShader { CGPROGRAM #pragma surface surf Standard #pragma multi_compile_instancing // 필수
UNITY_INSTANCING_BUFFER_START(Props) UNITY_DEFINE_INSTANCED_PROP(float4, _Color) UNITY_INSTANCING_BUFFER_END(Props)
struct Input { float2 uv_MainTex; };
void surf(Input IN, inout SurfaceOutputStandard o) { o.Albedo = UNITY_ACCESS_INSTANCED_PROP(Props, _Color).rgb; } ENDCG }}머티리얼 인스펙터에서 Enable GPU Instancing 체크박스를 활성화합니다.
MaterialPropertyBlock으로 인스턴스별 색상
섹션 제목: “MaterialPropertyBlock으로 인스턴스별 색상”Material을 직접 수정하면 새 머티리얼이 생성되어 인스턴싱이 깨집니다. MaterialPropertyBlock을 사용하세요.
MaterialPropertyBlock _mpb;MeshRenderer _renderer;
void Start() { _mpb = new MaterialPropertyBlock(); _renderer = GetComponent<MeshRenderer>();}
public void SetColor(Color color) { _mpb.SetColor("_Color", color); _renderer.SetPropertyBlock(_mpb); // 머티리얼 공유 유지}Graphics.DrawMeshInstanced
섹션 제목: “Graphics.DrawMeshInstanced”GameObject 없이 메시를 대량으로 렌더링합니다.
public class InstancedRenderer : MonoBehaviour { [SerializeField] Mesh mesh; [SerializeField] Material material; [SerializeField] int count = 1000;
Matrix4x4[] _matrices; MaterialPropertyBlock _mpb; static readonly int ColorID = Shader.PropertyToID("_Color");
void Start() { _matrices = new Matrix4x4[count]; var colors = new Vector4[count]; _mpb = new MaterialPropertyBlock();
var rand = new System.Random(42); for (int i = 0; i < count; i++) { _matrices[i] = Matrix4x4.TRS( new Vector3(rand.Next(-50, 50), 0, rand.Next(-50, 50)), Quaternion.Euler(0, rand.Next(360), 0), Vector3.one); colors[i] = new Vector4( (float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble(), 1f); } _mpb.SetVectorArray(ColorID, colors); }
void Update() { // DrawMeshInstanced는 최대 1023개 for (int i = 0; i < count; i += 1023) { int batch = Mathf.Min(1023, count - i); var slice = new Matrix4x4[batch]; System.Array.Copy(_matrices, i, slice, 0, batch); Graphics.DrawMeshInstanced(mesh, 0, material, slice, batch, _mpb); } }}Graphics.DrawMeshInstancedIndirect (무제한)
섹션 제목: “Graphics.DrawMeshInstancedIndirect (무제한)”GPU 버퍼를 사용해 1023개 제한 없이 렌더링합니다.
ComputeBuffer _argsBuffer;ComputeBuffer _dataBuffer;
void InitBuffers() { uint[] args = { mesh.GetIndexCount(0), (uint)count, 0, 0, 0 }; _argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments); _argsBuffer.SetData(args);
_dataBuffer = new ComputeBuffer(count, sizeof(float) * 16); _dataBuffer.SetData(_matrices); material.SetBuffer("_DataBuffer", _dataBuffer);}
void Update() { Graphics.DrawMeshInstancedIndirect(mesh, 0, material, new Bounds(Vector3.zero, Vector3.one * 1000), _argsBuffer);}
void OnDestroy() { _argsBuffer?.Release(); _dataBuffer?.Release();}SRP Batcher와의 관계
섹션 제목: “SRP Batcher와의 관계”URP/HDRP에서는 SRP Batcher가 먼저 동작합니다.
| 조건 | 배칭 방식 |
|---|---|
| 동일 셰이더 변형 | SRP Batcher (다른 머티리얼도 OK) |
| 동일 메시 + 동일 머티리얼 | GPU Instancing |
| 정적 오브젝트 | Static Batching |
GPU Instancing은 동적 오브젝트(나무, 풀, 파티클 등)에 특히 효과적입니다.
- 셰이더에
#pragma multi_compile_instancing+ 머티리얼 설정 필수 - 인스턴스별 프로퍼티는
MaterialPropertyBlock으로 전달 (머티리얼 직접 수정 금지) DrawMeshInstanced는 최대 1023개,DrawMeshInstancedIndirect는 무제한- SRP Batcher → GPU Instancing → Static Batching 순으로 우선 적용