ほげたつブログ

UE4とアニメーションをかじって生きてる

慣性ベースドなアニメーションブレンド(解説編)

GDC2018 にて Microsoft から「Inertialization: High-Performance Animation Transitions in 'Gears of War'」と題した講演が行われました。実用的で良い講演だったので解説していこうと思います。


www.gdcvault.com


講演概要

  • 従来のクロスフェードによるアニメーションブレンドでは、ブレンド中に2つのアニメーションを評価する必要があり、ゲームの状態により瞬間的な処理コストが跳ね上がるという問題を抱えている。
  • Gears of War ではアニメーションブレンド時に2つのアニメーションを評価する事を辞め、アニメーション遷移後のポスト処理としてブレンドを行った。
  • ブレンド開始時点の関節速度を維持することで、遷移前アニメーションの評価を止めても自然なブレンドを実現できる。


従来のクロスフェード

f:id:hogetatu:20180526130732p:plain

Gears of War でのポスト処理ブレンド

f:id:hogetatu:20180526132006p:plain


試してみた感想

解説に入る前に、手元で試してみた感想を書いておきます。


メリット

  • 複数のアニメーションを同一フレームに評価する必要が無いので、比較的ボトルネックになりにくい。
  • 最終的なポーズを基準にブレンドするため、アニメーションレイヤーが複雑な構成になっていても絵が破綻しにくい。
  • 加速度をベースに遷移後アニメに対して加算する Transform を決定するため、重ねがけのような効果が見られる。(2つ目のツイート)
  • 遷移前アニメーションがブレンド中にイベント通知を行わない。(メリットでありデメリット)

デメリット

  • 全身2ポーズ分の関節Transformをキャッシュするので、比較的メモリ使用量が増える。
  • 遷移前アニメーションがブレンド中にイベント通知を行わない。(メリットでありデメリット)


イベント通知については通知の用途によって印象が分かれると思います。個人的にはブレンド中のイベント通知についてはトラブルが多い印象なので、通知処理の方で排他制御するよりも通知が呼ばれない方がシンプルで良いという印象です。


補間式

スライドには必要な式が揃っているので、機能を実装するという目的の上では全てを把握する必要は無いのですが、ここでは自分用のメモとして式の導出についても書いておきます。

時間 t におけるポーズ差分 x

アニメーションブレンドとは遷移前アニメのポーズから遷移後アニメのポーズに徐々に変化していくことを指します。つまり時間 t におけるポーズ差分 x を  x(t) とした時、 x(t) は時間の経過と共に差分が減少していき、ブレンド時間  t_1 で 0 に収束する事が望まれます。ここで遷移前アニメと遷移後アニメの差分を x_0 、遷移時の速度を v_0 、遷移時の加速度を a_0 とした場合、以下の式になると記載されています。


 x(t) = At^5 + Bt^4 + Ct^3 + \frac{a_0}{2}t^2 + v_0t + x_0

 A = -\frac{a_0t_1^2 + 6v_0t_1 + 12x_0}{2t_1^5}

 B = \frac{3a_0t_1^2 + 16v_0t_1 + 30x_0}{2t_1^4}

 C = -\frac{3a_0t_1^2 + 12v_0t_1 + 20x_0}{2t_1^3}


確認のために、いくつか任意のパラメータを与えてプロットしてみたところ、いずれも 0 に収束していることから妥当性が伺えます。

f:id:hogetatu:20180527043424p:plain

f:id:hogetatu:20180527043617p:plain

f:id:hogetatu:20180527043712p:plain

これは 1985 年に Flash and Hogan が「The Coordination of Arm Movements: An Experimentally Confirmed Mathematical 」と題した論文が元となっており、日本では躍度最小モデルとして知られています。このモデルは人間の運動を解析的に解くことができるもので、完全ではないものの、比較的良く実際の運動と一致します。躍度とは位置を3回微分したもの、つまり加速度の変化率です。躍度最小モデルでは躍度の2乗を開始から終了まで積分し、それを評価関数として最小になるものが最適とされています。


 C_j = \frac{1}{2} \int_{0}^{t_1} (\frac{d^3x(t)}{dt^3})^2 dt


また、 x(t)オイラーポアソン方程式を満たす場合に評価関数  C_j極値を取り、これを解くと以下の式が導かれます。


 \frac{d^6 x(t)}{dt^6} = 0


これにより  x(t) は 5 次式であることがわかるため、距離  x(t) 及び、微分により速度  v(t) 、加速度  a(t) は以下の式で定義できます。


 x(t) = A t^5 + B t^4 + C t^3 + D t^2 + E t + F

 v(t) = 5A t^4 + 4B t^3 + 3C t^2 + 2D t + E

 a(t) = 20A t^3 + 12B t^2 + 6C t + 2D


