Version: 2017.1
言語: 日本語
グラフィックスコマンドバッファ
スパーステクスチャ

GPU インスタンシング

はじめに

GPU インスタンシングを使うと、少ない ドローコール で、同じメッシュの複数のコピーをいっぺんに描画 (またはレンダリング) できます。 これは、建物、樹木、草などのオブジェクトを描画したり、シーンに繰り返し登場するものを描画する場合に便利です。

GPU インスタンシングは、各ドローコールで同じメッシュをレンダリングするだけですが、各インスタンスに変化を加えるために、パラメーター (例えば、色やスケール) を変えることで、同じパターンの繰り返しの回数を減らすことができます。

GPU インスタンシングを使うと、シーンごとに使用されるドローコールの数を減らすことができます。 これにより、プロジェクトのレンダリングパフォーマンスが大幅に向上します。

マテリアルにインスタンシングを加える

GPU インスタンシングをマテリアル上で有効にするには、Project ウィンドウでマテリアルを選択し、Inspector で Enable Instancing をチェックします。

Material Inspector ウィンドウの Enable Instancing チェックボックス
Material Inspector ウィンドウの Enable Instancing チェックボックス

このチェックボックスは、マテリアルシェーダーが GPU インスタンシングをサポートする場合のみ表示されます。これには、Standard、StandardSpecular、そしてすべてのサーフェス シェーダー が含まれます。詳しくは スタンダードシェーダー を参照してください。

以下のスクリーンショットは、複数のゲームオブジェクトを持つ同じシーンを示しています。最初の画像では GPU インスタンシングが有効になっており、2 番目の画像では有効になっていません。 FPSBatches Saved by batching の違いに注目してください。

GPU インスタンシングが有効: GPU インスタンシングを有効にした複数の同じゲームオブジェクトを持つ単純なシーン
GPU インスタンシングが有効: GPU インスタンシングを有効にした複数の同じゲームオブジェクトを持つ単純なシーン
GPU インスタンシングが無効: GPU インスタンシングを無効にした複数の同じゲームオブジェクトを持つ単純なシーン
GPU インスタンシングが無効: GPU インスタンシングを無効にした複数の同じゲームオブジェクトを持つ単純なシーン

GPU インスタンシングを使用するる場合、以下の制限があります。

  • Unity は インスタンシングのために、自動的に MeshRenderer コンポーネントと Graphics.DrawMesh の呼び出しを選択します。SkinnedMeshRenderer はサポートされないことに注意してください。

  • Unity は、1 回の GPU インスタンシングドローコールで、同じメッシュとマテリアルを共有するゲームオブジェクトだけをバッチ処理します。インスタンシングの効率を上げるために、少数のメッシュとマテリアルを使用するようにします。バリエーションを作成するには、シェーダースクリプトを変更してインスタンスごとのデータを追加します (詳細は次のセクションを参照してください)。

You can also use the calls Graphics.DrawMeshInstanced and Graphics.DrawMeshInstancedIndirect. to perform GPU Instancing from your scripts.

GPU インスタンシングは、以下のプラットフォームおよび API で利用可能です。

  • Windows の DirectX 11DirectX 12

  • Windows、macOS、Linux、iOS、Android の OpenGL Core 4.1+/ES3.0+

  • macOS と iOS の Metal

  • Windows と Android の Vulkan

  • PlayStation 4Xbox One

  • WebGL (requires WebGL 2.0 API)

インスタンスごとのデータを加える

デフォルトでは、インスタンス化された各ドローコールで、異なる Transform を持つゲームオブジェクトのインスタンスだけをバッチします。インスタンス化したゲームオブジェクトをさらに相違させるには、シェーダーを変更してマテリアルカラーなどのインスタンスごとのプロパティーを加えます。

以下の例は、インスタンスごとに異なるカラー値を持つインスタンス化したシェーダーを作成する方法を示しています。


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
        // Physically based Standard lighting model, and enable shadows on 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_CBUFFER_START(Props)
           UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
        UNITY_INSTANCING_CBUFFER_END
        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * UNITY_ACCESS_INSTANCED_PROP(_Color);
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

When you declare _Color as an instanced property, Unity takes all _Color GameObjects that share a Mesh and Material and includes them in a single draw call.


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 Instancing を使用可能にしなくてはなりません。それには、Project ウィンドウでシェーダーを選択し、Inspector で Enable Instancing をチェックします。

