コンピュータアニメーションにおける正運動学(Forward Kinematics)と時間ワーピング(Time Warping)
¶ はじめに
本記事では、コンピュータアニメーションにおける正運動学(Forward Kinematics)と時間ワーピング(Time Warping)の原理と実装を紹介します。

正運動学はロボティクス、コンピュータゲーム、コンピュータアニメーションなどで広く使われています。概念的には、骨格(skeleton)の各関節(joint)に対して相対的な平行移動と回転を与え、全関節を再帰的に辿ることで全体の運動を計算します。
時間ワーピングは、コンピュータアニメーションにおけるキーフレーム(keyframe)に基づく手法です。あるフレームを特定の時刻に描画したい場合、キーフレームの前後の再生速度を調整する必要があり、その結果としてモーションに対して加速・減速を適用します。
¶ 原理と実装
¶ 骨格(Skeleton)
コンピュータアニメーションで人の動きを表現するために、骨格(skeleton)を用いてキャラクターの挙動を駆動します。本記事では Acclaim の ASF 形式の骨格を使用します。ASF には各ボーンのインデックス、長さ、方向、ニュートラルポーズ(neutral pose)の角度、自由度(DoF)が定義されています。
以下は ASF の例です:
:root
order TX TY TZ RX RY RZ
axis XYZ
position 0 0 0
orientation 0 0 0
:bonedata
begin
id 1
name lhipjoint
direction 0.635348 -0.713158 0.296208
length 2.47672
axis 0 0 0 XYZ
end
// 以下省略
骨格を読み込むと、次のようになります:

ニュートラルポーズから実際の動きを表現するには、Acclaim の AMC モーションファイルが必要です。AMC には各ボーンの運動情報が記述されています。ルートノードはワールド座標に対する変化量を持ちますが、それ以外のボーンは親(ルート)に対する相対変換だけを持ちます。1 つの AMC ファイルには、あるモーション全体に含まれる全フレームについて、各ボーンの変化量が含まれます。
以下は AMC の例です。先頭にフレーム番号があり、その後にそのフレームにおける各ボーンの情報が続きます:
2
root -0.303728 17.5624 -27.7253 2.02549 1.77071 -4.33872
lowerback 16.0608 -0.380636 1.35189
upperback 1.68665 -0.267024 -0.0539964
thorax -7.21419 -0.169571 -0.765959
lowerneck -2.88855 -0.493739 -1.55908
upperneck -9.88628 -0.567977 1.15901
head -2.623 -0.258251 0.642519
rclavicle -7.65321e-015 -2.38542e-015
rhumerus -42.619 18.2084 -90.2387
// ... 以下省略
モーションファイルを読み込むと、次のようになります:

より詳しくは、CMU Graphics Lab Motion Capture Database を参照してください。
¶ 正運動学(Forward Kinematics)
正運動学は、変換の伝播チェーンとして捉えることができます。まず最初の関節の変換を計算し、その変換を使って次の関節の変換を計算する、という具合に順に伝播させていきます。
¶ 概念
概念は次の図で説明できます(source: alanzucconi.com):
ステップ 1:

P0 は骨格の root です。P1 と P2 は骨格上の関節です。各関節は 3 つの自由度を持ち、それぞれに局所(ローカル)変換があります。
ステップ 2:

P0 の変換が P1 と P2 に与える影響を計算します。
ステップ 3:

P1 の変換が P2 に与える影響を計算します。この時点で、P2 の変換は P0 と P1 の変換を合成したものになります。
以上の手順により、正運動学で骨格上の全関節の最終的な変換を導出できます。
以下は、人型骨格(ASF)と走行モーション(AMC)に対して正運動学を適用した結果です:

