レイトレーシングで音響シミュレーション(Audio Raytracing)
音響シミュレーション作ってみました。
— suzuki_ith (@suzuki_ith) 2023年6月6日
レイトレースで向きと距離をサンプリングして HRTF を畳み込んでいます。 pic.twitter.com/9dywaubTdG
こちらの動画のメモです。
処理は以下のとおりです。
- メッシュからレイトレース用の BVH を作成
- HRTF(HRIR) データを読み込み
- スピーカーとリスナーを配置
- リスナーからスピーカーまでの経路をサンプリング
- インパルス応答を経路に応じて変更する
- インパルス応答をスピーカーの音に畳み込んで出力
メッシュからレイトレース用の BVH を作成
一般的なレイトレースで使われる BVH と特に違いはありません。
HRTF(HRIR) データを読み込み
こちらのデータを使用させていただきました。
HRTF と書いてありますが、中身は HRIR のようです。
データ形式が *.mat だったので以下のライブラリを使用しました。
GitHub - tbeu/matio: MATLAB MAT File I/O Library
スピーカーとリスナーを配置
動画ではスピーカーの姿勢は固定しています。
リスナーの位置はゲーム内の頭の姿勢を使っています。
リスナーからスピーカーまでの経路をサンプリング
レイを飛ばすとき、2種類の処理を行っています。
- リスナーからスピーカーに向けてレイを飛ばして間に障害物がなければ、その向きに最も近い方向に対応したインパルス応答を HRTF(HRIR) から検索して使う
- HRTF(HRIR) に含まれる全方向についてリスナーからレイを飛ばし、反射を繰り返す。各反射の点からスピーカーに向けてレイを飛ばして間に障害物がなければ最初の方向に対応するインパルス応答を使う
2. については以下のようになっています。
- 経路の最後は NEE (Next Event Estimation) を使う
- 空気中での拡散を再現するため、指数分布を使って求めた距離を最大距離としてレイトレースする。最大距離までの間にレイヒットが見つからなかった場合は最大距離の位置で拡散反射するものとする
- 障害物に当たった場合は法線方向から半球上のランダムに拡散反射
- 空気中で拡散した場合はその点から球上のランダムに拡散反射
- 動画では 5 回で打ち切り
空気中のフォグの濃度が一定であるとして、次にフォグの粒子に当たる距離をランダムにサンプリングする場合、指数分布を使えます。具体的には C++ の std::exponential_distribution を使っています。
exponential_distribution - cpprefjp C++日本語リファレンス
インパルス応答を経路に応じて変更する
上で取得したインパルス応答をもとに経路の長さと音速から求めた時間差分だけずらして、経路長による位相差を再現します。
また、距離に応じて振幅を減衰させます。エネルギーは振幅の二乗だと思うので振幅を距離で割っています。
このときに使う「距離」は、スピーカーまでの最後の距離だけを使っています。他の距離はランダムサンプリングの時点で考慮されていると判断しました。
インパルス応答をスピーカーの音に畳み込んで出力
上で変更したインパルス応答を平均化して、スピーカーの音に畳み込みます。
畳み込みには FFT / 逆 FFT と Overlap Save 法を使いました。