UNITY DrawCall調査 particles GPU Instancing  ~ UNITY2018.3.6f1 ~

前回に続き、DrawCall削減で、ParticleのGPU Instancing関連を調査

#ParticleSystem
##Billboard
通常Particleを表示させると下記のようになる

![SnapCrab_NoName_2019-3-5_2-45-18_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/13916/f6ee93ab-6fc3-a5c3-f5b7-64ef2d9ced49.png)

![SnapCrab_NoName_2019-3-5_2-45-34_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/13916/554e0ec2-15cc-0ff9-8e96-682bf20396e6.png)

DynamicBatchingが効いてちゃんと効率化されてます

ParticleSystemのRenderingを見てみましょう。
RenderModeがBillboardですね。BillboardではGPU Instancingは使えないが
DynamicBatchingでいいようにしてくれます

##mesh
GPU Instancingを使うにはmeshにします
meshにすると、メッシュをパーティクルとして飛ばせます
デフォルトで Default-ParticleSystemというメッシュが付いています
そしてそのシェーダーはGPU Instancing対応しています

![SnapCrab_NoName_2019-3-5_2-53-27_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/13916/3c7189bf-b944-642b-fbbb-a8d2e71e67ed.png)


GPU Instancing無し 

![SnapCrab_NoName_2019-3-5_2-56-36_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/13916/e182e276-726e-a49a-d676-d9f243ff8a2f.png)

DynamicBatchingは効いてるが、GPU Instancingはありません


GPU Instancingあり

![SnapCrab_NoName_2019-3-5_2-54-46_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/13916/ad938adc-ba0a-0df3-71ed-6a4aa35b2b92.png)

GPU Instancing効いてます

#自作のマテリアルをつける

```cpp
Shader "Instancing2"
{
		SubShader
	{
		Tags { "RenderType" = "Opaque" "DisableBatching" = "True"}

		Pass
		{
			Tags { "LightMode" = "ForwardBase" } 

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#pragma multi_compile_instancing
			#pragma instancing_options procedural:vertInstancingSetup

			#include "UnityCG.cginc"
			#include "UnityStandardParticleInstancing.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float4 color : COLOR;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct v2f
			{
				float4 color : TEXCOORD1;
				float4 vertex : SV_POSITION;
			};

			v2f vert(appdata v)
			{
				UNITY_SETUP_INSTANCE_ID(v);

				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.color = i.color;

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				return i.color;
			}

			ENDCG
		}
        }
}
```

前回とほぼ同じだが、

```cpp
#pragma instancing_options procedural:vertInstancingSetup
```

がないとちゃんと動かなかった。基本的にGPU Instancingを行う時は上記オプションをつけたほうがよさそうだ
また、Colorプロパティーに関するものは今回無いので削除した

![SnapCrab_NoName_2019-3-5_3-10-30_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/13916/e519849f-f732-443e-70aa-f3e4d6cf67c0.png)

ちゃんと効く

#グラデーションをする

ParticleのパラメータでStartColorをグラデーションにしてみる
色が変わる＝マテリアルが変わるので、普通に行うと色の数だけマテリアルが生成されると思うが

##Default-ParticleSystem

![SnapCrab_NoName_2019-3-5_3-15-15_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/13916/3cfbca79-4be2-fa49-3348-6df996be3b86.png)

グラデーションでパーティクルが表示されたが
DrawCallは GPU Instancingの有無にかかわらず単色の時と変わらない
何故だろうか？

##自作メッシュ

メッシュを自作に変更する

GPU Instancing無しだとちゃんと表示されるが
有りにすると全部白いパーティクルになった

![SnapCrab_NoName_2019-3-5_3-21-18_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/13916/99c0b4d5-74b7-cb54-b366-5cc62aa22db3.png)


何かがおかしい

答えは、Instancingの仕組みにある
頂点属性や、プロパティーは INSTANCE_IDにより配列から値を取り出す
Positionは実は UnityObjectToClipPos の内部で計算されている
が、Colorはそのままなのでダメ
Colorの変換には vertInstancingColor という関数が用意されている

頂点シェーダーの Color代入部分を下記に変更

```cpp
				o.color = v.color;
				vertInstancingColor(o.color);
```

![SnapCrab_NoName_2019-3-5_3-46-51_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/13916/f06d3fa8-cd51-7063-32e4-e46b5bb4d63e.png)

これで、パーティクルも GPU Instancingが正常に行われた


![SnapCrab_NoName_2019-3-5_3-51-59_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/13916/5b1440ef-1276-1fb4-7ceb-6514f0fb9e2d.png)

もちろん、前回の方法で、ShadowCasterもGPU Instancing化された


#今回できなかったやつ
本当はこのあたりもちゃんと調査したかったけど
案件が炎上したので、火消しに戻るため参照で

##パーティクルのカスタムデータ
頂点属性をカスタマイズすることが出来ます

https://docs.unity3d.com/jp/current/Manual/PartSysCustomDataModule.html

##コンピュートシェーダでインスタンシングデータ作成
コンピュートシェーダでデータ作る

https://qiita.com/mao_/items/920bec69f44d5f5a9382

##パーティクルのインスタンス事にプロパティ変更
マテリアル変数を UNITY_INSTANCING_BUFFER_START でブロック化し
インスタンス事に変数を与える方法は以前行ったが
これもパーティクルで行えるはずである