前出の例では、パーティクルのデフォルトの頂点ストリーム設定のみを使用しています。これには、位置、法線、色、1 つの UV が含まれます。ただし、カスタム頂点ストリームを使用することによって、速度、回転、サイズなど、他のデータをシェーダーに送信できます。
次の例では、シェーダーは特殊効果を表示するように設計されています。これにより、高速なパーティクルは明るく表示され、低速なパーティクルは暗く表示されます。スピード頂点ストリームを使用して、スピードに応じてパーティクルを明るくする追加のコードが存在します。また、このシェーダーは効果がテクスチャシートアニメーションを使用しないと仮定するため、カスタムストリーム構造体から除外されます。
以下は、完全なシェーダーです。
Shader "Instanced/ParticleMeshesCustomStreams"
{
Properties
{
_MainTex("Albedo", 2D) = "white" {}
}
SubShader
{
Tags{ "RenderType" = "Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma exclude_renderers gles
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma instancing_options procedural:vertInstancingSetup
#define UNITY_PARTICLE_INSTANCE_DATA MyParticleInstanceData
#define UNITY_PARTICLE_INSTANCE_DATA_NO_ANIM_FRAME
struct MyParticleInstanceData
{
float3x4 transform;
uint color;
float speed;
};
#include "UnityCG.cginc"
#include "UnityStandardParticleInstancing.cginc"
struct appdata
{
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert(appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
o.color = v.color;
o.texcoord = v.texcoord;
vertInstancingColor(o.color);
vertInstancingUVs(v.texcoord, o.texcoord);
#if defined(UNITY_PARTICLE_INSTANCING_ENABLED)
UNITY_PARTICLE_INSTANCE_DATA data = unity_ParticleInstanceData[unity_InstanceID];
o.color.rgb += data.speed;
#endif
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
half4 albedo = tex2D(_MainTex, i.texcoord);
return i.color * albedo;
}
ENDCG
}
}
}
シェーダーには、カスタム頂点ストリームが使用されていないときのデフォルトのインスタンシングデータレイアウトを含む UnityStandardParticleInstancing.cginc が配置されています。そのため、カスタムストリームを使用する場合は、対象のヘッダーで定義されているデフォルト設定のいくつかをオーバーライドする必要があります。これらのオーバーライドは include の 前 に配置する必要があります。上の例では、次のカスタムオーバーライドを設定しています。
最初に、UNITY_PARTICLE_INSTANCE_DATA マクロを使用して、カスタムストリームデータのための ‘MyParticleInstanceData’ というカスタム構造体を使用するよう指示しています。
#define UNITY_PARTICLE_INSTANCE_DATA MyParticleInstanceData
次に、もう 1 つの定義で、Anim Frame Stream (アニメーションフレームストリーム) がこのシェーダーで必要でないことをインスタンシングシステムに伝えています。なぜなら、この例の効果はテクスチャシートアニメーションでの使用を意図していないためです。
#define UNITY_PARTICLE_INSTANCE_DATA_NO_ANIM_FRAME
第 3 に、カスタムストリームデータの構造体が宣言されます。
struct MyParticleInstanceData
{
float3x4 transform;
uint color;
float speed;
};
これらのオーバーライドはすべて UnityStandardParticleInstancing.cginc が配置されている箇所より前の位置に配置されているため、シェーダーはこれらの定義にシェーダー自体のデフォルト設定を使用しません。
構造体を記述する場合、変数は Particle System Renderer モジュールの Inspector にリストされている頂点ストリームと一致する必要があります。つまり、Renderer モジュールの UI で使用するストリームを選択し、カスタムストリームデータ構造体の変数定義に同じ順序で追加して、一致させる必要があります。
最初の項目 (Position) は必須であるため、削除することはできません。プラス (+) とマイナス (-) のボタンを使用して他のエントリーを自由に追加/削除して、頂点ストリームデータをカスタマイズできます。
リスト内で INSTANCED と示されるエントリーにはインスタンスデータが含まれているため、パーティクルインスタンスのデータ構造体に加える必要があります。INSTANCED という語のすぐ後に加えられた数字 (例: INSTANCED0 の 0 と INSTANCED1 の 1) は、最初の “transform” 変数の後に、変数を構造体に配置する順序を示します。末尾の文字 (.x .xy .xyz .xyzw) は、変数の型を示し、シェーダーコードの float、float2、float3、float4 変数の型にマップされます。
リストに表示されていて、INSTANCED と 示されて いない 他の頂点ストリームデータは、パーティクルインスタンスのデータ構造体から削除できます。これは、それらがシェーダーによって処理されるインスタンス化されたデータではないためです。このデータは、UV、法線、接線などのソースメッシュに属します。
例を完成させる最後の手順は、頂点シェーダー内のパーティクルの色に速度を適用することです。
#if defined(UNITY_PARTICLE_INSTANCING_ENABLED)
UNITY_PARTICLE_INSTANCE_DATA data = unity_ParticleInstanceData[unity_InstanceID];
o.color.rgb += data.speed;
#endif
すべてのインスタンシングコードを UNITY_PARTICLE_INSTANCING_ENABLED のチェック内にラップする必要があります。そのようにすると、インスタンス化が使用されていないときにもコンパイルできます。
この時点で、フラグメントシェーダーにデータを渡したい場合は、他のシェーダーデータの場合と同様に、v2f 構造体にデータを書き込むことができます。
この例では、カスタムの頂点ストリームで使用するカスタムシェーダーを変更する方法について説明しますが、サーフェスシェーダーにも全く同じアプローチで同じ機能を実現できます。