はじめに
ShaderGraphを利用して、サウンド合成をやってみました。
以下の動画は、ShaderGraphを使って実際に音を作っているところをキャプチャしたものです。
サンプル
サウンド合成までの流れ
まずはテクスチャを用意し、これをShaderGraphで加工します。
そして、加工したテクスチャをOnAudioFIlterReadメソッド経由でオーディオデータに書き込み、音としてスピーカーから再生します。
OnAudioFilterReadについて
Unityのサウンド再生では、ゲームシーン内のAudioSourceが再生した音をAudioListenerが拾いスピーカーへ流します。
OnAudioFilterReadメソッドを利用することで、AudioSourceからAudioListenerへのデータの流れに独自の処理を挟み、音を加工することができます。
例えば、自分でディレイエフェクトを実装して音に反響を付けたり、ローパスフィルターを実装して音の高周波成分を削ったり・・・
Unity公式リファレンス
Unity公式リファレンスにも解説やサンプルが載っています。
docs.unity3d.com
OnAudioFilterReadの利用例 : ディレイエフェクト
ディレイエフェクトの実装例が載っている記事
qiita.com
実装
STEP1. ソースコード
実装自体は以下のソースコードで完結します。
using UnityEngine; using Random = System.Random; [RequireComponent(typeof(AudioSource))] public class SoundShader : MonoBehaviour { const int Width = 128; // texture width const int Height = 64; // texture height [SerializeField, Range(0f, 1f)] private float soundVolume = 0.05f; [SerializeField] private Material soundMaterial; // Material for audio synthesis RenderTexture soundRT; // シェーダーで加工した結果を保持するためのRenderTexture Texture2D soundTexture; float[] textureBuffer; // オーディオに渡すためのデータ int bufferReadPos = 0; // データの読み取り位置 void Start() { soundRT = new RenderTexture(Width, Height, 0); soundRT.Create(); textureBuffer = new float[soundRT.width * soundRT.height]; soundTexture = new Texture2D(soundRT.width, soundRT.height); } void Update() { UpdateBuffer(); } private void UpdateBuffer() { // シェーダーでテクスチャを加工し、結果をsoundRTに保存 Graphics.Blit(soundRT, soundRT, soundMaterial, 0); // RenderTextureはそのままではピクセルにアクセスできないのでTexture2Dに変換 RenderTexture.active = soundRT; soundTexture.ReadPixels(new Rect(0, 0, Width, Height), 0, 0); // RenderTexture -> Texture2D // Texture2D -> float[] // Texture2D.GetPixel()をOnAudioFilterRead()の中で使うと怒られるので注意 int dst = 0; for (int y = 0; y < soundRT.height; y++) { for (int x = 0; x < soundRT.width; x++) { // とりあえず、テクスチャのrチャンネルをオーディオに渡す textureBuffer[dst++] = soundTexture.GetPixel(x, y).r * 2f - 1f; // [0:1] -> [-1:1] } } } void OnDestroy() { soundRT.Release(); DestroyImmediate(soundTexture); } void OnAudioFilterRead(float[] data, int channels) { int dst = 0; while (dst < data.Length) { float value = textureBuffer[bufferReadPos] * soundVolume; for (int i = 0; i < channels; i++) { data[dst + i] = value; // write } dst += channels; bufferReadPos ++; if (bufferReadPos == textureBuffer.Length) { bufferReadPos = 0; } } } }
STEP2. シェーダーグラフを作る
今回は以下のようなノイズを出力するシェーダーグラフを作ってみます。
STEP3. マテリアル作成
シェーダーグラフを右クリックしてマテリアル作成します
STEP4. マテリアル登録
適当なオブジェクトにSTEP1.のコンポーネント(SoundShader)をアタッチし、STEP3のマテリアルを登録します
STEP5 ゲーム再生(完成)
ゲームを再生すると、ノイズのような音が再生されます。
Ryoji.Ikeda 風のノイズを作って遊んでみた
ノイズを作って遊んでみました。
www.youtube.com
ノード全体