はじめに
ShaderGraphを利用して、サウンド合成をやってみました。
以下の動画は、ShaderGraphを使って実際に音を作っているところをキャプチャしたものです。
www.youtube.com
www.youtube.com
サンプル
github.com
まずはテクスチャを用意し、これをShaderGraphで加工します。
そして、加工したテクスチャをOnAudioFIlterReadメソッド経由でオーディオデータに書き込み、音としてスピーカーから再生します。
OnAudioFilterReadについて
Unityのサウンド再生では、ゲームシーン内のAudioSourceが再生した音をAudioListenerが拾いスピーカーへ流します。
OnAudioFilterReadメソッドを利用することで、AudioSourceからAudioListenerへのデータの流れに独自の処理を挟み、音を加工することができます。
例えば、自分でディレイエフェクトを実装して音に反響を付けたり、ローパスフィルターを実装して音の高周波成分を削ったり・・・
Unity公式リファレンス
Unity公式リファレンスにも解説やサンプルが載っています。
docs.unity3d.com
OnAudioFilterReadの利用例 : ディレイエフェクト
ディレイエフェクトの実装例が載っている記事
qiita.com
実装
実装自体は以下のソースコードで完結します。
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
ノード全体