Version: 2021.3
언어: 한국어
GPU 인스턴싱
드로우 콜 배칭

GPU 인스턴싱을 지원하는 셰이더 생성

이 페이지는 커스텀 Unity 셰이더에 GPU 인스턴싱 지원을 추가하는 방법에 대한 정보를 포함하고 있습니다. 먼저 커스텀 Unity 셰이더가 GPU 인스턴싱을 지원하는 데 필요한 셰이더 키워드, 변수 및 함수에 대해 설명합니다. 그런 다음 표면 셰이더와 버텍스/프래그먼트 셰이더 모두에 인스턴스당 데이터를 추가하는 방법을 다룬 예제를 포함합니다.

렌더 파이프라인 호환성

기능 빌트인 렌더 파이프라인 유니버설 렌더 파이프라인(URP) 고해상도 렌더 파이프라인(HDRP) 커스텀 스크립터블 렌더 파이프라인(SRP)
커스텀 GPU 인스턴스화된 셰이더 지원 지원 안 함 지원 안 함 지원 안 함

셰이더 수정

이 섹션은 GPU 인스턴싱과 관련된 셰이더 추가에 대한 정보를 포함하고 있습니다.

덧셈 설명
#pragma multi_compile_instancing 인스턴싱 배리언트를 생성합니다. 이것은 프래그먼트 및 버텍스 셰이더에 필요합니다. 표면 셰이더에서는 선택 사항입니다.
#pragma instancing_options Unity가 인스턴스에 사용하는 옵션을 지정합니다. 사용 가능한 옵션 스위치에 대한 자세한 내용은 [#pragma instancing_options](#Instancing_options-switches)를 참조하십시오.
UNITY_VERTEX_INPUT_INSTANCE_ID 버텍스 셰이더 입출력 구조에서 인스턴스 ID를 정의합니다. 이 매크로를 사용하려면 INSTNCING_ON 셰이더 키워드를 활성화하십시오. 그렇지 않으면 Unity가 인스턴스 ID를 설정하지 않습니다.
인스턴스 ID에 액세스하려면 #ifdef INSTANCING_ON 블록 내에서 vertexInput.instanceID를 사용합니다. 이 블록을 사용하지 않으면 배리언트가 컴파일되지 않습니다.
UNITY_INSTANCING_BUFFER_START(bufferName) bufferName이라는 인스턴스당 상수 버퍼의 시작을 선언합니다. 이 매크로를 UNITY_INSTNCING_BUFFER_END와 함께 사용하여 각 인스턴스에 고유하게 하려는 프로퍼티 선언을 래핑합니다. UNITY_DEFINE_INSTANCED_PROP를 사용하여 버퍼 내부의 프로퍼티를 선언합니다.
UNITY_INSTANCING_BUFFER_END(bufferName) bufferName이라는 인스턴스당 상수 버퍼의 끝을 선언합니다. 이 매크로를 UNITY_INSTNCING_BUFFER_START와 함께 사용하여 각 인스턴스에 고유하게 하려는 프로퍼티 선언을 래핑합니다. UNITY_DEFINE_INSTANCED_PROP를 사용하여 버퍼 내부의 프로퍼티를 선언합니다.
UNITY_DEFINE_INSTANCED_PROP(type, propertyName) 지정된 타입과 이름을 사용하여 인스턴스당 셰이더 프로퍼티를 정의합니다. 아래 예제에서 _Color 프로퍼티는 고유합니다.
UNITY_SETUP_INSTANCE_ID(v); 셰이더 함수가 인스턴스 ID에 액세스할 수 있도록 합니다. 버텍스 셰이더의 경우 이 매크로를 처음에 사용해야 합니다. 프래그먼트 셰이더의 경우 이 추가는 선택 사항입니다. 예를 들어 버텍스 및 프래그먼트 셰이더를 참조하십시오.
UNITY_TRANSFER_INSTANCE_ID(v, o); 버텍스 셰이더의 입력 구조에서 출력 구조로 인스턴스 ID를 복사합니다. 프래그먼트 셰이더에서 인스턴스당 데이터에 액세스해야 하는 경우 이 매크로를 사용합니다.
UNITY_ACCESS_INSTANCED_PROP(bufferName, propertyName) 인스턴스 상수 버퍼의 인스턴스당 셰이더 프로퍼티에 액세스합니다. Unity는 인스턴스 ID를 사용하여 인스턴스 데이터 배열을 인덱싱합니다. bufferName은 지정된 프로퍼티를 포함하는 상수 버퍼의 이름과 일치해야 합니다. 이 매크로는 INSTNCING_ON 및 인스턴싱되지 않은 배리언트에 대해 다르게 컴파일합니다.

