生きてます。こちらは「Unreal Engine (UE) Advent Calendar 2024」8日目の記事です。✌︎('ω'✌︎ )
なんとアニメーション関連の記事は4年振りです。ごめんって。
Dead Blending とは
UE5.3 から実験的機能として実装された慣性ブレンドの一種です。これまでは Inertialization ノードのみでしたが、それと同じように使えるノードとして Dead Blending ノードが実装されました。慣性ブレンド利用時の選択肢が増えたと捉えて下さい。そもそも慣性ブレンドとは何なのかという人は公式ドキュメントか以前投稿した記事を先に読んでおくと良いかもしれません。
さて Dead Blending とは具体的にはどういった機能なのでしょうか。公式ドキュメントには以下の記述があります。
Inertialization ノードは入力アニメーションと出力アニメーション間のオフセットを記録し、ポーズがポッピングしないようにフェードアウトさせます。
Dead Blending ノードは出力アニメーションの将来のポーズを予測し、それらを入力アニメーションとブレンドします。
これだけだとよくわかりませんが、なんとなく Dead Blending の方が良さそうに見えます。Dead Blending 作者による解説ページにも良さげな事が書かれています。
In general I've found it creates better looking blends than the standard Inertialization node, in particular when you have transitions between poses that are very different(一般的に、特にポーズ間の違いが大きい場合に、標準的な Inertialzation ノードよりも見栄えの良いブレンドを作成できることがわかりました)
果たしてこれによって Inertialization ノードは過去のものとなり、今後は Dead Blending ノードを使っていけば良いのでしょうか。その辺りについて見ていこうと思います。
Dead Blending は Inertialization の上位互換?
忙しい人のために結論から先に書きます。
Dead Blending ノードは Inertialization ノードを完全に置き換えられるものではないが、Inertialization ノードでブレンド中のポーズが暴れる現象により使用を断念した人達を救える可能性があるもの
です。
慣性ブレンドについて記事を書いたり講演で紹介したりしていると「慣性ブレンドを使ってみようと試してみたけど断念したよ」という声が集まってくるのですが、大抵のケースでは遷移間のポーズが大きく異なる場合に暴れを抑えきれなかったというもので、普通にやるとそう結論付けるのも仕方ないよねと思う程度には暴れるのが現実でした。それに対して CEDEC2020 の某講演にてポーズマッチングという名前で紹介しましたが、遷移先モーション中のタイムライン上のポーズの中から遷移先ポーズがより近くなるように現在ポーズとマッチングすることでなるべく暴れが抑えられるように使用するという小技を併用していたりします。慣性ブレンドはハマれば強力なのですが、乗りこなすのはなかなか難しいですね。
具体的にどんな暴れ方をするのかというところですが、例えば次の動画では走り止まりに遷移する時にブレンド開始タイミングによっては膝下がピョコってする時があるのがわかると思います。
これをそのまま Dead Blending に置き換えるだけで暴れが無くなります。ハッピー。
上の二つの動画は同じブレンド時間(0.4秒)なのですが、Inertialization ノードと Dead Blending ノードでは Dead Blending の方がブレンドする際に緩やかになりやすい(というよりは Inertialization ノードがブレンド時間に対して急変化しやすい)傾向にあるため、Dead Blending ノードのプロパティで Blend Time Multiplier を 0.8 にして 0.4 * 0.8 = 0.32 秒のブレンド時間にすると更に印象が良くなります。
この結果を見ると Dead Blending は上位互換なのでは?と思い始めますが、なかなかそう上手くはいきません。次の動画はループで作られている歩きモーションをループではなく単発再生しつつ、モーションの再生が終わったら同じモーションを単発再生するというのを繰り返しているものです。極端な例としてモーションを切り替える時に長めの 1.0 秒でブレンドしています。
モーションの最初と最後のフレームのポーズは一致しているのに、Dead Blending でのブレンド中にぬるっとした感じになって上手く繋がっていませんね。それに対して同じケースでの Inertialization ノードの結果はこちらです。
長めの 1.0 秒でブレンドしているのに、ブレンドしているようには見えないくらい綺麗に繋がっていますね。これらから結論付けると「多くのケースでは Dead Blending が良い結果になるが、遷移間のポーズ差分がとても小さい時は Inertialization の方が適している」ということになります。
そもそもブレンドの話をしているのに遷移間のポーズ差分が小さいケースは考慮すべきなのかという疑問が出た人もいると思います。例えば A → B と繋がる事がわかっているモーションを作る時は A の最後のポーズと B の最初のポーズを一致させて作るのが自然だと思いますが、丁寧に作れば作るほど Dead Blending ではブレンド時の引っ掛かりが見えてきます。これはブレンド時間を短くすることで軽減可能ですが、どれだけ短くしても完全に一致したポーズ間では引っ掛かりが生じます。そのため「流石にブレンド無しという事にはできないが、大抵は一致しているので綺麗にブレンドしてほしい」というケースにおいては Inertialization を使うというのがベターな選択になります。
Inertialization はざっくり言えば遷移間のポーズ差分を遷移先モーションに対して加算してブレンドアウトしていくという手法なので、遷移間のポーズ差分がゼロに近いならブレンド時間を長くしても遷移先モーションがそのまま綺麗に流れてくれるという強みがあります。急変化を避けるために長めのブレンド時間をデフォルト設定にしておけば、遷移間のポーズ差分が大きい時はブレンドがなだらかにしてくれて、差分が小さい時は同じ設定でもブレンドによる違和感が出ないというところでシチュエーションによる細かいチューニングが不要になるのが良いですね。
ただし前述の通り Inertialization が Dead Blending より有効なのは限られた条件下のみとなるため、やりたい事との相性を考えてどちらを使うのかを選定するのが良いと思います。モーションの遷移によってどちらを使うのか選べるのが良いのですが、標準機能の上では両立ができないのが難しいところです。ちなみに改造ができるプログラマがいるのであれば、ポーズキャッシュの型とフレーム数が一致しているのでブレンド開始時の Transition 生成のところと Evaluate 時にそれを加算するところを分岐するようにマージすれば場面に応じてブレンド手法を切り替えるということもできるので使いやすくなります。
Dead Blending の負荷やリソース面での制約
先に言っておきますが、ここでは絶対評価として処理に 〇ms かかるみたいな話はしません。シングルスレッドでの性能評価は環境によって変化しますし、ブレンドの負荷は Task Graph スレッドで計上されるのが一般的なので、マルチスレッド前提であればボトルネックになりにくいということもあります。なのでここでは Dead Blending と Inertialization での相対評価で考えます。ちなみに UE 5.5.0 時点での話です。
Dead Blending と Inertialization をCPU負荷とメモリ使用量という面で比較した場合、CPU負荷にボトルネックになりえる程の決定的な差はありませんが、メモリ使用量については明確な差があるので注意が必要です。どちらも同じ慣性ブレンドなのでブレンドを開始したタイミングで2フレーム分のポーズキャッシュから Transition を作成し、遷移先ポーズの上から加算していくというロジックなのですが、その中で違いが出るのは Transition で利用するプロパティの構造と加算時の計算式です。それ以外の部分はソースコードを読んだらわかりますが最早コピペだろうというくらい似ています。
CPU負荷に影響するのは計算式ですが、Inertialization がブレンド開始時に決めた変数と方程式から得られた定数を使って計算式に流すだけのシンプルなものに対し、Dead Blending は出力アニメーションの将来のポーズを予測するところで Inertialization と比較するとやや複雑な計算が必要になっています。ただし処理的にはベクトル計算のところで最適化が効きやすそうな印象ではあるのと、前述したようにマルチスレッド前提であればボトルネックになりにくい部分なので、これを理由に Dead Blending を不採用にするという判断はすべきではないかなと個人的には思ってます。
次にメモリ使用量についてですが、ポーズキャッシュについては同サイズであるものの、Transition で使用するボーン数に比例して増えるプロパティサイズは以下の通りとなっています。
Inertialization
TArray<int32> BoneIndices; TArray<FVector3f> BoneTranslationDiffDirection; TArray<float> BoneTranslationDiffMagnitude; TArray<float> BoneTranslationDiffSpeed; TArray<FVector3f> BoneRotationDiffAxis; TArray<float> BoneRotationDiffAngle; TArray<float> BoneRotationDiffSpeed; TArray<FVector3f> BoneScaleDiffAxis; TArray<float> BoneScaleDiffMagnitude; TArray<float> BoneScaleDiffSpeed;
4 + 12 + 4 + 4 + 12 + 4 + 4 + 12 + 4 + 4 = 64 バイト
Dead Blending
TArray<int32> BoneIndices; TArray<FVector> BoneTranslations; TArray<FQuat> BoneRotations; TArray<FQuat4f> BoneRotationDirections; TArray<FVector> BoneScales; TArray<FVector3f> BoneTranslationVelocities; TArray<FVector3f> BoneRotationVelocities; TArray<FVector3f> BoneScaleVelocities; TArray<FVector3f> BoneTranslationDecayHalfLives; TArray<FVector3f> BoneRotationDecayHalfLives; TArray<FVector3f> BoneScaleDecayHalfLives;
4 + 24 + 32 + 16 + 24 + 12 + 12 + 12 + 12 + 12 + 12 = 172 バイト
つまり Dead Blending は Inertialization よりもブレンド中に約 2.7 倍程のメモリを消費するということですね。これはブレンド対象とするボーン数が増えるほど増えていくメモリになるので、ボーン数と同時実行されるブレンド数が増えれば増えるほど両者の差は広がっていきます。これは昨今のゲーム開発ではメモリ使用量が多くなりやすいという傾向も踏まえるとなるべく抑えたい部分ではあります。
ただし逆に言えばブレンド対象とするボーン数が少なければ差は小さいという見方もできます。例えば物理骨や補助骨はブレンド対象外にしても良いケースがあると思いますが、その場合はどちらのノードでも Filtered Bones というプロパティでブレンド対象外とするボーンを指定できるようになっています。これを使ってブレンド対象のボーンを絞ることでメモリ使用量を減らしていくことが可能です。デモ動画の通り Dead Blending は強力な武器になりえるので、細かくチューニングして使用していく価値はあると思います。また、ブレンドが終われば各プロパティはしっかり Empty() されるので確保した分のメモリは解放されます。そのため同時にブレンドを行うアクター数が減れば全体でのピークは抑えられると思います。
またこれはどちらのノードにも言えることなのですが、そもそも常に 2 フレーム分のポーズキャッシュを持っているのが大きいので、遠くにいて最適化等でTickを止めているアクターなどはキャッシュをクリアしてしまうのがオススメです。ブレンド中のメモリ使用量は分散できますが、常駐するものはスポーン数に比例して増えるのでボトルネックになりやすいです。デフォルト機能ではキャッシュクリア方法が無いと思うので近くのプログラマに相談してみて下さい。あと UE5 から FVector や FQuat が倍精度浮動小数点数(8 バイト)になってしまいましたが、そもそもワールド上の座標ならまだしもボーンに流す値に倍精度が必要になることなんて無いと思うので、FVector3f に変更して単精度(4 バイト)に戻す、もしくはもっと気合を入れて半精度(2 バイト)に対応するのもオススメです。ブレンド中の加算アニメーション生成のために差分から Velocity 計算するためのキャッシュなので、ぶっちゃけそんなに精度は要らないです。
まとめ
- Dead Blending は Inertialization の使用を断念した人を救える可能性がある
- Dead Blending は銀の弾丸ではないので特性を理解して Inertialization と使い分ける必要がある
- 特にメモリ使用量の肥大化は注意
Dead Blending というのかい?贅沢な名だね。今からお前の名はピョコってしない慣性ブレンドだよ
明日は僕もよく知ってる シェーダーの魔術師 nさん です。震えて待て。