はじめに
Ghost PhotographersというVRホラーゲームで実装した遮蔽効果の備忘録です。
#CRI_ADX勉強会 で質問があったのでその解答の補足として。
デモ
左側がVR視点での画面。
右側がシーンを上面から見たワイヤーフレーム表示。
基本的な遮蔽
リスナーから音源へ向かってレイを飛ばします。
ここで、壁があれば遮蔽されている判定にします。
レイはリスナーと音源の直線を1本のみ。
再生開始時と、再生中は限られたタイミングでチェックします。
デバッグ表示について
時々ラインが表示されているタイミングで、レイが飛んでいます。
赤いラインの時は、遮蔽があったことを示します。
間に壁があった。
壁までの距離も測っていて、あまりに壁に近い場合は、遮蔽の判定処理をスキップします。
目の前にある壁で過剰に遮蔽のあり・なしを切り替えると不自然になるため。
距離が離れいてる状態での過去の情報に従う形になります。
(統計的、ヒステリシスな情報にひっぱらられます。)
おかしなことになりそうですが、キャラクターがワープしたりしないし、
ゆっくりとしか移動できないため、大丈夫そうでした。
遮蔽効果を付与したい音源を限定する
減衰が強く目の前でしか鳴らない音は、遮蔽処理対象外。
このゲームでは、シーン内の遮蔽すべき音がおおよそ事前に把握できたため、
特定の音だけ遮蔽効果を付与することで、処理量を最小限にしています。
コンポーネントをつけることで、対象の音源が遮蔽トレースの対象となります。
見た目として、遮蔽がかかっていないと不自然と感じる音にのみに設定します。
遮蔽効果は曖昧
処理負荷の問題もあり、厳密な遮蔽はあきらめつつも、不自然に壁抜けして聞こえないように工夫しています。
さまざまな処理スキップ
リスナーが動いていない場合は処理しない
リスナー位置が移動量の閾値を超えていない場合は、遮蔽のレイは飛ばしません。
遠距離は処理しない
遠くで鳴っている音に関しては距離減衰がかかっている想定で、レイは飛ばしません。
一定間隔で処理
移動し続けていても 連続して対象にたいしてレイを飛ばすかどうかの処理自体を間引いています。
対象にレイを飛ばしていないタイミングは、過去の情報に依存しています。
ただし、時間的に最近遮蔽効果が変化した音に対しては、間引き時間を少な目にしています。
変化があったオブジェクトが移動していたり、注目すべき音として、処理の優先度が上がるようなイメージです。
遮蔽の変化は緩慢に
遮蔽が変化した時、フィルター具合をなだらかに変化するようにしています。
プレーヤーの移動速度が遅いため、なだらかでも不自然には聞こえません。
逆に、はっきり変えすぎると、処理スキップしている粗が目立ち始めます。
遮蔽時のパラメータ変化
遮蔽時の変化は、音量変化とローパスフィルターになります。
ローパスフィルターは、周波数の変化が音によっては耳障りになる場合もある点注意が必要です。
今回はホラー演出という誤魔化しもあります。
おわりに
今回はADX2を利用していますが、ADX2のUnityには遮蔽の処理がとくに無いのと、
MetaQuest2でも動くスタンドアローンVRタイトルであるため、酔い軽減のためにも
処理をとにかく削りたいため、必要最小限のレイで実装してみたものになります。
何かの参考になれば幸い。