この時、論文では動きの開始点及び最終点で速度と加速度が 0 になるように式を解いていますが、今回は 最終点での距離、速度、加速度が 0 になるのが目標 であるため、それらを用いて式を解いていきます。


 x_0 = F

 x_{t_1} = A t_1^5 + B t_1^4 + C t_1^3 + D t_1^2 + E t_1 + F = 0

 v_0 = E

 v_{t_1} = 5A t_1^4 + 4B t_1^3 + 3C t_1^2 + 2D t_1 + E = 0

 a_0 = 2D

 a_{t_1} = 20A t_1^3 + 12B t_1^2 + 6C t_1 + 2D = 0


従って、 D = \frac{a_0}{2} E = v_0 F = x_0 なので以下の連立方程式が成り立ちます。


f:id:hogetatu:20180602121452p:plain


これを解くことで  A, B, C が求まります。


 A = -\frac{a_0t_1^2 + 6v_0t_1 + 12x_0}{2t_1^5}

 B = \frac{3a_0t_1^2 + 16v_0t_1 + 30x_0}{2t_1^4}

 C = -\frac{3a_0t_1^2 + 12v_0t_1 + 20x_0}{2t_1^3}


また、 x(t) D, E, F を代入して、


 x(t) = At^5 + Bt^4 + Ct^3 + \frac{a_0}{2}t^2 + v_0t + x_0


これにより、スライドに書かれている  x(t) 及び、 A, B, C が求まりました。


遷移時の加速度 a0 と Overshoot

 x(t) に与える遷移時の加速度  a0 によっては、収束までの過程で 0 を超えてしまうことがあり、これを Overshoot と呼びます。

f:id:hogetatu:20180602123320p:plain

Overshoot させないために、 a0ブレンド時間  t_1 において躍度が 0 になるものを選びます。躍度は加速度の変化率なので、前述の  a(t)微分します。


 j(t) = 60A t^2 + 24B t + 6C

 j_{t_1} = 60A t_1^2 + 24B t_1 + 6C = 0


これを解くことで  a0 が求まります。


 a_0 = \frac{-8v_0t_1 - 20x_0}{t_1^2}


ただし、これだけではまだ不十分で、遷移前アニメーションによっては Overshoot してしまうため、今度は  t_1 を調整します。 a_0 t_1 の値によって変化し、1 つの極値が存在します。以下は  a_0(t_1) をプロットしたものです。

f:id:hogetatu:20180602143929p:plain

極値では変化率が 0 になるので、微分して極値を求めます。


 \frac{da_0}{dt_1} = \frac{8v_0t_1 + 40x_0}{t_1^3} = 0


これを解くと、 a_0極値を取る際の  t_1 が求まります。


 t_1 = - \frac{5x_0}{v_0}


上記で  t_1 を調整した後に再度プロットしてみると、Overshoot しないことが確認できます。

f:id:hogetatu:20180602154500p:plain

また、上記の式はあくまで  t_1 の上限値であるため、これよりも小さい値であれば Overshoot せずに収束します。つまり、実際には以下の式になります。


 t_1 = \min(t_1, - \frac{5x_0}{v_0})


まとめ

■ 遷移後ポーズ

f:id:hogetatu:20180610183752p:plain

■ 現在フレーム -1 ポーズ

f:id:hogetatu:20180610184254p:plain

■ 現在フレーム -2 ポーズ

f:id:hogetatu:20180610184308p:plain

■ 遷移前ポーズと遷移後ポーズの差分

 x_0 = [現在フレーム -1 ポーズ] - [遷移後ポーズ]

■ 遷移時の速度

 v_0 = ( [現在フレーム -1 ポーズ] - [現在フレーム -2 ポーズ] )  / dt

■ 遷移時の加速度

 a_0 = \frac{-8v_0t_1 - 20x_0}{t_1^2}

■ 遷移時間

 t_1 = \min(t_1, - \frac{5x_0}{v_0})

■ 時間 t におけるポーズ差分

 x(t) = At^5 + Bt^4 + Ct^3 + \frac{a_0}{2}t^2 + v_0t + x_0

 A = -\frac{a_0t_1^2 + 6v_0t_1 + 12x_0}{2t_1^5}

 B = \frac{3a_0t_1^2 + 16v_0t_1 + 30x_0}{2t_1^4}

 C = -\frac{3a_0t_1^2 + 12v_0t_1 + 20x_0}{2t_1^3}


以上が慣性ベースドなアニメーションブレンドの内容となります。長くなったので実装編は次回に持ち越します。実装編ではベクトル/クォータニオンにおける補間の解説と、UE4 での簡易的な実装方法までを解説する予定です。