シェーダーの Inspector ウインドウに表示された Enable Instancing チェックボックス
シェーダーの Inspector ウインドウに表示された Enable Instancing チェックボックス

頂点シェーダーとフラグメントシェーダーにインスタンシングを加える

以下の例は簡単な Unlit シェーダーを様々な色でインスタンシング可能にします。


Shader "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 // necessary only if you want to access instanced properties in fragment Shader.
            };

            UNITY_INSTANCING_CBUFFER_START(MyProperties)
                UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
            UNITY_INSTANCING_CBUFFER_END
           
            v2f vert(appdata v)
            {
                v2f o;

                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o); // necessary only if you want to access instanced properties in the fragment Shader.

                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }
           
            fixed4 frag(v2f i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i); // necessary only if any instanced properties are going to be accessed in the fragment Shader.
                return UNITY_ACCESS_INSTANCED_PROP(_Color);
            }
            ENDCG
        }
    }
}

シェーダーの変更

追加コード 機能
#pragma multi_compile_instancing Unity にインスタンシングバリアントを生成するように命令するために使用します。サーフェスシェーダーには必要ありません。
UNITY_VERTEX_INPUT_INSTANCE_ID 頂点シェーダー入力/出力構造体で使用してインスタンス ID を定義します。 詳細は SV_InstanceID を参照してください。
UNITY_INSTANCING_CBUFFER_START(name) / UNITY_INSTANCING_CBUFFER_END インスタンスごとのプロパティーはすべて、予約された定数バッファで定義する必要があります。この一組になったマクロを使って、各インスタンスに固有化したいプロパティーをラップします。
UNITY_DEFINE_INSTANCED_PROP(float4, _Color) 型と名前を使ってインスタンスごとのシェーダープロパティーを定義します。この例では、_Color プロパティーが固有のものです。
UNITY_SETUP_INSTANCE_ID(v); インスタンス ID がシェーダー関数にアクセスできるようにします。これは頂点シェーダーの最初に使用する必要があります。また、フラグメントシェーダーでは、オプションとして使用されます。
UNITY_TRANSFER_INSTANCE_ID(v, o); インスタンス ID を頂点シェーダの入力構造体から出力構造体にコピーします。これは、フラグメントシェーダーでは、インスタンスごとのデータにアクセスするときのみ必要です。
UNITY_ACCESS_INSTANCED_PROP(color) Use this to access a per-instance Shader property. It uses an instance ID to index into the instance data array.

注意

  • 複数のインスタンスごとのプロパティーを使用する場合は、MaterialPropertyBlocks でそれらすべてを満たす必要はありません。

  • 1 つのインスタンスにプロパティーがない場合、Unity は参照されているマテリアルからデフォルト値を取ります。マテリアルが指定したプロパティーのデフォルト値を持たない場合、Unity は値を 0 に設定します。MaterialPropertyBlock にインスタンス化していないプロパティーを使用しないでください。なぜなら、インスタンシングを無効にするからです。代わりに、他のマテリアルを作成してください。

上級者向けの GPU インスタンシングのヒント

バッチの優先度

バッチを行う場合、Unity はインスタンシングより 静的バッチ を優先します。ゲームオブジェクトの 1 つを静的バッチに指定し Unity がそのバッチ処理を行うと、たとえレンダラーがインスタンシングのシェーダーを使用していても、Unity はそのゲームオブジェクトのインスタンシングを無効にします。この場合、警告メッセージが Inspector ウィンドウに表示され、静的バッチ処理を無効にするよう指示します。静的バッチ処理を無効にするには、Player Settings (Edit > Project Settings > Player) に移動し、 Other Settings を開き、Rendering セクションの Static Batching チェックボックスの選択を解除します。

Unity は、動的バッチ処理よりもインスタンシングを優先します。Unity がメッシュをインスタンス化できる場合、メッシュの動的バッチ処理を無効にします。

Graphics.DrawMeshInstanced

ゲームオブジェクトを自動的にひとまとめにインスタンス化するのを妨げる要因がいくつかあります。これらの要因には、マテリアル変更と深度ソートなどがあります。Unity に GPU のインスタンシングを使用してオブジェクトを描画するよう強制するには、