GPU インスタンシングを使うと、少ない ドローコール で、同じメッシュの複数のコピーをいっぺんに描画 (またはレンダリング) できます。 これは、建物、樹木、草などのオブジェクトを描画したり、シーンに繰り返し登場するものを描画する場合に便利です。
GPU インスタンシングは、各ドローコールで同じメッシュをレンダリングするだけですが、各インスタンスに変化を加えるために、パラメーター (例えば、色やスケール) を変えることで、同じパターンの繰り返しの回数を減らすことができます。
GPU インスタンシングを使うと、シーンごとに使用されるドローコールの数を減らすことができます。 これにより、プロジェクトのレンダリングパフォーマンスが大幅に向上します。
GPU インスタンシングをマテリアル上で有効にするには、Project ウィンドウでマテリアルを選択し、Inspector で Enable Instancing をチェックします。
このチェックボックスは、マテリアルシェーダーが GPU インスタンシングをサポートする場合のみ表示されます。これには、Standard、StandardSpecular、そしてすべてのサーフェス シェーダー が含まれます。詳しくは スタンダードシェーダー を参照してください。
以下のスクリーンショットは、複数のゲームオブジェクトを持つ同じシーンを示しています。最初の画像では GPU インスタンシングが有効になっており、2 番目の画像では有効になっていません。 FPS、Batches 、Saved by batching の違いに注目してください。
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 11 と DirectX 12
Windows、macOS、Linux、iOS、Android の OpenGL Core 4.1+/ES3.0+
macOS と iOS の Metal
Windows と Android の Vulkan
PlayStation 4 と Xbox 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 をチェックします。
以下の例は簡単な 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_EN D |
インスタンスごとのプロパティーはすべて、予約された定数バッファで定義する必要があります。この一組になったマクロを使って、各インスタンスに固有化したいプロパティーをラップします。 |
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
にインスタンス化していないプロパティーを使用しないでください。なぜなら、インスタンシングを無効にするからです。代わりに、他のマテリアルを作成してください。
バッチを行う場合、Unity はインスタンシングより 静的バッチ を優先します。ゲームオブジェクトの 1 つを静的バッチに指定し Unity がそのバッチ処理を行うと、たとえレンダラーがインスタンシングのシェーダーを使用していても、Unity はそのゲームオブジェクトのインスタンシングを無効にします。この場合、警告メッセージが Inspector ウィンドウに表示され、静的バッチ処理を無効にするよう指示します。静的バッチ処理を無効にするには、Player Settings (Edit > Project Settings > Player) に移動し、 Other Settings を開き、Rendering セクションの Static Batching チェックボックスの選択を解除します。
Unity は、動的バッチ処理よりもインスタンシングを優先します。Unity がメッシュをインスタンス化できる場合、メッシュの動的バッチ処理を無効にします。
ゲームオブジェクトを自動的にひとまとめにインスタンス化するのを妨げる要因がいくつかあります。これらの要因には、マテリアル変更と深度ソートなどがあります。Unity に GPU のインスタンシングを使用してオブジェクトを描画するよう強制するには、Graphics.DrawMeshInstanced を使用します。Graphics.DrawMesh と同様に、この関数は不要なゲームオブジェクトを作成せずに 1 フレーム分のメッシュを描画します。
Do not submit batches of instances that exceed the maxcount
specified in your Shader script (500 by default). When using graphics tools from OpenGL or Metal, Unity divides the maxcount
by 4, and uses the result as the maximum number of batches you can submit. The recommended practice for drawing arbitrary number of instances is to maintain a pool of pre-allocated 500-sized arrays (and MaterialPropertyBlocks
if needed) and reuse these arrays as much as possible. See documentation on Automatic Memory Management for more information about object pooling.
インスタンスの数を含む、インスタンシングドローコールのパラメーターを計算バッファから読み込むには、スクリプトで DrawMeshInstancedIndirect
を使用します。これは、GPU からすべてのインスタンスデータを取得する必要があり、CPU が描画するインスタンスの数を知らない場合 (たとえば、GPU カリングを実行するとき) に便利です。詳細な説明とコード例については、スクリプトリファレンスの Graphics.DrawMeshInstancedIndirect を参照してください。
#pragma instancing_options
ディレクティブは以下の switch を使用できます。
Switch | 説明 |
---|---|
maxcount: batchSize |
Use this to specify the maximum number of instances to draw in one instanced draw call. By default this value is 500. When using OpenGL or Metal, Unity divides the maxcount by 4, and uses the result as the maximum number of batches you can submit. Make this value as small as possible to match the amount of instances you want to draw. For example, to draw a maximum of 1000 instances, add maxcount: 1000 to your shader script. Larger values increase Shader compilation time, and can reduce GPU performance. |
force_same_maxcount_for_gl |
Use this to force Unity to stop dividing the maxcount by 4 on graphics tools from OpenGL or Metal. |
assumeuniformscaling |
すべてのインスタンスが統一されたスケーリング (X, Y, Z 軸すべてに対して) を持つと仮定するように Unity に命令します。 |
lodfade |
LOD のフェード値をインスタンス化可能にします。これは、LOD フェード機能を使う SpeedTree や他の LOD 技術に有用です。 |
procedural:FunctionName |
Graphics.DrawMeshInstancedIndirect で使用する追加のバリアントを生成するよう、Unity に命令します。 頂点シェーダーステージの最初に、Unity はコロンの後に指定された関数を呼び出します。インスタンスデータを手動で設定するには、インスタンスごとのデータを普通にシェーダーに追加するのと同じ方法で、この関数にインスタンスごとのデータを追加します。フェッチされたインスタンスプロパティーのいずれかがフラグメントシェーダーに含まれている場合は、Unity はフラグメントシェーダーの最初にも、この関数を呼び出します。 |
シェーダースクリプトを作成する場合、mul(UNITY_MATRIX_MVP,v.vertex)
ではなく、常に UnityObjectToClipPos(v.vertex)
を使用します。
インスタンス化されたシェーダーでは、通常どおり UNITY_MATRIX_MVP
を使用できますが、UnityObjectToClipPos
は頂点の位置をオブジェクト空間からクリップスペースに変換する最も効率的な方法です。Unity はまた、プロジェクト内のすべてのシェーダーをスキャンし、 mul(UNITY_MATRIX_MVP, v)
が発生すると自動的に UnityObjectToClipPos(v)
に置き換えるシェーダーのアップグレード機能を実装しています。
UNITY_MATRIX_MVP
(UNITY_MATRIX_MV
と共に) がまだ使用されている場合は、コンソールウィンドウ (Window > Console) にパフォーマンスの警告が表示されます。
#pragma
サーフェスディレクティブで noinstancing
を指定しない限り、サーフェスシェーダーにはデフォルトでインスタンシングバリアントが生成されます。Standard および StandardSpecular シェーダーはインスタンスシングが適用されるように既に変更されていますが、transform 以外は、インスタンスごとのプロパティーは定義されていません。Unity は、サーフェスシェーダーで#pragma multi_compile_instancing
が使用されても無視します。
シーン内のゲームオブジェクトで GPU インスタンシングが有効になっていない場合、Unity はインスタンシングバリアントを削除します。 ストリッピングの挙動を無効にするには、 Graphics Settings (Edit > Project Settings > Graphics) を開き、 Shader stripping セクションに移動し、Instancing Variants を変更します。
Graphics.DrawMeshInstanced
を使用するには、このメソッドに渡すマテリアルの GPU インスタンシングを有効にする必要があります。ただし、Graphics.DrawMeshInstancedIndirect
では GPU インスタンシングをを有効にする必要はありません。間接的なインスタンシングのキーワード PROCEDURAL_INSTANCING_ON
はストリッピングに影響されません。
インスタンス化されたドローコールは フレームデバッガー で Draw Mesh (instanced) として表示されます。
インスタンスごとのプロパティーを必ずしも定義する必要はありません。ただし、インスタンス ID の設定は必須です。なぜなら、ワールド行列が正しく機能するために必要だからです。サーフェスシェーダーは自動的にインスタンス ID を設定します。カスタム頂点シェーダーとフラグメントシェーダのインスタンス ID は手動で設定する必要があります。これを行うには、シェーダーのはじめにUNITY_SETUP_INSTANCE_ID
を使用します。
フォワードレンダリングを使用する場合、Unity は複数のライトの影響を受けるオブジェクトを効率的にインスタンス化できません。加算パスではなく、ベースパスのみがインスタンス化を有効に活用できます。ライティングパスの詳細については、フォワードレンダリング と Pass 内の Tags を参照してください。
ライトマップを使用するオブジェクトや、異なるライトや リフレクションプローブ の影響を受けるオブジェクトはインスタンス化できません。
マルチパスシェーダーのパスが 2 つ以上の場合は、最初のパスだけがインスタンス化されます。これは、Unity が強制的に後からのパスをオブジェクトごとに一緒にレンダリングして、マテリアルの変更を強制するためです。
上記の例で使用されているシェーダーのマクロはすべて、 UnityInstancing.cginc で定義されています。 このファイルは、[Unity installation folder]\Editor\Data\CGIncludes ディレクトリにあります。
2017–05–18 編集レビュー を行って修正されたページ
Enable Instancing チェックボックスの説明、DrawMeshInstancedIndirect、#pragma multi-compile に関しては 5.6 で追加