여러 개의 인스턴스당 프로퍼티를 사용하는 경우 MaterialPropertyBlock 오브젝트에서 모든 프로퍼티를 채울 필요는 없습니다. 또한 한 인스턴스에 프로퍼티가 없는 경우 Unity는 참조된 머티리얼에서 기본값을 가져옵니다. 머티리얼에 프로퍼티에 대한 기본값이 없는 경우 Unity는 해당 값을 0으로 설정합니다. 인스턴싱이 비활성화되기 때문에 MaterialPropertyBlock에 인스턴스화가 안된 프로퍼티를 두면 안 됩니다. 대신 다른 머티리얼을 만들어야 합니다.

Instancing_options 스위치

[#pragma instancing_options](#pragma-instancing_options) 지시문에서는 다음의 스위치를 사용할 수 있습니다.

스위치 설명
forcemaxcount:batchSizemaxcount:batchSize 대부분의 플랫폼에서 Unity는 인스턴싱 데이터 배열 크기를 자동으로 계산합니다. 타겟 디바이스의 최대 상수 버퍼 크기를 모든 인스턴스당 프로퍼티를 포함하는 구조의 크기로 나눕니다. 일반적으로 배치 크기에 대해 걱정할 필요가 없습니다. 그러나 일부 플랫폼에는 고정 배열 크기가 필요합니다. maxcount 옵션을 사용하여 해당 플랫폼의 배치 크기를 지정할 수 있습니다. 다른 플랫폼은 이 옵션을 무시합니다. 모든 플랫폼에 배치 크기를 강제로 적용하려면 forcemaxcount를 사용합니다. 이는 예를 들어 프로젝트에서 DrawMeshInstanced를 사용하여 256개의 인스턴스화된 스프라이트가 있는 드로우 콜을 실행할 때 유용합니다. 두 옵션의 기본값은 500입니다.
assumeuniformscaling 모든 인스턴스의 스케일링이 동일하다고 가정하도록 Unity에 지시합니다(모든 X, Y 및 Z축에 대한 동일한 스케일).
nolodfade Unity가 LOD 페이드 값에 GPU 인스턴싱을 적용하지 않도록 합니다.
nolightprobe Unity가 라이트 프로브 값과 해당 오클루전 데이터에 GPU 인스턴싱을 적용하지 못하도록 합니다. 프로젝트에 GPU 인스턴싱과 라이트 프로브를 모두 사용하는 게임 오브젝트가 포함되어 있지 않은 경우 이 옵션을 ON으로 설정하면 퍼포먼스가 향상될 수 있습니다.
nolightmap Unity가 GPU 인스턴싱을 라이트맵 아틀라스 정보 값에 적용하지 못하도록 합니다. 프로젝트에 GPU 인스턴싱과 라이트맵을 모두 사용하는 게임 오브젝트가 포함되어 있지 않은 경우 이 옵션을 ON으로 설정하면 성능이 향상될 수 있습니다.
procedural:FunctionName Graphics.DrawMeshInstancedIndirect와 함께 사용할 추가 배리언트를 생성합니다. 버텍스 셰이더 시작 단계에서 Unity가 콜론 다음에 지정되는 함수를 호출합니다. 인스턴스 데이터를 수동으로 설정하려면 일반적으로 인스턴스당 데이터를 셰이더에 추가하는 방식과 동일한 방식으로 인스턴스당 데이터를 이 함수에 추가합니다. 또한 페치된 인스턴스 프로퍼티 중 프래그먼트 셰이더에 포함된 프로퍼티가 있을 경우 Unity는 프래그먼트 셰이더 시작 단계에서 이 함수를 호출합니다.

GPU 인스턴싱이 있는 셰이더 배리언트 사용

Unity는 #pragma 지시문에서 noinstancing을 지정하지 않는 한 기본적으로 인스턴싱 배리언트를 사용하여 표면 셰이더를 생성합니다. Unity는 표면 셰이더에서 #pragma multi_compile_instancing 사용을 무시합니다.

Unity의 스탠다드 및 스탠다드 스페큘러 셰이더는 기본적으로 인스턴싱을 지원하지만 변환 외에는 인스턴스당 프로퍼티가 없습니다.

씬에 GPU 인스턴싱이 활성화된 게임 오브젝트가 포함되어 있지 않으면 Unity는 인스턴싱 셰이더 배리언트를 제거합니다. 스트립 동작을 오버라이드하려면 다음 단계를 따르십시오.

  1. Project Settings(메뉴: Edit > Project Settings)을 엽니다.
  2. Graphics로 이동합니다.
  3. Shader Stripping 섹션에서 Instancing VariantsKeep All로 설정합니다.

GPU 인스턴싱 셰이더에 이스턴스당 프로퍼티 추가

기본적으로 Unity GPU는 인스턴스화된 각 드로우 콜에서 서로 다른 트랜스폼이 있는 게임 오브젝트를 인스턴스화합니다. 인스턴스에 더 많은 배리에이션을 추가하려면 셰이더를 수정하여 컬러와 같은 인스턴스당 프로퍼티를 추가합니다. 표면 셰이더와 버텍스/프래그먼트 셰이더 모두에서 이 작업을 수행할 수 있습니다.

커스텀 셰이더는 인스턴스당 데이터가 필요하지 않지만 인스턴스 ID가 필요합니다. 월드 매트릭스가 올바르게 작동하려면 인스턴스 ID가 필요하기 때문입니다. 표면 셰이더는 자동으로 인스턴스 ID를 설정하지만 커스텀 버텍스와 프래그먼트 셰이더는 그렇지 않습니다. 커스텀 버텍스와 프래그먼트 셰이더의 ID를 설정하려면 셰이더 시작 부분에 UNITY_SETUP_INSTANCE_ID를 사용합니다. 이를 수행하는 방법의 예는 버텍스 및 프래그먼트 셰이더를 참조하십시오.

인스턴스 프로퍼티를 선언하면 Unity는 게임 오브젝트에 설정된 MaterialPropertyBlock 오브젝트의 모든 프로퍼티 값을 하나의 드로우 콜로 수집합니다. MaterialPropertyBlock 오브젝트를 사용하여 런타임 시 인스턴스당 데이터를 설정하는 방법의 예는 런타임 시 인스턴스당 데이터 변경을 참조하십시오.

멀티 패스 셰이더에 인스턴스당 데이터를 추가할 때 다음 사항에 유의하십시오.

  • 멀티 패스 셰이더에 두 개 이상의 패스가 있는 경우 Unity는 첫 번째 패스만 인스턴스화합니다. 이는 Unity가 이후 패스를 각 오브젝트와 함께 렌더링하여 머터리얼을 강제로 변경하기 때문입니다.
  • 빌트인 렌더 파이프라인에서 포워드 렌더링 경로를 사용하는 경우 Unity는 다수의 광원에 영향을 받는 오브젝트를 효율적으로 인스턴스화할 수 없습니다. Unity는 기본 패스에 대해서만 인스턴싱을 효과적으로 사용할 수 있으며 추가 패스는 그렇지 않습니다. 조명 패스에 대한 자세한 내용은 포워드 렌더링 및 패스 태그 문서를 참조하십시오.

표면 셰이더 예제

다음 예에서는 각 인스턴스에 다른 컬러 값을 가지는 인스턴스화된 표면 셰이더를 생성하는 방법을 보여줍니다.

Shader "Custom/InstancedColorSurfaceShader" 
{
    Properties 
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }

    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
        CGPROGRAM
        // Uses the physically based standard lighting model with shadows enabled for all light types.
        #pragma surface surf Standard fullforwardshadows
        // Use Shader model 3.0 target
        #pragma target 3.0
        sampler2D _MainTex;
        struct Input 
        {
            float2 uv_MainTex;
        };
        half _Glossiness;
        half _Metallic;
        UNITY_INSTANCING_BUFFER_START(Props)
           UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
        UNITY_INSTANCING_BUFFER_END(Props)
        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

버텍스 및 프래그먼트 셰이더 예제

다음 예에서는 각 인스턴스에 다른 컬러 값을 가지는 인스턴스화된 버텍스 및 프래그먼트 셰이더를 생성하는 방법을 보여줍니다. 표면 셰이더와 달리 버텍스 및 프래그먼트 셰이더를 생성할 때 UNITY_SETUP_INSTANCE_ID를 사용하여 인스턴스 ID를 수동으로 설정해야 합니다.

Shader "Custom/SimplestInstancedShader"
{
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
    }

    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_instancing
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID // use this to access instanced properties in the fragment shader.
            };

            UNITY_INSTANCING_BUFFER_START(Props)
                UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
            UNITY_INSTANCING_BUFFER_END(Props)

            v2f vert(appdata v)
            {
                v2f o;

                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o);
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i);
                return UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
            }
            ENDCG
        }
    }
}

런타임 시 인스턴스당 데이터 변경 예제

다음 예에서는 MaterialPropertyBlock 오브젝트를 사용하여 런타임 시 게임 오브젝트 그룹에 대한 인스턴스당 데이터를 설정하는 방법을 보여줍니다. 위의 셰이더 예제에서 _Color 프로퍼티를 랜덤 컬러로 설정합니다.

중요: MaterialPropertyBlocks는 SRP 배처 호환성을 차단합니다. 자세한 내용은 GPU 인스턴싱: 요구 사항 및 호환성을 참조하십시오.

using UnityEngine;

public class MaterialPropertyBlockExample : MonoBehaviour
{
    public GameObject[] objects;

    void Start()
    {
        MaterialPropertyBlock props = new MaterialPropertyBlock();
        MeshRenderer renderer;

        foreach (GameObject obj in objects)
        {
            float r = Random.Range(0.0f, 1.0f);
            float g = Random.Range(0.0f, 1.0f);
            float b = Random.Range(0.0f, 1.0f);
            props.SetColor("_Color", new Color(r, g, b));

            renderer = obj.GetComponent<MeshRenderer>();
            renderer.SetPropertyBlock(props);
        }
    }
}
GPU 인스턴싱
드로우 콜 배칭