VFXGraphにGraphicsBufferから値を渡す

#やること
VFXGraphに対してGraphicsBufferを介して値を渡す。
VFXGraphに複数の値を渡すには一旦テクスチャにベイクする必要があったが、
バージョン12からGraphicsBufferがサポートされているので試してみる。
GraphicsBufferに対して値を渡すのにComputeShaderを使う方法とC#スクリプトを使う方法をトライする。
#参考文献
https://github.com/keijiro/VfxGraphGraphicsBufferTest
#ComputeShaderを使う方法
参考文献を更に簡略化しただけですが
ComputeShader

```cs
#pragma kernel WaveAnimation

float Time;
RWStructuredBuffer<float3> PointBuffer;

[numthreads(4,4,1)]
void WaveAnimation (uint2 id : SV_DispatchThreadID)
{	
	float z = sin(id.x * 0.5 - Time * 2) *id.y* 0.5;
    
	uint index = id.x + id.y * 16;
	PointBuffer[index] = float3(id.x, id.y, z);
}
```

C#

```cs
using UnityEngine;
using UnityEngine.VFX;

public class WaveAnimation : MonoBehaviour
{
    [SerializeField] ComputeShader ComputeShader;
    GraphicsBuffer GraphicsBuffer;
    int kernel;
    
    private void Start()
    {
        GraphicsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 16 * 16, 3 * sizeof(float));
        GetComponent<VisualEffect>().SetGraphicsBuffer("TestBuffer", GraphicsBuffer);
        kernel = ComputeShader.FindKernel("WaveAnimation");
    }

    private void Update()
    {
        ComputeShader.SetFloat("Time", Time.time);
        ComputeShader.SetBuffer(kernel, "PointBuffer", GraphicsBuffer);
        ComputeShader.Dispatch(kernel,4, 4, 1);
    }

    private void OnDestroy()
    {
        GraphicsBuffer.Release();
    }
}
```

VFXGraph
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1947827/06867679-896d-61e2-1097-b3240f6b0a21.png)
結果
![wave.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1947827/f237545d-d35b-e282-2477-4c764670994c.gif)
#軽く解説１
ComputeShaderに関しては自分もしっかりとは理解できていないのですが、idというVector2を与えた時その値と時間に応じたZの値を計算させています。
https://edom18.hateblo.jp/entry/2017/05/10/083421
https://edom18.hateblo.jp/entry/2017/10/07/171147
このサイトが参考になりました。

C#の方はStartでGraphicsBufferを作成しVisualEffectのプロパティに渡しています。
Updateで現在時刻をcomputeshaderに渡して計算の命令を出しているようです。
基本的にはComputeBufferの上位互換？みたいなので、ComputeBufferが分かっている人ならすんなり使えると思われます。
https://qiita.com/IKD-Member/items/f424b3e53a6482cab884
この記事も参考になりました。
#C#スクリプトを使う方法
自分が前書いたテクスチャにベイクして渡す方法のサンプルを改造していきます。
https://qiita.com/rasashi1/items/9b44957d41fad8355f2b
グラフ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1947827/7b8e44a3-66bc-d40e-2428-67e359cf8f82.png)

vfx側のスクリプト

```cs
using UnityEngine;
using UnityEngine.VFX;

public class MagicOrb : MonoBehaviour
{
    VisualEffect vfx;
    GraphicsBuffer GraphicsBuffer;

    //Orbの状態を表す　
    //0：緑色　1:赤色　2:青色
    int[] orbState = new int[9];
    public int[] OrbState
    {
        get { return orbState; }
        set { orbState = value; }
    }

    private void Start()
    {
        vfx = GetComponent<VisualEffect>();
        GraphicsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 9, sizeof(int));
        vfx.SetGraphicsBuffer("TestBuffer", GraphicsBuffer);
        SetData2Buffer();
    }

    public void SetData2Buffer()
    {
        GraphicsBuffer.SetData(orbState);
    }
}
```
キューブ側のスクリプト

```cs
using UnityEngine;

public class OrbInteractor : MonoBehaviour
{
    [SerializeField] bool left = false;
    [SerializeField] MagicOrb magicOrb;

    bool change = false;

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Orb"))
        {
            //object名の数字部分を切り出す
            string name = other.gameObject.name;
            int num = int.Parse(name.Substring(4));

            change = false;

            if (left && magicOrb.OrbState[num] != 2)
            {
                magicOrb.OrbState[num] = 2;
                change = true;
            }
            else if (!left&&magicOrb.OrbState[num] != 1)
            {
                magicOrb.OrbState[num] = 1;
                change = true;
            }

            if (change)
            {
                //magicOrb.SetMap2VFX();
                magicOrb.SetData2Buffer();
            }
        }
    }
}
```

かなりシンプルになりましたね。
もっと大量のデータを渡すパターンも試してみたいところですが一旦これで完了で...