はじめに
こんにちは、初投稿です!
僕がUIのマテリアルを研究し続けていて丁度「オーディオ信号の周波数グラフ」を作成してみたかったところです。いい機会なので記事にまとめてUEアドベントカレンダーに載せようと思ったきっかけです。
こちらの記事ではラジアルとリニア形式、グラフの2パターンをご紹介していきます。
初心者も作れるように画像とステップを多く含めました。ベテランならばどんどん飛ばして確認したい項目のみでもご確認いただければと思います。
音に関する豆知識
本番に入る前に、作り方に当たって最低限こちらのオディオ知識が必要となります。よーく見かけるものに関わらず、意外とグラフの読み方知らない方が多いのではないかと思います。(恥ずかしながら僕はそうでした)
分かりやすく説明するためこちらのシンプル図を用意しました。
X方向には可聴周波数(Hz)幅でY方向には音圧(dB)です。横方向は左が低い音を示し、右が高い音です。縦方向は音の圧力、量のことですね。バーが高いとうるさいということです。
という訳で、このパラメーターがあればグラフが作れます。
作りこなせばできるようになること
最後まで作ってしまえばこちらの要素ができるようになります。
ー RenderTargetへ描画する方法
ー BPからマテリアルに値を渡す方法
ー Synesthesia NRTというサウンドプラグインの設定方法
ー BPインターフェスの使用方法
準備
環境と必要なものまとめです。
ー UE5
ー Audio Synesthesia (プラグイン)
ー サウンド(.wav)を用意 (最低限1x)
Audio Synesthesiaはサウンドファイルの情報を読み取りBPで扱うようにしてくれる、便利なプラグインです。
デフォルトにUE5にインストールされているはずですが、念のためご確認しておいてください。
Beta版になってます。プラグインメニューからチェックが入っていれば大丈夫です。
目次
ー フローのご紹介
ー ① NRT作成とSoundの設定
ー ② BPInterfaceの作成
ー ③ WidgetとRenderTargetの作成
ー ④ ActorBPの作成
ー ⑤ MaterialVisualの作成 (バーの作成)
ー ⑥ MaterialRenderの作成
ー テスト
ー NRTSetting説明
フローのご紹介
詳細に入る前に迷わずアセットがどのように繋いでいるかをご理解していただくためにこちらの大まかなフロー図を用意しました。
名称 | 解説 |
---|---|
NRT | サウンド情報を収穫してくれるアセット(プラグイン) |
Sound | サウンドファイル |
Actor BP | ウイジェットとサウンド間に値渡しするBP |
Widget HUD | 画面に映るWidget |
BP Interface | BP間に値渡しをシンプル化してくれるもの |
Widget Render Target | RenderTarget描画管理するウイジェット |
Material Visual | バー作成するマテリアル |
RenderTarget Texture | バーが描かれるテクスチャー |
Material Render | MaterialVisualの透明度をサポートしてくれるマテリアル |
描画処理説明と最適化について
毎フレームにRenderTargetへバーを書き込み、バーの数により描画回数が決まります。つまり、バーが多ければ多いほど描画回数が増して処理が重たくなります。具体的の例を挙げるとバーを40描画するには1フレーム当たり40回バーのポジションと長さを計算しRenderTargetへ出力します。。ここのRenderTargetは保存テクスチャーとして使用され、バーが全部揃った時点でMaterialRenderを通してアルファを反対し、1フレーム当たりの描画結果をスクリーンに写します。
処理が多いため、こちらの制作フローが最適化されておりません。本来はマテリアル内で解決したかったのですが、BPからの配列変数をマテリアルに渡せない代わりにRenderTargetテクスチャーをしました。
プロファイリングはまだしておりませんが、興味深いです。いつかの課題として残しておきます (笑
こちらの図は上記に解説したRenderTargetの使い方です。マテリアルを何回も通してRenderTargetに描画して、処理が終わると描画結果を出します。それを毎フレームに行います(!!)
① NRT作成とSoundの設定
さて、本編に入りましょう。
まず.wavのファイルをUEに入れます。このようなアセットが出てきます。
僕は無料BGM、Musmusというサイトからサウンドをダウンロードしました。
.wavがなければオンラインで簡単にどんな拡張子からでも.wavに変換できるコンバーターが存在します。
次に、オーディオ情報管理ができるNRTファイルの作成です。サイドメニュー(右クリック)からAudio>Analysis>SynesthesiaNRTを選択します。
ウインドウが現れます。ConstantQNRTとLoudnessNRT一つずつ作成します。
ConstantQNRTは周波数情報に該当するものです。ConstantQNRTだけでグラフが成り立てます。値は配列形式で出力されます。バーの位置は配列のIndexで定められ各Indexにある値が音圧に該当します。
LoudnessNRTは音圧に該当するものです。こちらの音圧は全周波数合計に該当し、ラジアルマテリアル中心の円がリズムについてくるようにLoundnessNRTを使用します。
次に、Audio>Analysis>Synesthesia NRT Settingを作成します。
周波数を調整したいだけなので作成するのはConstantQNRTSettingだけで十分です。
合わせてアセット3つになります。
各アセットの設定をしましょう。
ConstantQNRT
先ほど作成したConstantQNRTSettingとサウンドをこのように設定します。
LoudnessNRT
今回はLoudnessNRTSettingsを作成していないのでDefaultLoudnessNRTSettingsのままで大丈夫です。サウンドは上記と同様です。
ConstantQNRTSetting
ここはグラフのパラメーターを調整できる場所です。とりあえずこのように設定しておけばよいでしょう。
重そうと思えばFFTSizeをLargeもしくはXLargeにするとよいです。
SartingFrequencyは最小数の周波数を示すものです。値が高いとバーが減りますので、常に最小値(20)にしておきます。
NumBandsはバーの数になります。バーが多いと描画の回数が増え処理が重いのでご注意を。
Noise(dB)は最終的にバー合計の高さを調整できるのでその段階で弄りません。
それ以外はほぼ触っていないです。パラメーターに関してご興味があればこちらの公式ページをご確認ください。
これでサウンド編が完了です。
② BPInterfaceの作成
こちらのアセットの役名はクラスの親子を気にしないで変数をWidgetとBP間に簡単に渡せる、非常に便利なものです。BPとBPのつなぎ込み方法は色々あるのですが、今回はBPInterfaceがどのものなのかを含めて見たかったので使用しました。
サイドメニューのBlueprint>BlueprintInterfaceを選択します。
開いてから空なのでファンクション1つ追加します。「Add」ボタンを押し「OnSetSynthetiseur」という名をつけます。
InputセクションにはこのようにFloat変数4つ作成します。
名称 | 解説 |
---|---|
Db | 一定時の音圧(バー1本当たりの高さ) |
FreqNb | 可聴周波数の幅 (バーの数) |
FreqHz | 一定時の周波数(バー1本当たりの位置) |
Loudness | 音圧合計(円型マテリアルの中心に使用) |
保存し、閉じましょう。BPInterfaceを完成しました。
③ WidgetとRenderTargetの作成
ウイジェットはWBP_HUDとWBP_RenderTarget、2つ作成します。
サイドメニューからUserInterface>WidgetBlueprintから作成できます。
WBP_RenderTarget
変数とBPInterfaceの設定
BPInterfaceがWidgetから見えるため、ClassSettingsよりBPInterfaceを繋げておきます。Addボタンを押して作成したBPInterfaceをリストから選択します。
次に必要な変数を作ります。
TextureRenderTargetを設定するにはRenderTargetというテクスチャーが要るのでまず作りましょう。
RenderTargetテクスチャーの作成方法
サイドメニューからTexture>RenderTargetを選択します。
テクスチャーセッティングはこのようにしておきました。2Kが大きいですが、研究プロジェクトなのでとりあえずよいクオリティで出しておきましょう。
できたテクスチャーをWBP_RenderTarget内のTextureRenderTarget変数に設定します。
RenderMaterial内にはステップ⑤番に作る予定のマテリアルを入れますが、一旦変数だけ作っておいて後に戻って繋げましょう。
DynamicMaterial変数はBPで定義します。(次のステップ)
Graph
OnSetSynthetiseurは先ほど接続したBPInterfaceから呼び出せます。右クリックしOnSetSynthetiseurを検索すれば出てくるはずです。
このようにノードを繋げます。MaterialParameterの名称に気をつけましょう。
WBP_HUD
ウイジェット内は黒背景とContainerを作成しています。Containerはなんでもいいです。Canvas,OverlayもしくはGridPanel。ウイジェットGraphからWBP_RenderTargetを生成してContainerの子供として加えますので、UMGのヒエラルキーに直に置きません。
ウイジェットのGraph(BP)側ではOnInitRenderTargetのCustomEventを作成し、WBP_RenderTargetを生成した後に変数化しContainerに追加します。ReturnValue上に右クリックして「Promote to Variable」で変数化してくれます。Createノードは「Create Widget」の検索から生成できます。Class中には先ほど作ったWBP_RenderTargetウイジェットを設定します。
そのOnInitRenderTargetはのちほどActorBPから呼びます。次のステップ(ActorBP)に移りましょう。
④ ActorBPの作成
Actorブループリントを作成します。サイドメニューからBlueprint>BlueprintClassを選択します。
そのブループリント中にはオディオコンポネントを追加し、変数はLoudnessNRT、ConstantNRT、CQ(Float配列)とWBPHUD(HUDのウイジェットクラス)を作成します。
コンポネントオーディオを選択しながらDetailsタブ内にサウンドアセットを設定します。
Graph
BeginPlayノードからサウンド設定、CreateWidgetからWBP_HUDを生成し、Viewportに出します。ゲーム開始時にサウンドが再生するようにPlayノードも入れておいてCQ変数の初定義をします。(CQ変数は毎フレームに更新する必要があるのでBeginPlayに必要ないかもです、念のため定義しておきました)
OnSynthetiseur(Call Message)ノードを出してTargetにはHUDの子供であるWBP_RenderTargetを接続します。それでUEがWBP_RenderTargetウイジェットへオーディオ情報を送り、RenderTargetのテクスチャーに描画するようにできました。
ループに入る前に、CQ変数を更新し、各パラメーターに情報を渡します。
次はいよいよマテリアル編に入ります。
⑤ MaterialVisualの作成 (バーの作成)
マテリアルはサイドメニューから直接にマテリアルを選択すると作成されます。
マテリアルをUserInterfaceとTranslucentにします。マテリアル内のDetailsパネルから変更できます。
さてと、下記のグラフに移ります。
分かりやすくするため、マテリアルファンクションを作成しました。(MF_SynthBar)
下記スクショに詳しくノードを載せています。
オーディオ用のパラメーターは先ほど設定したActorBPでの値はオーディオアセットからの情報をウイジェットに送り出されてマテリアル内の値は上書きされますので、ここでは仮な状態でよいです。
IsCircularノードは「Switch Parameter」です。On/Offにより縦型、ラジアル型で姿を入れ替えられます。
GradientScale,GradientOffsetXとGradientOffsetYはラジアル型限定のパラメーターです。
バーの長さによりColor2が見えなかったりするのでグラデーションの見るどころを調整するためのパラメーターです。
MF_SyntBarの詳細
マテリアルファンクションの詳細に入ります。グラフが少し大きいので部分を取り分けてスクショを載せます。
バーの姿2つあるのでOutputノードを2つ用意してます。
まず、マテリアルファンクションの作成方法。
サイドメニューからMaterial>MaterialFunctionを選択します。
できたファンクションアセットをマテリアルにドラッグ・アンド・ドロップすればノードとして出現します。
ファンクションの中身ですが、名称付けられるRerouteを使用し、ぐちゃぐちゃならないようINPUTをこのようにまとめます。
Linear
Radial
RadialUVは別の名称付のRerouteを作成しこのようにUVを極座標系に変換します。
色々ノードを繋げていますが、一つ一つ説明していくとより長くなってしまうので今回は省略してスクショを参考にして再現していただければと思います。
マテリアルインスタンスを作成
マテリアルができると、インスタンスを作成します。マテリアルを右クリックし、Create Material Instanceを選択します。
isCircularにチェックを入れるとラジアル形式で、はずすと縦バーの形式に代わることが確認できます。
バーは一本しか見えないのは正しいです。全バーが表示されるのはRenderTargetだけなのでBP処理が入らないと見えないです。
ラジアル型とリニア型一つずつのインスタンスを作ってしまうといいかもしれません。BPでインスタンスを差し替えればよいし、インスタンス内のIsCircularを調整すれば同じ結果にできます。ただし、SwitchParameterはランタイムで制御できないので注意しましょう。
忘れずにWBP_RenderTargetにマテリアルを設定しましょう
WBP_RenderTarget再び開き、RenderMaterialに作ったマテリアルインスタンスを設定します。
⑥ MaterialRenderの作成
先ほど作ったマテリアルをそのままRenderTargetに描画してもよいのですが、透明度に問題があるため、透明度が美しく映るように別マテリアルを作成してます。どういう問題なのかは気になる方はこちらご確認ください。
新たなマテリアルを作成し、ドメインをUserInterface、ブレンドモードをAlphaCompositeにします。
ノードはシンプルです、作ったRenderTargetのテクスチャーをSamplerに設定し、変数化します。そしてSamplerのアルファをリバースするだけです。
WidgetにMaterialRenderを設定
最後に作ったWBP_RenderTargetがマテリアルを映すためにImageにマテリアルを割り当てます。
これですべての設定が完了しました。
テスト
テストはActorBPを好きなシーンにおいて再生するだけです。WBP_HUD黒背景なしでも3Dシーン上に表示できますよ。
懸念点
背景が黒くないとバーのエッジに白いピックセルが少し写ってしまいます。原因はまだ調べ中です。
また、ラジアル型では最初と最後のバーがどうしてもくっついているのですが、マテリアルを少し調整すればきれいに並べられる気がします。
NRTSettingについて
最初に作ったNRTSettingを再び開き、バーの数、周波数の幅を調整できます。
バグかもしれませんが、NRTSettingを保存するだけでパラメーターが更新されないようなのでご注意ください。煩わしいですが、ConstantQRTアセットに変更がないとセッティングが渡り移らないようです。僕は毎度ConstantQRTのサウンドを変えて戻してあげて保存するようにしてやっとNRTSettingの最新パラメーターが適用されます。
最後に
思ったより長い記事になりまた。お疲れ様です!
コメントや開発アドバイス、記事の書き方アドバイス、ご質問などがありましたらいつでもTwitter(X)上でご連絡ください。
https://twitter.com/ko_yuki_lo