【Unity】Visual Effect Graph(VFX Graph)で100万個以上のドカベンロゴを回すGPUパーティクルを実装してみた

# ﾃｰﾃｰﾃｰﾃﾃﾃﾃｯﾃﾃｰ

ｶｧｧｧﾝ!!!
ﾜｧｧｧｧ-----!!

![doka.gif](https://qiita-image-store.s3.amazonaws.com/0/80207/e68cab7d-5181-f166-7a10-a6e3b3be5c0d.gif)

ﾃｰﾃｰﾃｰﾃﾃﾃﾃｯﾃﾃｰ





# ▽ 概要

先日、[Visual Effect Graph(VFX Graph)](https://unity.com/visual-effect-graph)という新機能のPreviewが公開されました。

機能について簡単に説明すると、GPUベースで動くパーティクルなどをノードベースで組めると言った機能のようです。
(動作環境にもよりますが)GPUベースと言うこともあってか大量のオブジェクトを出しても結構動いてくれるように見受けられます。

早速ではありますが、Hello Worldとして100万個以上のドカベンロゴを同時に回すパーティクルを実装してみたので導入手順含めて備忘録序にメモして行ければと思います。

なお、こちらに記載している内容については **Previewを前提としたの物且つ完全に理解していない段階での手探りの実装となっている箇所もあるので、最適解ではない & 将来的には内容が変わってくると言った可能性があります。こちらの点についてもご了承下さい。**
※間違いがあったら保守できる範囲で随時修正していく予定。

プロジェクト一式はGitHubにアップしてあります。
[mao-test-h/DokabenVFX-Graph](https://github.com/mao-test-h/DokabenVFX-Graph)

--------------------------------------------------------

※100万個以上も同時に回すとただの赤い何かでしか無く、詳細な動きが確認し辛いかと思われるので...一応数を減らしたものも載せておきます。
![doka_.gif](https://qiita-image-store.s3.amazonaws.com/0/80207/da62ad43-8b81-874e-ef06-550c962032aa.gif)

ちなみにSceneViewにて遠くから眺めると↓の様な集合体になっていることが確認できます。
![doka.png](https://qiita-image-store.s3.amazonaws.com/0/80207/349934a2-7465-c0fe-0bf7-351c4c853aed.png)





# ▽ 導入について

VFX GraphはまだPackageManager経由では取得できないので、一から導入するとした場合にはリポジトリから取得/配置してHDRP周りのセットアップなどを行う必要があります。  
詳細については以下の記事が参考になります。

[【Unity】Visual Effects Graph(VFX Graph)をとりあえず動かしてみる](http://notargs.hateblo.jp/entry/vfx_graph)


ちなみに今回の記事に合わせて公開しているプロジェクトの方では「[peeweek/VisualEffectGraph-EasySetup](https://github.com/peeweek/VisualEffectGraph-EasySetup)」と言う最初から必要なセットアップ諸々が済まされているリポジトリの内容を参考に必要な情報は予め済ませておいた上でバージョン管理しているので、動作させる際にはVFX Graph関連のpackageをcloneしてローカルに配置するSetup.bat(.sh)を実行するだけで動かすことが出来るかと思われます。

※ローカルに配置したpackageについては[Packages/manifext.json](https://github.com/mao-test-h/DokabenVFX-Graph/blob/master/Packages/manifest.json)の方で読み込むようになってます。

```json
{
  "dependencies": {
    "com.unity.package-manager-ui": "2.0.2",
    "com.unity.render-pipelines.core": "file:../GitHub/com.unity.render-pipelines.core",
    "com.unity.render-pipelines.high-definition": "file:../GitHub/com.unity.render-pipelines.high-definition",
    "com.unity.shadergraph": "file:../GitHub/com.unity.shadergraph",
    "com.unity.visualeffectgraph": "file:../GitHub/com.unity.visualeffectgraph",
    ...
```

## ▼ 動作要件

[ドキュメント](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/Visual-Effect-Graph)の方から引用すると動作させる要件としては以下が必要になるかと思われます。

- Unity 2018.3 or newer
- Compute Shader support for your build target.
- Windows or Mac editor (Linux is still experimental at the moment)
- HD Render PIpeline configured project

※↑には「Compute Shader support for your build target」とありますが、「[Feedback Wanted: Visual Effect Graph](https://forum.unity.com/threads/feedback-wanted-visual-effect-graph.572110/)」の方を見るとAndroid Vulkanはまだ対応していない様に思われます。

※VFXのAssetを見た感じだと内部的にShaderLabとComputeShaderが自動生成されている模様。(中身まではまだ追いきれておらず)





# ▽ 実装について

ノード全体としては以下の様になってます。

![example.png](https://qiita-image-store.s3.amazonaws.com/0/80207/9ff5aa4d-102e-c754-61c9-0ec5e240d2bf.png)

手始めということもあるので処理の実行順や各用語等についてざっくりと説明すると、 **コンテキストと呼ばれる処理の状態を示すノード(画像中で言うと「Spawn」「Initialize」「Update」「Quad Output」が該当)** を連結させ、コンテキスト内に **ブロックという処理の内容(Initializeに設定されている物で言うと「Set Lifetime Random」「Set Size」「Set Pivot」「Add Velocity」が該当)** を設定することで全体を制御する流れになるかと思われます。

詳細については以下のドキュメントをご覧ください。

- [The Visual Effect Graph Window](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/The-Visual-Effect-Graph-Window)
- [VFX Systems Contexts and Blocks](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/VFX-Systems-Contexts-and-Blocks)
- [VFX Contexts](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/VFX-Contexts)

ちなみに、今回実装する上では全体的に「[Visual Effect Graph - Easy Setup (For GitHub version ONLY)](https://github.com/peeweek/VisualEffectGraph-EasySetup)」の方を参考にさせて頂きました。
幾つかのサンプルとなるVFXも含まれているのでおすすめです。

それでは各コンテキスト毎に上から順に説明していきます。

### ※Blackboardについて

こちらについて軽く補足しておくと、値を定義しておく事が出来るパネルです。
中にある「Exposed」にチェックを付けておくとInspectorにも公開されるみたいです。

公開することによるパフォーマンスへの影響と言った副作用についてはまだ見えておりませんが、調整可能な値については公開しておいた方が見通しが良いかもしれません。

- [Blackboard Panel](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/The-Visual-Effect-Graph-Window#blackboard-panel)

![Blackgoard.png](https://qiita-image-store.s3.amazonaws.com/0/80207/31556054-2b2a-fdbf-d4e5-dea65c600d28.png)


## ▼ [Spawn](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/VFX-Contexts#spawn)

生成周りの制御です。
組み込みブロックである「[Constant Rate](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/VFX-Blocks-Spawn#constant-rate)」を設定してます。

※補足として「[Spawn](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/VFX-Contexts#spawn)]のリンクの方を見た感じだとイベント制御もできるみたいです。こちらを上手く使えば「〇〇した時にパーティクルを発生させる」と言った制御ができるかと思われます。(要検証)

![spawn.png](https://qiita-image-store.s3.amazonaws.com/0/80207/bb57ec73-567c-346b-98b7-905e3a179199.png)



## ▼ [Initialize](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/VFX-Contexts#initialize)

パーティクルの初期化周りの設定になるかと思われます。
今回は主に以下の設定を行っております。

- 生存時間の決定(Set Lifetime Random)
- Meshのスケールをロゴのサイズに合わせて調整(Set Size)
- MeshのPivotを下端に設定(Set Pivot)
- パーティクルを飛ばす向きの決定(Add Velocity(Random Direction))

![Initialize.png](https://qiita-image-store.s3.amazonaws.com/0/80207/29d2ab91-5e61-053e-3796-4636c7978a4d.png)



## ▼ [Update](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/VFX-Contexts#update)

[Turbulence](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/VFX-Blocks#turbulence)と言うブロックを設定してパーティクルの動きを制御してます。
今回の実装で言えばDrag coefficientの値を調整することで抵抗値を調整したりしてます。

![Update.png](https://qiita-image-store.s3.amazonaws.com/0/80207/84c50393-8af0-d3c1-b006-822533107567.png)


## ▼ [Output](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/VFX-Contexts#output) ([Quad Output](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/VFX-Contexts#quad-output))

こちらはパーティクルの形状周りの制御になるかと思われます。
今回は主に以下の設定を行っております。

- Quadで出力
- Texture/Blend Mode等の設定
- 向きの設定
    - [Orientation Blocks](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/VFX-Blocks#orientation-blocks)
- 角度の設定
- 生存時間に応じた色の設定

ロゴアニメーションに於いて肝になるであろう回転処理についてもこちらで行っておりますが、詳細については後述します。

![QuadOutput.png](https://qiita-image-store.s3.amazonaws.com/0/80207/86506463-d64c-2ebc-be54-874b40d1d286.png)



## ▼ 回転処理について

回転処理については以下の様に実装してます。
基本的には「[以前書いた頂点シェーダーでロゴを回す記事の内容](https://qiita.com/mao_/items/291c0ecb465ef27c6c6c)」を元にVFX Graph向けに組み直したものとなります。

![example2.png](https://qiita-image-store.s3.amazonaws.com/0/80207/c3b2025a-f625-6b51-2e11-28009ddf1819.png)


まず最初に移植元となる頂点シェーダーのコードを一部引用します。

```text
            // コマ落ちアニメーション(radians)
            groupshared float Animation[16] = 
            {
                1.5707963267948966,
                1.4660765716752369,
                1.3613568165555772,
                1.2566370614359172,
                1.1519173063162575,
                1.0471975511965979,
                0.9424777960769379,
                0.8377580409572781,
                0.7330382858376184,
                0.6283185307179586,
                0.5235987755982989,
                0.4188790204786392,
                0.31415926535897926,
                0.2094395102393195,
                0.10471975511965975,
                0,
            };

            // ......... 

            v2f vert (appdata_t IN)
            {
                // ............

                // 「_SinTime : -1~1」の値を0~1の範囲に正規化
                float normal = (_SinTime.w + 1) / 2;
                // SinTimeの値を0~15の範囲にスケール。値を量子化することでアニメーションテーブルのIndexとして扱う。
                float rot = Animation[round(normal*15)];

                // 回転行列
                float sinX = sin(rot);
                float cosX = cos(rot);
                float2x2 rotationMatrix = float2x2(cosX, -sinX, sinX, cosX);

                // 回転の適用。TextureSettingsに於けるPivotは「Bottom」を期待
                IN.vertex.yz = mul(rotationMatrix, IN.vertex.yz);
                // ............
            }

```

こちらをVFX Graphに移すとなった際のポイントとして、算術周りはほぼそのままの形で移植することは出来ましたが、配列の定義が出来ないように思われたので別のアプローチで実装する必要がありました。

その為に今回の実装としては16x1の画像のRチャンネルに回転情報を入れておき、VFX Graph側でサンプリングして配列の代わりとして参照するやり方で実装しております。

![hoge.png](https://qiita-image-store.s3.amazonaws.com/0/80207/81cb0ac8-76ad-8d2e-2b47-fd83ea9d553b.png)

後はQuad Outputの「Set Angle」ブロックに対して、上記で得た角度情報をX軸に流し込んでやることでコマ落ちアニメーションが実現できます。









# ▽ 参考/関連サイト

- [Visual Effect Graph - Document](https://github.com/Unity-Technologies/ScriptableRenderPipeline/wiki/Visual-Effect-Graph)
- [Feedback Wanted: Visual Effect Graph](https://forum.unity.com/threads/feedback-wanted-visual-effect-graph.572110/)
- [Visual Effect Graph - Easy Setup (For GitHub version ONLY)](https://github.com/peeweek/VisualEffectGraph-EasySetup)
- [【Unity】Visual Effects Graph(VFX Graph)をとりあえず動かしてみる](http://notargs.hateblo.jp/entry/vfx_graph)

- [HDRenderPipelineを使わずにVisualEffectGraphをインストールする](https://forty.hatenadiary.jp/entry/2018/10/29/012151)
    - ※今回の実装では直接関連しているわけではないが、いずれ検証してみたいのでメモとして記載。

- [ドカベンメーカー](https://arkw.net/products/web/dokaben//)