¶ 数式
骨格全体の変換は、(1) ワールド座標に対する骨格の変換と、(2) 骨格内の各関節の相対変換、の 2 つから成ります。まず相対変換の合成結果を計算します。ワールド座標に対する変換は各ノードで共通なので、最後に各関節へワールド変換を加えればよいです。
ここでは、相対変換の扱い方を考えます。
骨格が $\mathbf P_0 \to \mathbf P_1 \to \mathbf P_2$ で与えられるとします。$\mathbf P_i$ は関節位置ベクトル、$\mathbf D_1 = P_0 \to P_1$ は第 1 リンクのベクトルです。
このとき、$\mathbf P_0$ と $\mathbf P_1$ の関係は次の通りです:
$$ \mathbf P_1 = \mathbf P_0 + \mathbf D_1 $$
ここで $\mathbf D_1$ は第 1 リンクの変位ベクトルです。
ただし、$P_0$ の回転も加味する必要があります。$\mathbf P_0$ を基準点(pivot)として、長さ $\mathbf D_1$ を $\alpha_0$ 度回転させると:
$$ \mathbf P_1 = \mathbf P_0 + rotate(\mathbf D_1, \mathbf P_0, \alpha_0) $$
同様に、$\mathbf P_i$ の一般形は次のように書けます:
$$ \mathbf P_i = \mathbf P_{i-1} + rotate(\mathbf D_i, \mathbf P_{i-1}, \sum_{k=0}^{k-1}{\alpha_k}) $$
¶ 時間ワーピング(Time Warping)
時間ワーピングを使うと、ある区間のアニメーションをキーフレームに基づいて調整できます。たとえばパンチ動作で、元々フレーム 150 にあった動作をフレーム 160 まで遅らせたい、あるいはフレーム 140 に前倒ししたい、といったケースです。

この効果を実現するには、各フレームの変化量を調整する必要があります。ここでは、元のアニメーションでフレーム 150 に起きていた動作を、新しいアニメーションではフレーム 160 に遅らせるケースを考えます。骨格アニメーションのタイミングをワープする具体的な手順は次の通りです:
-
新しいフレームが、元のアニメーションのどのフレーム位置に対応するかを計算します。この例では、新しいアニメーションの 0〜160 が元の 0〜150 に対応するので、新しい 0〜160 の各フレームは、元のアニメーションの $150/160$ フレームごとに対応します。
対応は次のようになります:
新 0 1 2 .... 160 旧 0 0.94 1.92 .... 150 -
元のアニメーションのフレームから、新しいアニメーションのフレームを補間で生成します。たとえば新しいアニメーションの 2 フレーム目は、元の 1.92 フレーム目に対応するので、元の 1 フレーム目と 2 フレーム目の間を補間して 1.92 フレーム目を求めます。
-
平行移動(translation)は線形補間でよいですが、回転(rotation)はクォータニオン(Quaternion)へ変換してから SLERP で補間するほうが正確です。(もちろん回転も線形補間できますが、かなり不正確になります。)
以下は、正運動学を実装し、さらに時間ワーピングを適用した結果です:

黄色が元のフレーム、青が時間ワーピング後に調整されたフレームです。
補足として、数学ライブラリ周りの変換は複雑で、実装では単位変換(degree と radian の相互変換など)でミスしやすいです。また、Eigen(C++)でのクォータニオン変換もかなり時間を使って調べました。最終的に SLERP は次のように書けました:
auto rot1 = ComputeRotMatXyz(ToRadian(angular_vector1);
auto rot2 = ComputeRotMatXyz(ToRadian(angular_vector2));
auto q1 = Quaternion_t(rot1);
auto q2 = Quaternion_t(rot2);
auto new_q = Slerp(q1, q2, ratio);
auto new_angular_vec = ToDegree(ComputeEulerAngleXyz(ComputeRotMat(new_q)));
¶ まとめ
正運動学により、各関節の相対変換から骨格全体の運動を計算できます。時間ワーピングにより、キーフレームに基づいたタイミング調整を実現し、アニメーションの再生結果を変化させられます。どちらもコンピュータアニメーションにおいて基本かつ重要な技術です。