VisualEffectGraphのPropertyBinderを自作する

# 初めに
UnityのVisualEffectGraphには任意のゲームオブジェクトの位置や入力情報などを取得してPropertyに格納できるPropertyBinderという便利な機能があります。
今回はAudioSourceで再生している音声の波形をVFXGraphに渡すPropertyBinderを自作してみます。デフォルトでもAudioSourceのスペクトラムを渡すBindingが存在しますが音声の波形を渡す機能が無いので、この2つの波形を渡すBindingを自作します。
自作したPropertyBinderを利用したサンプルプログラムは [ここ](https://github.com/5h00T/VFXGAWB) にあります。
# 作成方法
VFXBinderBaseというクラスを継承した子クラスを作成し、IsValidメソッドとUpdateBindingメソッドを実装することでBindingとして利用可能になります。
VFXBinder属性は実際にこのPropertyBindingsを追加する際のパスとなります。

```C#
using UnityEngine;
using UnityEngine.VFX;
using UnityEngine.VFX.Utility;

[VFXBinder("sh00t/AudioWave")]
public class AudioBinder : VFXBinderBase
{
    [VFXPropertyBinding("UnityEngine.Texture2D"), SerializeField]
    ExposedProperty outputWaveMapProperty = "OutputWaveMap";

    [VFXPropertyBinding("UnityEngine.Texture2D"), SerializeField]
    ExposedProperty outputSpectrumMapProperty = "OutputSpectrumMap";

    [VFXPropertyBinding("System.UInt32"), SerializeField]
    ExposedProperty waveLengthProperty = "WaveLength";

    [VFXPropertyBinding("System.UInt32"), SerializeField]
    ExposedProperty spectrumLengthProperty = "SpectrumLength";

    [SerializeField]
    private AudioSource audioSource;
    private Color[] outputColors;
    private Color[] outputSpectrumColors;
    private Texture2D outputWaveMap;
    private Texture2D outputSpectrumMap;
    private float[] outputWave;
    private float[] outputSpectrum;
    public int waveLength = 512;
    public int spectrumLength = 512;
    public FFTWindow fftWindow = FFTWindow.BlackmanHarris;

    public override bool IsValid(VisualEffect component)
    {
        return audioSource != null &&
            component.HasTexture(outputWaveMapProperty) &&
            component.HasTexture(outputSpectrumMapProperty) &&
            component.HasUInt(waveLengthProperty) &&
            component.HasUInt(spectrumLengthProperty);
    }

    private void GetWave()
    {
        if (outputWave == null || waveLength != outputWave.Length)
        {
            outputWave = new float[waveLength];
            outputColors = new Color[waveLength];

            outputWaveMap = new Texture2D(waveLength, 1, TextureFormat.RFloat, false);
            outputWaveMap.filterMode = FilterMode.Bilinear;
            outputWaveMap.wrapMode = TextureWrapMode.Clamp;
        }

        if (outputSpectrum == null || spectrumLength != outputSpectrum.Length)
        {
            outputSpectrum = new float[spectrumLength];
            outputSpectrumColors = new Color[spectrumLength];

            outputSpectrumMap = new Texture2D(spectrumLength, 1, TextureFormat.RFloat, false);
            outputSpectrumMap.filterMode = FilterMode.Bilinear;
            outputSpectrumMap.wrapMode = TextureWrapMode.Clamp;
        }

        audioSource.GetOutputData(outputWave, 1);

        for (var i = 0; i < waveLength; i++)
        {
            outputColors[i].r = outputWave[i];
        }
        outputWaveMap.SetPixels(outputColors);
        outputWaveMap.Apply();

        audioSource.GetSpectrumData(outputSpectrum, 1, fftWindow);
        for (var i = 0; i < spectrumLength; i++)
        {
            outputSpectrumColors[i].r = outputSpectrum[i];
        }
        outputSpectrumMap.SetPixels(outputSpectrumColors);
        outputSpectrumMap.Apply();
    }

    public override void UpdateBinding(VisualEffect component)
    {
        GetWave();

        component.SetTexture(outputWaveMapProperty, outputWaveMap);
        component.SetTexture(outputSpectrumMapProperty, outputSpectrumMap);
        component.SetUInt(waveLengthProperty, (uint)waveLength);
        component.SetUInt(spectrumLengthProperty, (uint)spectrumLength);
    }
}

```
IsValidはバインディングが作成できるかどうかを検証するメソッドです。このメソッドがtrueを返した場合のみ後述するUpdateBindingメソッドが実行されます。このメソッドによって有効ではないと判断されると警告が表示されます
![VFXGraphAudioBinder - SampleScene - PC, Mac & Linux Standalone - Unity 2020.2.1f1 Personal_ _DX11_ 2021_02_10 1_57_58 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211264/a74dda6f-38cb-2d99-8447-9dc3a2bbb189.png)![VFXGraphAudioBinder - SampleScene - PC, Mac & Linux Standalone - Unity 2020.2.1f1 Personal_ _DX11_ 2021_02_10 1_58_05 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211264/3dbcadaa-3ac2-0f86-0612-d70fb5a00547.png)
今回はAudioSourceの登録と、各ExposedPropertyに存在するプロパティが入力されていない場合にfalseを返す処理にしました。
VFXPropertyBinding属性は一致する型をVFXGraphのPropertyから選択できるドロップダウンを提供します。
![vfx (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211264/73fb6a7d-0406-2639-deec-13c99dda6063.png)![VFXGraphAudioBinder - SampleScene - PC, Mac & Linux Standalone - Unity 2020.2.1f1 Personal_ _DX11_ 2021_02_10 2_13_01 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211264/57c5512e-c08e-d063-a176-4758e653dc6d.png)
UpdateBindingメソッドではIsValidメソッドがtrueを返した時に行われる、プロパティに値を渡す処理を作成します。音声波形とスペクトラムを取得してプロパティに渡すテクスチャを更新しています。初回実行時と途中で取得する波形の長さが変更された場合には新しく配列などを生成し直しています。

# 終わりに
PropertyBinderを自作することでいろんな情報をインスペクター上でVFXGraphのプロパティと結びつけられるのでどんどん使っていきましょう。
![gif_animation_002.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211264/71582abf-6f87-85ac-7c5b-8c8a04db9048.gif)

# 参考
- https://docs.unity3d.com/Packages/com.unity.visualeffectgraph@7.1/manual/PropertyBinders.html