【VFX】ポイントキャッシュの読み取り方法をTextrureで模倣する

# 【VFX】ポイントキャッシュの読み取り方法をTextrureで模倣する

## キーワード

Unity, Visual Effect Graph, Point Cache, Texture

## これは何

Textureを利用してVisual Effect Graphのパーティクル出現位置を調整する方法の紹介です。
こんな感じのTexture画像を読み込むと、
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/a21a4a94-25db-7767-0ea2-5003f93df8b1.png" alt="" width="64" height="64">
こんな感じに表現されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/d3a89e25-761b-7947-2154-98ee003d99d7.gif" alt="" width="200" height="200">

## 経緯

Visual Effect Graphで、TextureをPoint Cacheに変換して利用する記事を見つけました。
神屋電算 さんの記事です。 [【unity】visual-effect-graphで文字がサラサラっと消えるやつを作る](http://kamiya-densan.jp/2019/01/06/【unity】visual-effect-graphで文字がサラサラっと消えるやつを作/)

ただ、Point Cacheはパラメータを外部から設定できない？ようでした。Textureなら設定できるので、それっぽい情報を探していたところ、次の記事がUnityフォーラムにありました。
[Generation Of A Point Cache For Vfx Graph](https://forum.unity.com/threads/procedural-generation-of-a-point-cache-for-vfx-graph.659254/)
フォーラム上のRemyさんの回答がほぼほぼ答えなのですが、一部詰まった所がありましたので、一通り導入する方法をまとめました。

## 設定（概要）

下記手順で動作するようになります。
あらかじめ、Visual Effect Graphで表現したい図形の表示座標をRGBAで表現したpng画像があるとします。

### Texture

読み込み対象のTextureは次の制約を課されます。

- Mitmapを持たないこと(1)
- Wrap ModeがClampではないこと(2)
  - とりあえずRepeatにしておけばいいと思います
- Filter ModeがPointであること(3)
    - デフォルトはBilinearが設定されていると思います（多分）。Binaryですと場合によっては得られる座標点が補完されなめらかになります。
- Float値であること(4)
  - そうでない場合正しく座標が読み込まれません。Automaticもダメでした。
- sRGB, Alpha Is Transparencyについても、falseやNoneの方がいいようです。
- 以下の状態です。<br>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/f865d5fb-8980-1aeb-6d92-18d9309d8437.jpeg" alt="" width="400">

### Visual Effect Graphの操作
- TextureはSample Texture2Dより読み込みます。(1)
  - こちらの s ノードをSet Positionボックスの座標系に接続します。(2)
- 任意のUV座標を読ませるよう設定します(3)
  - Remyさんの回答は丁寧に座標を取得していますが、ざっくりRandom Numberを使っても大丈夫そうです。
- 以下の状態です。 ※Random numberはConstantフラグをOffにするか、Seedを別値にしておきます。(4)
![01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/910b7078-6be7-6a8d-14f9-db70f468b68c.jpeg)



## 詳細

概要通りの設定をすれば動作します。
ここからは、Textureがどのように座標情報として利用されるかなどをまとめました。

### Textureと座標の関係

読み込む画像の各ピクセル座標のr,g,bが、Unity空間上のx,y,zと対応しています。
r,g,bは今回の場合0-255で作成しています。
これをUnityが0-1のfloat値として利用していますが、スクリプト上でそれ以上の値を設定できるかもしれません（要検証）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/0a0d4b13-e495-28a1-1963-45b6f5dacc39.jpeg" alt="" width="15%" height="35%">

### Remyさんの添付グラフ

[Generation Of A Point Cache For Vfx Graph](https://forum.unity.com/threads/procedural-generation-of-a-point-cache-for-vfx-graph.659254/)にて回答されているRemyさんの手法についてまとめました。

まず外部に三つの変数を公開しておきます。
それぞれ、参照したいテクスチャ、幅、高さ、になります。
![4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/45b28112-1f42-418e-86a7-b169ad2b9740.jpeg)


Get Attribute: ParticleIdノードを用いて、生成されるパーティクルのIDを求めます。
以降、このIDに対して計算を行うことで、パーティクル毎に位置を決めていくようです。
Moduloのノードで、1次元での参照位置を計算しています。
![5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/cd735271-6d28-f637-2880-c17d103c6fea.jpeg)


次の部分では、参照位置を幅、高さの各軸になるよう求めなおしています。
![6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/e5cb9afd-9988-f4bd-0968-a05687f4c292.jpeg)


ここまでで参照するピクセル座標は決まりましたが、最終的にはUV座標でアクセスする必要があります。
下の計算部分でUV座標に直しています。
ここで、それぞれのDivideがfloatの結果を求めることを確認してください。
![7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/43320aa3-8beb-f974-3bd7-70a7baa0aa2e.jpeg)

intなどになっている場合、設定を開きます。
![7.5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/630025ce-17e3-829e-840c-6b3ea541119f.jpeg)


a,bともにfloatとし、再度設定ボタンを押してください。
![7.5-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/09c40b76-fd52-ff3a-7faa-c3df786b2ce0.jpeg)


結果で得られたUV座標は、それぞれの色ピクセルの端を指しています。
こちらで参照位置を各色の中心に変更します。
（理解が間違っていたらご指摘ください）
![8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/86d2c854-9ef3-bb71-e193-f76ccbcd2c20.jpeg)

こちらのDivideの項目も、Vector2でない場合は先ほどと同様に設定しなおしておきます。
![8.5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/90d83909-eaf8-f122-489e-5e1d0c51ce0b.jpeg)

最後にSet Positionと接続します。
ここではInitializeブロック内にSet Positionを追加し、そこに接続しました。
![9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/b353433a-5e5a-7720-719c-2103466bcf5d.jpeg)

このようにグラフを接続することで、読み込み画像の左下のピクセルから順に数値を読み取り、
各パーティクルの座標として利用できるようです。

#### 例

- 参照ピクセルが(r,g,b)=(255,0,0)つまり赤だった場合、(x,y,z)=(1,0,0)となるので、Unity空間上の1,0,0にパーティクルは生成されます。
- 参照ピクセルが(r,g,b)=(255,2555,0)つまり黄だった場合、(x,y,z)=(1,1,0)となるので、Unity空間上の1,1,0にパーティクルは生成されます。


- 次の画像は赤から黒へのグラデーションなので、左の画像をグラフの入力とすると、(0,0,0)-(1,0,0)の範囲内に16個の点が打たれます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/fb9fc210-703f-bac2-a39f-35735d9705b3.jpeg" alt="" width="10%" height="10%"> --><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/26a56be5-9461-8f87-4716-31e526fedff8.jpeg" alt="" width="10%" height="10%">

- ピクセル座標から色情報への変換イメージ
  - 文字AをUnity上でVFXを用いて表現したい場合、図のように座標を色情報に変換します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/ce9e2699-d410-3257-1861-332a2644279e.jpeg" alt="" width="65%" height="65%">
  - 最終的に、これはなに の項目で紹介した画像になります。

### その他

- ランダム打点な座標取得とRemyさんの手法の差
    - Remyさんの手法を取らずとも、Random NumberノードをSample 2DのUVに接続すれば似たような表示が可能です。ただ、Remyさんの手法の場合パーティクル座標は画像の左下から順に値を追うことになるので、表示を調整すると書き順の再現的なことができます。

- PNGにおける透明ピクセルの扱い
  - Textureの設定でAlpha Is TransparencyをTrueにした場合は無視され、Falseの場合は白として扱われるようです。
    - 微妙にUnity上での結果が変わりました。


## わかっていないこと

w……あなたは何者でどこへ向かうのでしょうか……

## グラフ全体

![13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248057/ef936fb6-ce84-68fc-1131-d801eb1721b6.jpeg)


## 参考

Unityフォーラム JakHussainさんの質問
[Generation Of A Point Cache For Vfx Graph](https://forum.unity.com/threads/procedural-generation-of-a-point-cache-for-vfx-graph.659254/)

神屋電算
[【unity】visual-effect-graphで文字がサラサラっと消えるやつを作る](http://kamiya-densan.jp/2019/01/06/【unity】visual-effect-graphで文字がサラサラっと消えるやつを作/)