25. キャラクタアニメーションの発展

第13章ではキャラクタを組み立てました。関節のスケルトン、フォワードキネマティクスとインバースキネマティクス、リニアブレンドスキニングで付けたスキン、顔のブレンドシェイプ、そして其れを動かすモーションキャプチャです。
第14章では其れをゲームの中で動かしました。クリップのブレンドとクロスフェード、ステートマシン、ブレンドツリー、ラグドール、群衆です。
此の章では、其れらの箱を開けて中を見ます。

先ずはスキンからです。
第13章では、リニアブレンドスキニングで肘が細くなり、捻った手首が潰れる事を見て、其の対策を約束しました。其れがデュアルクォータニオンスキニングで、此処で導いて計算します。残った問題を片付ける補正シェイプとスムージング系のデフォーマも一緒に扱います。
次にブレンドを深く掘ります。重みを計算したブレンドスペース、加算アニメーションとレイヤー、ルートモーション、そしてクロスフェードより安くて綺麗な遷移である慣性化です。
続いて、最近の多くのゲームで手作りのステートマシンを置き換えたモーションマッチングを、探索の計算例と共に見ます。
其の後は全身のインバースキネマティクスです。FABRIK、計算例付きのヤコビ行列の方法、可動域、凸凹な地面の上の足、何かを見る頭、そして物理で動くラグドールです。
最後に、パフォーマンスキャプチャからソルブする顔、群衆、アニメーションデータの圧縮を扱います。

此の章の内容は全て検定のシラバスの外ですが、分野5の2つのシラバス項目、キャラクタのアニメーションとリアルタイムアニメーションから直接育った物で、25.6節の終わりの検定のポイントで其れらに結び付けます。
数学は線型代数 第13章のクォータニオンと、此のシリーズの第4章・第5章の変換行列です。教え直す代わりにリンクを張ります。

25.1 スキニング行列を最後まで計算する

行列の連鎖

第13章の13.8節のリニアブレンドスキニング(LBS)の式を思い出しましょう:

𝐯′=∑jwj𝐒j𝐯,𝐒j=𝐖j𝐁j−1,∑jwj=1

此処には3つの行列が出て来て、其々別の所から来ます:

  • 𝐁j:バインドポーズ、つまりメッシュをモデリングした姿勢でのボーンのワールド行列。決して変わらないので、エンジンは其の逆行列、逆バインド行列をモデルファイルに保存します(glTFでは配列inverseBindMatricesです)。
  • 𝐖j:現在のポーズでのボーンのワールド行列。アニメーションしたローカル変換から、毎フレームのフォワードキネマティクスで 𝐖j=𝐖parent(j)𝐋j と計算します。
  • 𝐒j:スキニング行列で、2つの積です。「メッシュをモデリングした時のボーンの位置を取り消し、今のボーンの位置を適用する」という意味です。

何故逆バインド行列が要るのでしょうか?
頂点はどのボーンに対してでもなく、モデル空間で保存されています。
𝐖j だけを掛けると、肘の近くの頂点は、原点からの肘のオフセットの分だけもう一度動かされてしまいます。
𝐁j−1 が先ず頂点をボーン自身の座標系で表し、𝐖j が其の座標系を新しい場所へ運びます。
すぐに便利な確認が出来ます。バインドポーズでは 𝐖j=𝐁j なので全ての 𝐒j=𝐈 となり、ウェイトが何であってもメッシュはモデリングした場所に其の儘留まります。

計算例

第13章の2本のボーンの腕を、第4章の同次座標の3 × 3行列を使って2次元で考えます。
ボーン0は原点から、ボーン1は (2,0) から始まるので、𝐁0=𝐈、𝐁1=𝐓(2,0) です。
今回は両方の関節が動きます。肩が30°回り、肘が上腕に対して90°回ります:

𝐖0=𝐑(30°),𝐖1=𝐖0𝐓(2,0)𝐑(90°)

スキニング行列は 𝐒0=𝐑(30°) と 𝐒1=𝐑(30°)𝐓(2,0)𝐑(90°)𝐓(−2,0) です。
𝐒1 は右から左へ読みます。肘を原点へ動かし、90°回し、元へ戻し、最後に腕全体を肩の周りに30°回します。
掛け算すると

𝐓(2,0)𝐑(90°)𝐓(−2,0)=[0−1210−2001],𝐒1=[−0.5−0.8662.7320.866−0.5−0.732001]

肘の外側の頂点 𝐯=(2,−0.5) を、ウェイト0.5と0.5でスキニングします:

  • 𝐒0𝐯=(2cos30°+0.5sin30°,2sin30°−0.5cos30°)=(1.982,0.567)
  • 𝐒1𝐯=(−0.5⋅2+0.866⋅0.5+2.732,0.866⋅2+0.5⋅0.5−0.732)=(2.165,1.250)
  • 混合:0.5(1.982,0.567)+0.5(2.165,1.250)=(2.074,0.909)

肘は今 𝐖0(2,0)=(1.732,1.0) にあり、頂点は其処から 0.3422+0.0922=0.354 の距離です。第13章と同じ0.354への細りです。腕全体を回しても曲げの形は変わらないからです。

何故スキンが縮むのか:混ぜた行列

混合は線型なので、先に行列を混ぜてから1回だけ掛けても同じです:

𝐌=0.5𝐒0+0.5𝐒1=[0.183−0.6831.3660.6830.183−0.366001]

左上の2 × 2部分の行列式は 0.1832+0.6832=0.5 で、此れは 0.707𝐑(75°) です。30°と120°の丁度真ん中の75°の回転に、0.707倍の一様な拡大縮小が掛かっています。
覚えておく価値のある一般的な恒等式です:

12(𝐑(α)+𝐑(β))=cosβ−α2𝐑(α+β2)

2つの回転の平均は正しい向きを指しますが、間の角度の半分のコサイン倍に縮みます。90°なら0.707、180°なら0です。
第13章の13.4節の行列の線型補間が、関節近くのスキニングされた全ての頂点の中に隠れているのです。
従ってLBSが間違うのはウェイトの所為ではありません。剛体変換の重み付き和が剛体変換ではない所為です。

実際には

剛体のスケルトンのスキニング行列は一番下の行が (0,0,0,1) なので、行列パレットは普通3 × 4行列としてアップロードします。1関節あたり16個ではなく12個のfloatです。
法線は混ぜた3 × 3部分を使いますが、此れが正確なのは拡大縮小が無い時だけです。拡大縮小するボーンには第5章の5.4節の逆転置行列が要ります。

25.2 デュアルクォータニオンスキニング

縮みを直すには、剛体の儘でいられる物を混ぜれば良いのです。
回転だけなら答えは既に知っています。単位クォータニオンを混ぜて正規化(nlerp)すれば、結果は必ず回転です(線型代数 第13章の13.6節)。
ですがボーンは原点ではなく自分の関節の周りに回るので、回転と平行移動は一体です。別々に混ぜると、間違った中心の周りに回ってしまいます。
デュアルクォータニオン(双対四元数)は、回転と平行移動を1つの物にまとめ、クォータニオンの様に混ぜて正規化出来る様にします。
カヴァン、コリンズ、ジャーラ、オサリバンが「Skinning with Dual Quaternions」(I3D 2007)でデュアルクォータニオンスキニング(DQS)を導入し、「Geometric Skinning with Approximate Dual Quaternion Blending」(ACM Transactions on Graphics、2008年)で詳しく解析しました。

双対数

双対数は a+εb という形の数で、ε は次の1つの規則だけを持つ新しい記号です:

ε2=0,ε≠0

其れだけです。i2=−1 の複素数と比べてみて下さい。
2つの双対数を掛けると、ε2 の項は単に消えます:

(a+εb)(c+εd)=ac+ε(ad+bc)

例えば (2+3ε)(4+5ε)=8+ε(10+12)=8+22ε です。

デュアルクォータニオンと剛体変換

デュアルクォータニオンは、同じ考え方で部分をクォータニオンにした物です:

q^=qr+εqd

qr が実部、qd が双対部で、其々普通のクォータニオン (w,x,y,z) です。合わせて8個の数です。
積は、線型代数 第13章のハミルトン積で双対数の規則に従います:

q^1q^2=qr1qr2+ε(qr1qd2+qd1qr2)

「単位クォータニオン qr で回してから 𝐭 だけ平行移動する」という剛体変換は、次の単位デュアルクォータニオンです:

q^=qr+ε12tqr,t=(0,𝐭)(純クォータニオン)

別の単位デュアルクォータニオンとの積は、行列の積と全く同じ様に、2つの変換の合成になります。
点 𝐩 に適用するには、実部で回し、双対部から平行移動を読み戻します:

𝐩′=qr𝐩qr∗+𝐭,𝐭=2qdqr∗ のベクトル部

(単位 qr なら 2qdqr∗=tqrqr∗=t だからです。)

混合:デュアルクォータニオン線型ブレンド

DQSは、LBSの混ぜた行列を、混ぜたデュアルクォータニオンに置き換えます。デュアルクォータニオン線型ブレンド(DLB)です:

b^=∑jwjsjq^j,c^=b^‖br‖

  • ウェイトはLBSと同じスキンウェイトです。リグは何も変わりません。
  • sj=±1 で符号を選びます。q と −q は同じ回転なので(二重被覆、線型代数の13.5節)、qrj⋅qr0≥0 となる符号を取ります。こうして頂点の全てのボーンが、slerpの様に「短い方の道」で混ざります。
  • 両方の部分を、混ぜた実部の長さで割ります。すると cr は単位クォータニオン、つまり純粋な回転で、2cdcr∗ のベクトル部が平行移動になります。ウェイトが何であっても、頂点は剛体変換で動かされます。(2cdcr∗ のスカラー部に残る僅かな残りは単に無視します。)

計算例:曲げた肘

第13章と同じ曲げです。ボーン1が肘 𝐜=(2,0,0) の周りにz軸周りで90°回り、ボーン0は其の儘で、頂点 𝐯=(2,−0.5,0) のウェイトは0.5と0.5です。

ボーン1をデュアルクォータニオンにする。 回転は qr1=(cos45°,0,0,sin45°)=(0.7071,0,0,0.7071) です。
点 𝐜 の周りの回転は「原点の周りに回してから 𝐭=𝐜−R𝐜 だけ平行移動する」事です。R(2,0,0)=(0,2,0) なので 𝐭1=(2,−2,0) です。
双対部は 12(0,2,−2,0)(0.7071,0,0,0.7071) です。
2つのクォータニオンの積は (a,𝐀)(b,𝐁)=(ab−𝐀⋅𝐁,a𝐁+b𝐀+𝐀×𝐁) です。此処では 𝐀⋅𝐁=0 で、

0.7071(2,−2,0)+(2,−2,0)×(0,0,0.7071)=(1.4142,−1.4142,0)+(−1.4142,−1.4142,0)=(0,−2.8284,0)

従って qd1=12(0,0,−2.8284,0)=(0,0,−1.4142,0) です。
ボーン0は恒等変換で、qr0=(1,0,0,0)、qd0=0 です。

混合。 qr0⋅qr1=0.7071≥0 なので符号は反転しません。

br=0.5(1,0,0,0)+0.5(0.7071,0,0,0.7071)=(0.8536,0,0,0.3536),bd=(0,0,−0.7071,0)
‖br‖=0.85362+0.35362=0.8536=0.9239
cr=(0.9239,0,0,0.3827)=(cos22.5°,0,0,sin22.5°),cd=(0,0,−0.7654,0)

実部は45°の回転、丁度曲げの半分です。

平行移動。 cr=(w,𝐕)、cd=(e,𝐄) と書くと、2cdcr∗ のベクトル部は 2(w𝐄−e𝐕+𝐕×𝐄) になります。
w=0.9239、𝐄=(0,−0.7654,0)、e=0、𝐕=(0,0,0.3827) なので:

𝐭=2((0,−0.7071,0)+(0.2929,0,0))=(0.5858,−1.4142,0)

そして此れは 𝐜−R(45°)𝐜=(2−1.4142,0−1.4142,0) です。混ぜた変換は肘の周りの45°の回転なのです。

頂点。 R(45°)(2,−0.5)=(1.7678,1.0607) に 𝐭 を足すと (2.3536,−0.3536) で、肘からの距離は 0.35362+0.35362=0.5 です。
LBSでは (2.25,−0.25)、肘から0.354でした。DQSは筒の半径を其の儘保ちます。

左:リニアブレンドスキニングで90°曲げた筒。外側の頂点は2.25, -0.25で関節から0.354。右:同じ曲げをデュアルクォータニオンスキニングで。頂点は2.354, -0.354で関節から0.5。関節の少し手前で真っ直ぐな表面から0.044の小さな膨らみがある。点線の円は関節の周りの半径0.5

計算例:捻りと反転

次は第13章のキャンディラッパーです。ボーン1がx軸(ボーン自身の軸)周りに φ 捻れ、関節の輪の上の頂点 (2,0,0.5) をウェイト0.5と0.5で追います。
捻りの軸は原点を通るので平行移動は無く、qr1=(cosφ2,sinφ2,0,0) です。

  • φ=90°:br=(0.8536,0.3536,0,0)、正規化して (0.9239,0.3827,0,0)、45°の捻りです。頂点は (2,−0.3536,0.3536) に行き、半径0.5です。LBSは半径 0.5cos45°=0.354。
  • φ=170°:qr1=(0.0872,0.9962,0,0) で、qr0 との内積は正です。混合は85°の捻りで半径0.5。LBSは 0.5cos85°=0.044 で、殆ど潰れています。
  • φ=190°:qr1=(−0.0872,0.9962,0,0) で、今度は内積が負なので、−qr1=(0.0872,−0.9962,0,0) を使います。同じ回転を −170° の捻りとして表した物です。混合は −85° の捻りです。

半径は常に0.5で、キャンディラッパーは起きません。
ですが170°と190°の間で、関節の輪は +85° から −85° へ跳びます。ボーンの滑らかな20°の変化に対して、突然170°回るのです。
此れがカヴァンらの述べる反転アーティファクトです。DQSは常に短い方の道で混ぜ、180°で短い方の道が反対側に切り替わります(丁度180°では内積が0で、丸め誤差が決めます)。
LBSにも独自の版があり、輪の半径が180°で0を通ります。
実際のリグでは、回転を分担するツイストボーンを使って、元々1つの関節にそんな大きな捻りを掛けない様にします。

左:ボーン1の捻りに対する関節の輪の半径。DQSは0.5の儘、LBSは捻りの半分のコサインの絶対値の0.5倍で、90°で0.354、190°で0.044。右:DQSでの輪の捻り。ボーンの捻りの半分で、180°で85°からマイナス85°へ跳ぶ

膨らみ

曲げた筒では、関節の少し手前の頂点はボーン1に小さなウェイトを持ち、肘の周りに小さな角度だけ回されるので、外側へ振り出されます。
第13章のウェイト(x=1.5 で0から x=2.5 で1へ変わる)では、外側の表面は y=−0.544 まで出て、y=−0.5 にある上腕の表面より0.044外に出ます。半径の約9 %の膨らみで、LBSなら内側に潰れる所です。
普通は其の儘受け入れるか、補正シェイプ(25.3節)で直します。DCCツールの中には、LBSとDQSを頂点ごとに混ぜられる物もあります(例えばMayaのスキンクラスタには「Classic Linear」、「Dual Quaternion」、両者をウェイトで混ぜる方法があります)。

コード

DLBと2つの計算例をC++で書きます。線型代数シリーズのlinalg.hhのQuat、rotate、conjugateを使います:

#include <cmath>
#include <cstdio>
#include <initializer_list>
#include "linalg.hh"

// Unit dual quaternion: real = rotation, dual = 0.5 * translation * rotation.
struct DualQuat {
  Quat real, dual;
};

Quat scaled(Quat q, float s) { return {s * q.w, s * q.x, s * q.y, s * q.z}; }
Quat added(Quat a, Quat b) { return {a.w + b.w, a.x + b.x, a.y + b.y, a.z + b.z}; }
float dot(Quat a, Quat b) { return a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z; }

// Rigid transform "rotate by r, then translate by t" as a dual quaternion.
DualQuat fromRigid(Quat r, Vec3 t) {
  return {r, scaled(Quat{0.0f, t.x, t.y, t.z} * r, 0.5f)};
}

// Apply a unit dual quaternion to a point: p' = r p r* + t, t = vector part of 2 * dual * real*.
Vec3 transformPoint(const DualQuat &q, Vec3 p) {
  Quat t = q.dual * conjugate(q.real);
  return rotate(q.real, p) + Vec3{2.0f * t.x, 2.0f * t.y, 2.0f * t.z};
}

// Dual quaternion linear blending (DLB): weighted sum on the short side of bone 0, then normalize.
DualQuat blend(const DualQuat *dq, const float *w, int n) {
  DualQuat b{{0, 0, 0, 0}, {0, 0, 0, 0}};
  for (int i = 0; i < n; ++i) {
    float s = dot(dq[i].real, dq[0].real) < 0.0f ? -w[i] : w[i]; // q and -q are the same rotation
    b.real = added(b.real, scaled(dq[i].real, s));
    b.dual = added(b.dual, scaled(dq[i].dual, s));
  }
  float len = std::sqrt(dot(b.real, b.real));
  return {scaled(b.real, 1.0f / len), scaled(b.dual, 1.0f / len)};
}

// Rotation by angle about axis through the point c, as rotation + translation.
DualQuat rotationAbout(Vec3 axis, float angle, Vec3 c) {
  Quat r = Quat::fromAxisAngle(axis, angle);
  return fromRigid(r, c - rotate(r, c));
}

int main() {
  const float deg = 3.14159265f / 180.0f;
  const float w[2] = {0.5f, 0.5f};
  Vec3 elbow{2, 0, 0};

  // Bend: bone 1 turns 90 degrees about z at the elbow; vertex on the outside of the elbow.
  DualQuat bend[2] = {fromRigid(Quat{}, {0, 0, 0}), rotationAbout({0, 0, 1}, 90 * deg, elbow)};
  Vec3 v{2, -0.5f, 0};
  Vec3 lbs = 0.5f * v + 0.5f * transformPoint(bend[1], v);
  Vec3 dqs = transformPoint(blend(bend, w, 2), v);
  printf("bend  LBS (%.4f, %.4f) r = %.4f\n", lbs.x, lbs.y, distance(lbs, elbow));
  printf("bend  DQS (%.4f, %.4f) r = %.4f\n", dqs.x, dqs.y, distance(dqs, elbow));

  // Twist: bone 1 twists about x by 90, 170 and 190 degrees; vertex on the ring at the joint.
  Vec3 u{2, 0, 0.5f};
  for (float phi : {90.0f, 170.0f, 190.0f}) {
    DualQuat twist[2] = {fromRigid(Quat{}, {0, 0, 0}), rotationAbout({1, 0, 0}, phi * deg, elbow)};
    Vec3 l = 0.5f * u + 0.5f * transformPoint(twist[1], u);
    DualQuat b = blend(twist, w, 2);
    Vec3 d = transformPoint(b, u);
    float ringTwist = 2.0f * std::atan2(b.real.x, b.real.w) / deg;
    printf("twist %3.0f: LBS r = %.4f, DQS r = %.4f, DQS ring twist %.1f deg\n", phi,
           std::hypot(l.y, l.z), std::hypot(d.y, d.z), ringTwist);
  }
  return 0;
}

bend  LBS (2.2500, -0.2500) r = 0.3536
bend  DQS (2.3536, -0.3536) r = 0.5000
twist  90: LBS r = 0.3536, DQS r = 0.5000, DQS ring twist 45.0 deg
twist 170: LBS r = 0.0436, DQS r = 0.5000, DQS ring twist 85.0 deg
twist 190: LBS r = 0.0436, DQS r = 0.5000, DQS ring twist -85.0 deg

GPUの上で

エンジンは関節ごとに3 × 4行列の代わりにfloat4を2つアップロードします。12個ではなく8個のfloatです。
頂点シェーダーでは、単位クォータニオン (𝐮,w) による点の回転を、行列を作らずに 𝐩+2𝐮×(𝐮×𝐩+w𝐩) と書きます。
此処ではクォータニオンを (x,y,z,w) の順、つまり w を4番目の成分に入れて保存します。glTFも同じ順です:

struct DualQuatPalette {
  float4 real[64]; // rotation (x, y, z, w) of every joint
  float4 dual[64]; // dual part (x, y, z, w) of every joint
};
ConstantBuffer<DualQuatPalette> dqPalette;

// Dual quaternion skinning of one position with four joint indices and weights (summing to 1).
float3 skinPositionDQ(float3 position, uint4 joints, float4 weights) {
  float4 r0 = dqPalette.real[joints.x];
  float4 br = float4(0.0, 0.0, 0.0, 0.0);
  float4 bd = float4(0.0, 0.0, 0.0, 0.0);
  for (int k = 0; k < 4; ++k) {
    float4 r = dqPalette.real[joints[k]];
    float w = dot(r, r0) < 0.0 ? -weights[k] : weights[k]; // blend the short way round
    br += w * r;
    bd += w * dqPalette.dual[joints[k]];
  }
  float len = length(br);
  br /= len;
  bd /= len;
  float3 p = position + 2.0 * cross(br.xyz, cross(br.xyz, position) + br.w * position);
  float3 t = 2.0 * (br.w * bd.xyz - bd.w * br.xyz + cross(br.xyz, bd.xyz));
  return p + t;
}
layout(set = 0, binding = 1) uniform DualQuatPalette {
  vec4 dqReal[64]; // rotation (x, y, z, w) of every joint
  vec4 dqDual[64]; // dual part (x, y, z, w) of every joint
};

// Dual quaternion skinning of one position with four joint indices and weights (summing to 1).
vec3 skinPositionDQ(vec3 position, uvec4 joints, vec4 weights) {
  vec4 r0 = dqReal[joints.x];
  vec4 br = vec4(0.0);
  vec4 bd = vec4(0.0);
  for (int k = 0; k < 4; ++k) {
    vec4 r = dqReal[joints[k]];
    float w = dot(r, r0) < 0.0 ? -weights[k] : weights[k]; // blend the short way round
    br += w * r;
    bd += w * dqDual[joints[k]];
  }
  float len = length(br);
  br /= len;
  bd /= len;
  vec3 p = position + 2.0 * cross(br.xyz, cross(br.xyz, position) + br.w * position);
  vec3 t = 2.0 * (br.w * bd.xyz - bd.w * br.xyz + cross(br.xyz, bd.xyz));
  return p + t;
}
struct DualQuatPalette {
  float4 real[64]; // rotation (x, y, z, w) of every joint
  float4 dual[64]; // dual part (x, y, z, w) of every joint
};
ConstantBuffer<DualQuatPalette> dqPalette;

// Dual quaternion skinning of one position with four joint indices and weights (summing to 1).
float3 skinPositionDQ(float3 position, uint4 joints, float4 weights) {
  float4 r0 = dqPalette.real[joints.x];
  float4 br = float4(0.0, 0.0, 0.0, 0.0);
  float4 bd = float4(0.0, 0.0, 0.0, 0.0);
  for (int k = 0; k < 4; ++k) {
    float4 r = dqPalette.real[joints[k]];
    float w = dot(r, r0) < 0.0 ? -weights[k] : weights[k]; // blend the short way round
    br += w * r;
    bd += w * dqPalette.dual[joints[k]];
  }
  float len = length(br);
  br /= len;
  bd /= len;
  float3 p = position + 2.0 * cross(br.xyz, cross(br.xyz, position) + br.w * position);
  float3 t = 2.0 * (br.w * bd.xyz - bd.w * br.xyz + cross(br.xyz, bd.xyz));
  return p + t;
}

法線は混ぜた実部だけで回します。
カヴァンらは、DQSのコストはLBSよりシェーダー命令が少し増えるだけだと報告しています。
限界は名前の通りです。デュアルクォータニオンが表せるのは回転と平行移動だけなので、拡大縮小するボーン(漫画的な潰れ、膨らむ筋肉)には追加の工夫が要ります。例えば拡大縮小だけを別の段階で先に適用してから、デュアルクォータニオンで混ぜます。

25.3 補正シェイプ・ポーズスペースデフォメーション・デルタマッシュ

スキニングの式は解剖学を知りません(肘の内側の皺、盛り上がる肩、膝の皿)。其の為、映画や高品質なゲームのリグはスキニングの上にシェイプを加えます。

ポーズで駆動する補正ブレンドシェイプ

補正ブレンドシェイプは、ウェイトを手でアニメーションするのではなく、ポーズ、普通は関節の角度で駆動するブレンドシェイプ(第13章の13.5節)です:

w(θ)=clamp(θ−θ0θ1−θ0,0,1)

差分はスキニングの前にバインドポーズで足すので、スキニングが其れをボーンと一緒に運びます:

𝐯′=∑jwj𝐒j(𝐯+∑kwk(𝜽)𝐝k)

スキニングの後で足すと、腕を横に伸ばした状態でモデリングした差分は、腕が動いた途端に間違った向きを指してしまいます。

計算例。 25.2節の90°の曲げで、LBSは外側の頂点を (2.25,−0.25) に置きましたが、アーティストは其れを丸い肘の上の (2.3536,−0.3536)(DQSが置いた場所)に置きたいとします。
此の頂点に対してLBSは線型部分が 𝐀=[0.5−0.50.50.5] の混ぜた行列を掛けるので、バインド空間の差分 𝐝 は結果を 𝐀𝐝 だけ動かします。
必要なのは 𝐀𝐝=(2.3536−2.25,−0.3536+0.25)=(0.1036,−0.1036) で、

𝐝=𝐀−1(0.1036,−0.1036)=[11−11][0.1036−0.1036]=(0,−0.2071)

バインドポーズでは補正シェイプが頂点を更に0.2071外側(下、ボーンから離れる方向)へ押し、縮む混合が其れを正しい半径へ戻します。
ドライバーを θ0=30°、θ1=90° にすると、シェイプは30°未満では切れ、60°でウェイト0.5、90°で全開です。
実際の制作では差分は解くのではなく彫ります。アーティストが腕を90°に曲げ、見た目が正しくなるまで肘を彫り、ツールが頂点ごとに丁度此の計算でバインド空間の差分を求めます(「反転した」又は「スキニング前の」補正シェイプ)。

ポーズスペースデフォメーション

角度のドライバーは蝶番の関節には使えますが、肩の補正は3つの回転軸に同時に依存します。
ポーズスペースデフォメーション(PSD。ルイス、コードナー、フォン、SIGGRAPH 2000)は、補正をポーズの空間での散在データ補間として扱います。アーティストは幾つかの例のポーズで補正を彫り、其の間の補正は放射基底関数(RBF)で補間します。
RBFの補間関数は、例の位置を中心とする山の和です:

f(𝜽)=∑iaiϕ(‖𝜽−𝜽i‖),ϕ(r)=e−(r/σ)2

係数 ai は、f が例の値を丁度通る様に選びます。例1つにつき1本の線型方程式です。

角度1つ、例2つの計算例です。基本姿勢 θ=0°(補正ウェイト0)と彫ったポーズ θ=90°(ウェイト1)で、σ=60° とします。
2つの例の間の山の高さは ϕ(90°)=e−2.25=0.1054 なので

[10.10540.10541][a0a1]=[01]⇒a1=11−0.10542=1.0112,a0=−0.1054a1=−0.1066

45°では両方の山が e−0.5625=0.5698 で、f=0.5698(−0.1066+1.0112)=0.515。
120°では f=−0.1066e−4+1.0112e−0.25=−0.0020+0.7875=0.786 です。

肘の角度に対する補正ウェイト。30°から90°への線型ドライバーと、例のポーズ0°と90°を通るガウスRBF。45°で0.515、120°で0.786で、90°を越えると0へ戻って行く

RBFの曲線は滑らかで両方の例を通りますが、90°より先で何が起きるか見て下さい。ガウスの山は距離と共に消えるので、肘を更に曲げると補正は0へ戻ってしまい、普通は其れは間違いです。
此れがRBF補間の典型的な落とし穴です。例の外では0に戻ります。
リグでは、極端なポーズ(例えば140°)にもう1つ例を彫るか、補間関数に線型の項を加えて避けます。
同じ仕組みは多次元(肩の3つの角度全て、又はクォータニオンの距離)でも、ウェイトの代わりに頂点の差分全体でも使えます。執筆時点では、PSDソルバーやRBFノードはDCCのリギングの標準的な道具です。

デルタマッシュ

デルタマッシュは、Rhythm & Huesで開発され、MancewiczらがDigiPro 2014で発表した物で、変形を自動的に綺麗にします:

  1. バインドポーズでメッシュを平滑化し(各頂点を隣の頂点の平均へ近付ける 𝐯←𝐯+λ(𝐯¯−𝐯) を数回繰り返す)、各頂点の平滑化した位置からの差分(デルタ)を、平滑化した表面のローカル座標系(接線、従接線、法線)で保存します。
  2. 実行時には、安いスキニング、剛体バインドでも良いのでメッシュを変形し、変形したメッシュを同じ様に平滑化して、保存した差分を、平滑化した変形後の表面のローカル座標系に回して足し戻します。

平滑化が粗いスキニングの折れや潰れを消し、差分がモデリングした細部(皺、ボタン、筋肉)を戻します。法線方向に0.3の差分が、座標系が30°回った平滑化後の表面の点 (5,2) の上なら、(5,2)+0.3(−sin30°,cos30°)=(4.85,2.26) に来ます。
平滑化は毎フレーム時間が掛かるので、ゲームではデルタマッシュをオフラインで使い、より良いLBSのウェイトをベイクする事がよくあります。

25.4 アニメーションブレンドを深く

第14章の14.8節では、ブレンド、クロスフェード、ステートマシン、ブレンドツリーを挙げました。

ポーズとは何か、どう混ぜるか

ポーズは関節ごとに1つのローカル変換で、普通は親に対する平行移動、回転のクォータニオン、拡大縮小(TRS)として保存します。
2つのポーズを重み α で混ぜるのは、関節ごとに、ローカル空間で行います:

  • 平行移動と拡大縮小:線型補間
  • 回転:nlerp(符号の確認付きの正規化した線型補間)又はslerp。殆どのエンジンはnlerpを使います。ブレンドの重みはどうせ毎フレーム変わり、nlerpの方が安く、沢山の入力に対して順序に依らない(幾つものクォータニオンの重み付き和を取って、最後に1回正規化する)からです。

ローカル空間なら各関節は自分の回転だけを混ぜ、其の後のFKがボーンの長さを保った正しいスケルトンを作ります。ワールド行列を混ぜると縮んでしまいます(25.1節)。

1次元のブレンドスペースと同期

ブレンドスペースはパラメータの空間の点にクリップを置き、今のパラメータの値で重みを決めます。
1次元では速さに沿って、待機を0 m/s、歩きを1.5 m/s、走りを4.5 m/sに置きます。
2.0 m/sは歩きと走りの間です:

wrun=2.0−1.54.5−1.5=16,wwalk=56

ですが歩きのサイクルは1.2秒、走りは0.8秒です。フレームごとに混ぜると、足はすぐにずれてしまいます(片方のクリップでは左足、もう片方では右足が着地)。
対策は正規化時間(位相)による同期です。両方のクリップがサイクルの同じ割合を再生し、混ぜたサイクルの長さは

T=56⋅1.2+16⋅0.8=1.1333 s

になります。従って歩きは 1.2/1.1333=1.059 倍、走りは 0.8/1.1333=0.706 倍の速さで再生されます。
クリップが同じ足から始まらなくても位相が揃う様に、クリップには同期マーカー(足の着地のイベント)を付けます。

厄介な細部があります。ルートの速さはもう丁度2.0ではありません。
1サイクルの歩幅は、歩きが 1.5⋅1.2=1.8 m、走りが 4.5⋅0.8=3.6 m で、混ぜると1.1333秒に 56⋅1.8+16⋅3.6=2.1 m、つまり1.853 m/sです。
正確な速さになる重みを解く(此処では wrun=0.2308)か、再生速度を 2.0/1.853=1.079 倍にするか、IKで歩幅を調整します(ストライドワーピング)。

三角形による2次元のブレンドスペース

移動は2つの数、キャラクタの座標系での横と前の速度で決まります。
2次元のブレンドスペースは其の平面にクリップを散らします。中心に待機、4方向に1.5 m/sの歩きと4.5 m/sの走りです。
任意の点で混ぜる為に、サンプルを三角形分割し(例えばドロネー三角形分割)、入力の点が入った三角形での重心座標を重みにします。第6章の6.9節のエッジ関数の面積と全く同じです。
例えば執筆時点のUnreal Engineのブレンドスペースは、既定で三角形分割を使います。

計算例。 入力 𝐩=(1.0,2.5)(前へ2.5 m/s、右へ1.0 m/sずれる)は、前走り (0,4.5)、前歩き (0,1.5)、右走り (4.5,0) の三角形の中にあります。
三角形の面積は 12⋅3⋅4.5=6.75 です。
各重みは、其の頂点の向かいにある小さな三角形の面積を6.75で割った物です:

  • 前走り:三角形 𝐩、前歩き、右走り。面積3.0、重み 3.0/6.75=0.444
  • 前歩き:三角形 前走り、𝐩、右走り。面積2.25、重み0.333
  • 右走り:三角形 前走り、前歩き、𝐩。面積1.5、重み0.222

確認:0.444(0,4.5)+0.333(0,1.5)+0.222(4.5,0)=(1.0,2.5) で、重みの和は1です。
評価するクリップは3つだけで、重みは三角形から次の三角形へ連続的に変わります。

待機、4方向の1.5 m/sの歩きと4.5 m/sの走りの9つのサンプルを三角形分割した2次元のブレンドスペース。入力1.0, 2.5は前走り、前歩き、右走りの三角形の中にあり、重みは0.444、0.333、0.222

細い三角形では重みが急に変わるので、アニメーターは必要な所にサンプル(斜めの歩き)を足します。

加算アニメーション

加算アニメーションはポーズではなく、基準のポーズからの差分を保存します。呼吸、怯み、反動、カーブでの傾きです。
各関節について

qadd=qref−1qpose,q=qbaseslerp(qid,qadd,w)

です。qid=(1,0,0,0) は回転無しなので、重み w が差分を拡大縮小します(平行移動は重み付きの差分を単に足します)。
計算例。 怯みの動きが、背骨の関節を基準に対してx軸周りに12°前へ曲げるとします。
背骨がx軸周りに20°の走りの上に重み0.5で再生すると、結果はx軸周りに 20°+0.5⋅12°=26° です。
差分は下で何が再生されていても其の上に足されるので、1つの怯みが待機にも歩きにも走りにも使えます。加算のレイヤーが幾つもあると順番が効きます。違う軸周りの回転は可換ではないからです。

レイヤーとマスクによるブレンド

レイヤーブレンドは、体の部分ごとに違うアニメーションを再生します。脚は走り、上半身は狙ったりリロードしたりします。
ボーンマスクは各関節に上のレイヤーの重みを与えます。捻りが分散する様に背骨に沿って上げて行く事が多く、例えば3つの背骨の関節に0.25、0.5、0.75、胸から上は1です。
マスク0.5の背骨の関節が、走りで0°、狙いのポーズで40°なら、0.5のslerpで20°になります。

ルートモーション

第13章のルート関節が、キャラクタの世界の中での動きを担います。
ルートモーションでは、クリップのルートの変位を毎フレーム取り出してキャラクタの移動の処理に適用するので、足はキャプチャした通りに地面に付いた儘です。
計算例。 歩きのクリップは1.2秒のサイクルでルートを1.8 m前へ動かします。1.5 m/sです。60 fpsなら1フレームあたりクリップの前方向 +z に沿って 1.8/72=0.025 mです。
キャラクタがy軸周りに30°を向いているなら、世界での変位は 𝐑y(30°)(0,0,0.025)=(0.025sin30°,0,0.025cos30°)=(0.0125,0,0.0217) mです。
代わりの方法は、ゲームのコードが動かす其の場(インプレース)のアニメーションです。2つが食い違うと足が滑ります。コードが2.0 m/sで動き、歩きが1.5 m/sでアニメーションしていれば、再生速度(1.33倍)か歩幅を調整しない限り毎秒0.5 m滑ります。
ゲームでは、正確な経路が大事な攻撃やよじ登りにはルートモーション、反応の良い走りにはコードでの移動を使う事がよくあります。

遷移:クロスフェードと慣性化

クリップAからクリップBへ0.2〜0.3秒掛けるクロスフェードは、遷移の間ずっと両方のクリップを評価して混ぜます。
其の間コストは2倍で、混合自体のアーティファクト(両方のクリップの足の接地が混ざる)が出る事もあります。
慣性化(イナーシャライゼーション)は新しいクリップへ即座に切り替え、跳びを0へ減衰するオフセットで隠します。
デヴィッド・ボロがGears of War 4(2016年)の為の物として、SIGGRAPH 2017とGDC 2018の講演で説明しました。
切り替えの瞬間に、各関節(と各チャンネル)について、オフセット x0=旧−新 と其の変化率 v0 を記録します。そして新しいクリップに、x0 から速度 v0 で始まり、ブレンド時間 T の後に速度も加速度も0で0に着くオフセット x(t) を足します。
条件が6つなので5次の多項式が要ります。正規化した時間 s=t/T、V=v0T とし、初めの加速度を0とすると

x(s)=x0+Vs−(10x0+6V)s3+(15x0+8V)s4−(6x0+3V)s5

です。
v0 が0から離れる向き(オフセットがまだ大きくなっている)なら、行き過ぎない様に先に0にします。
ボロの版は、同じく行き過ぎを防ぐ為に初めの加速度も選び、T を短くする事もあります。此処では計算を簡単にする為に加速度を0にしています。
V=0 なら此れは x0(1−10s3+15s4−6s5) で、第13章のスムーズステップの5次の親戚「スムーザーステップ」です。

計算例。 切り替えの瞬間、肘は旧いクリップでは30°で−45°/sで動いていて、新しいクリップでは10°で+15°/sで動いています。
オフセットは x0=20°、其の速度は v0=−60°/s(既に縮んでいるので其の儘)、T=0.3 s、V=−18° です:

x(s)=20−18s−92s3+156s4−66s5

確認:x(1)=20−18−92+156−66=0。
t=0.15 s(s=0.5)では 20−9−11.5+9.75−2.0625=7.19° なので、肘は 10+15⋅0.15+7.19=19.44° になります。

t = 0での切り替えの前後の関節の角度。30°から下がる旧いクリップ、10°から上がる新しいクリップ、0.3秒のクロスフェード、そして旧い動きを滑らかに続けて0.3秒で新しいクリップに着く慣性化の曲線。0.15秒で19.44°

慣性化の曲線は旧い動きを丁度其の速度で離れ、新しいクリップに滑らかに着地し、評価するクリップは1つだけです。
回転のオフセットはクォータニオン(新しいポーズから旧いポーズへの回転)で、其の軸と角度の形で減衰させます。
例えば執筆時点のUnreal Engineには慣性化のノードがあり、今では多くのゲームが殆どの遷移に使っています。モーションマッチング(25.5節)を実用的にしているのも慣性化です。

ステートマシン、再び

第14章のステートマシンが此れら全てをまとめます。各ステートはブレンドスペースかブレンドツリーを持ち、各遷移は条件とブレンドの方法を持ち、大きなグラフはサブステートマシンを入れ子にします。
弱点は規模です。何百ものクリップには、手で調整した何千もの遷移が要ります。モーションマッチングが攻めるのは此の問題です。

25.5 モーションマッチング

モーショングラフからモーションマッチングへ

第13章の13.9節のモーショングラフ(コヴァー、グライヒャー、ピギン、2002年)は、あるクリップから別のクリップへ跳べる場所を事前に計算します。
モーションマッチングは事前計算をやめます。編集していないモーションキャプチャの大きなデータベース(演者があらゆる組み合わせで歩き、曲がり、動き出し、止まる何分もの動き)を持ち、実行時に数フレームごとに、今のポーズからプレイヤーの望む方へ一番良く続くフレームを探索します。
ユービーアイソフト・モントリオールのサイモン・クラヴェがGDC 2016(「Motion Matching and The Road to Next-Gen Animation」)で発表し、For Honorで使われました。其れ以来、多くのゲームとエンジンが採用しています。

特徴

データベースの各フレームに、其れを選ぶ時に大事な事を表す特徴ベクトルを、キャラクタのローカル座標系で付けます:

  • 軌跡の特徴:少し先の未来、例えば0.33、0.67、1.0秒後にルートがどこにあるか(とどちらを向くか)
  • ポーズの特徴:幾つかの主な関節、普通は足と腰の位置と速度。次のフレームが今の動きを跳ばずに続ける為です。

実行時のクエリも同じ様に作ります。ポーズの特徴はキャラクタの今のポーズから、軌跡の特徴はプレイヤーのスティック入力から先を予測して作ります(第14章の様なバネを使う事が多いです)。
単位が違う特徴(メートル、メートル毎秒)は比べられる様にしなければなりません。各特徴をデータベース全体の平均を引いて標準偏差で割って正規化し、優先度を決める為に重みを掛けます(反応の良さには軌跡に、滑らかさにはポーズに大きな重み)。

探索の計算例

1フレームあたり6個の数の小さな例です。0.5秒後と1.0秒後のルートの未来の位置 (x,z) と、左右の足の前方向のずれ zL,zR です。
標準偏差は軌跡の数が0.5 m、足が0.2 mで、重みは全て1です。
プレイヤーはスティックを右前に倒し、今のポーズでは左足が前にあります:

𝐪=(0.3,0.7,0.9,1.2,0.15,−0.10)

候補のフレームが4つあります:

フレーム 軌跡 0.5秒 軌跡 1.0秒 足 軌跡のコスト ポーズのコスト 合計
A:真っ直ぐ歩く (0, 0.75) (0, 1.5) (0.12, −0.12) 3.970 0.033 4.003
B:右へ曲がって歩く、逆の足 (0.25, 0.7) (0.85, 1.25) (−0.10, 0.14) 0.030 3.003 3.033
C:右へ曲がって歩く (0.3, 0.65) (0.95, 1.1) (0.18, −0.05) 0.060 0.085 0.145
D:右へ曲がって走る (0.6, 1.4) (1.8, 2.6) (0.2, −0.2) 13.400 0.313 13.713

各コストは、正規化した差の2乗の和です。
Cでは、軌跡の差を0.5で割ると (0,−0.1,0.1,−0.2) で、2乗して足すと0.06。足の差を0.2で割ると (0.15,0.25) で、0.0225+0.0625=0.085 です。
Bは軌跡が更に近い(0.03)のですが、足の位相が逆です:(−0.250.2)2+(0.240.2)2=1.5625+1.44=3.003。
Bへ跳ぶと1フレームで足が入れ替わってしまいます。ポーズの特徴が其れを防ぎます。
Cが勝ち、キャラクタは其処へ遷移します。普通は慣性化を使います。

キャラクタの座標系での上からの図。0.3, 0.7と0.9, 1.2を通る望む軌跡と、真っ直ぐのA、右へ曲がるBとC、遠くまで走るDの候補の軌跡。コストは4.003、3.033、0.145、13.713

コストと速さ

実際の特徴ベクトルは20〜30個の数を持ちます。
毎秒30フレームで30分のキャプチャは 30⋅60⋅30=54,000 フレームで、各27個のfloatなら、総当たりの探索は 54,000⋅27=146 万個の数(5.8 MB)に触れます。其れを毎秒何回か、キャラクタごとにです。
プレイヤーには払えても群衆には払えないので、実装では加速構造(特徴の上のk-d木やバウンディングボックスの階層)を加え、探索を0.1秒程度ごとにだけ行い、今の続きが十分良い間は探索を省きます。
キャプチャはプレイヤーが求め得る全ての動きを含まなければならず、状況に合わないフレームはタグで除外します。

学習型モーションマッチングと学習した制御

学習型モーションマッチング(ホールデン、カヌーン、ペレピチカ、ポパ、SIGGRAPH 2020)は、データベースを同じ仕事をする小さなニューラルネットワークで置き換えます。1つは次の特徴を予測し(「ステッパー」)、1つは探索の仕事をし(「プロジェクター」)、1つは特徴から完全なポーズを作り直します(「デコンプレッサー」)。メモリは僅かで済みます。
其れより前には、位相関数ニューラルネットワーク(ホールデン、幸村、齊藤、SIGGRAPH 2017)が、ユーザーの入力と地形から直接移動の動きを生成しました。
強化学習で訓練した制御器が、シミュレーションしたキャラクタを関節のトルクで動かし、キャプチャしたクリップを真似させる事も出来ます(例えばDeepMimic、ペンら、2018年)。物理的に正しく、押されると反応する動きです。
レンダリングでのニューラルネットワークは第29章で扱います。

25.6 全身のインバースキネマティクス

第13章では、2リンクのIKを解析的に、CCD法で反復的に解き、ヤコビ行列を見せました。
此処では、長い連鎖や全身に使う方法を見ます。

FABRIK

FABRIK(Forward And Backward Reaching Inverse Kinematics。アリスティドゥとラゼンビー、Graphical Models、2011年)は、角度ではなく関節の位置で動きます。
各関節を隣の関節へ向かう直線の上の正しい距離に置く事でボーンの長さを保ち、2つの段階で進みます:

  1. 届くか:目標がルートからボーンの長さの和より遠ければ、連鎖を目標へ向けて真っ直ぐ伸ばして終わります。
  2. 後ろ向きの段階:エンドエフェクタを目標に置きます。そして末端からルートへ向かって、各関節を(既に動かした)子から其の関節の今の位置への直線の上に、子からボーンの長さの距離で置きます。ルートはずれた所に来ます。
  3. 前向きの段階:ルートを本来の場所へ戻します。そしてルートから末端へ向かって、各関節を(既に動かした)親から其の関節の今の位置への直線の上に、ボーンの長さの距離で置きます。
  4. エンドエフェクタが目標に十分近くなるまで繰り返します。

第13章の腕での計算例です。l1=3、l2=2 で、x軸に沿って真っ直ぐ(関節は (0,0)、(3,0)、(5,0))、目標は (2,3) で、距離は 13=3.606<5。届きます。

  • 後ろ向き:手が (2,3) へ行きます。肘は元の位置 (3,0) の方へ動きます。方向 (1,−3) の長さは 10=3.162 なので、肘は (2,3)+2(1,−3)3.162=(2.632,1.103) へ行きます。ルートは付いて来て (−0.135,−0.056) に行き、居るべき場所から0.146離れます。
  • 前向き:ルートを (0,0) へ戻します。肘は長さ2.854の (2.632,1.103) の方へ行き、3(2.632,1.103)2.854=(2.767,1.159) に来ます。手は其処から (2,3) の方へ行きます。方向 (−0.767,1.841) の長さは1.994なので、手は (2.767,1.159)+2(−0.767,1.841)1.994=(1.998,3.005) に着きます。

l1 = 3、l2 = 2の腕でのFABRIK。目標が2, 3にある開始のポーズ。手が目標の上、肘が2.632, 1.103で、ルートが0.146離れた後ろ向きの段階。ルートを戻し、肘が2.767, 1.159、誤差0.0056になった前向きの段階

1回の反復で誤差は0.0056、2回で0.0001未満です。同じ問題でCCD法は0.012に達するのに4回の掃引が要りました(第13章の13.7節)。
FABRIKの動きは安く(関節ごとに正規化1回、三角関数無し)、曲げが連鎖全体に分散し、複数のエンドエフェクタ(ライフルを持つ両手)も枝ごとに段階を実行して共有の関節で平均すれば扱えます。
角度は最後の位置から読み取ります:θ1=atan2(1.159,2.767)=22.73° で、正確な値は22.62°です。

ヤコビ行列の方法

第13章のヤコビ行列 𝐉 は、関節の小さな変化とエンドエフェクタの小さな動きを Δ𝐩≈𝐉Δ𝜽 で結びます。
エフェクタを誤差 𝐞=𝐩target−𝐩 だけ動かすには、古典的な3つの選択肢があります(サミュエル・バスの2004年の解説「Introduction to Inverse Kinematics with Jacobian Transpose, Pseudoinverse and Damped Least Squares methods」が定番の入門です):

  • ヤコビ行列の転置法:Δ𝜽=α𝐉⊤𝐞。逆行列は一切要りません。𝐉⊤𝐞 は誤差が一番速く減る方向で、良い歩幅は α=𝐞⋅𝐉𝐉⊤𝐞‖𝐉𝐉⊤𝐞‖2 です。
  • 擬似逆行列:Δ𝜽=𝐉+𝐞。目標の座標より関節が多い連鎖では 𝐉+=𝐉⊤(𝐉𝐉⊤)−1(線型モデルで 𝐞 を達成する一番小さな関節の変化)。正方行列の 𝐉 なら単に 𝐉−1 です。
  • 減衰最小二乗法(DLS。ワンプラー、又独立に中村と花房、1986年):Δ𝜽=𝐉⊤(𝐉𝐉⊤+λ2𝐈)−1𝐞。減衰 λ が正確さと安定性を交換します。‖𝐉Δ𝜽−𝐞‖2+λ2‖Δ𝜽‖2 を最小にするので、巨大な関節の変化を決して求めません。

計算例。 第13章のFKのポーズ、θ1=30°、θ2=45°、手は (3.116,3.432) で、

𝐉=[−3.432−1.9323.1160.518],det𝐉=4.243

目標は (2.5,3.8) なので 𝐞=(−0.616,0.368)、距離は0.717です。

  • 転置法:𝐉⊤𝐞=(3.260,1.380)、α=0.0404 なので Δ𝜽=(0.1317,0.0558) rad =(7.55°,3.20°)。
  • 擬似逆行列(此処では 𝐉−1):Δ𝜽=(5.30°,8.85°)。
  • λ=1 のDLS:Δ𝜽=(6.34°,5.36°)。

新しい角度でFKを計算すると、手は目標から其々0.035、0.070、0.041外れます。
擬似逆行列の段階は線型モデルでは正確です(丁度 (2.5,3.8) を予測します)が、腕は直線ではなく弧の上を動くので、どの方法も更に数回の反復が要ります。終わり近くで誤差が小さくなれば、3つとも収束します。

第13章のFKのポーズの腕と、目標2.5, 3.8へ向けて転置法、擬似逆行列、減衰最小二乗法で1段階進めた後の腕。誤差は0.035、0.070、0.041

違いが出るのは特異姿勢の近くです。
肘を5°だけ曲げ(θ1=0、手は (4.992,0.174)、det𝐉=6sin5°=0.523)、手をxに沿って更に0.3先、丁度届かない所に求めます。
逆行列の答えは Δ𝜽=(65.5°,−164.1°) です。腕は振り回され、手は (1.12,0.73) に着きます。
λ=1 のDLSの答えは (0.49°,−1.58°) で、小さくて無害な一歩です。
此れがソルバーが減衰を加える理由で、det𝐉 が小さくなると λ を自動で大きくする事もよくあります。

可動域と其の他の拘束

反復的なソルバーは、各段階の後に可動域を守らせます。蝶番の角度をクランプし(膝に160°を求める段階は150°になる)、球関節のボーンを許される円錐の中へ射影し直し、FABRIKでは段階の途中で各関節を親に対する許される範囲へ戻します。
蝶番には曲げる平面も要ります。FABRIKだけでは、近ければ平気で膝を逆に曲げます。其れを直すのが第13章のポールベクター、又は「正しい側の半平面へ鏡映する」規則です(実践課題で見ます)。

足の接地

アニメーションは平らな床でキャプチャします。
階段や坂の上では、フットIKが足を本当の地面に付けます:

  1. 各足のアニメーションの位置から下へレイを飛ばし、地面の高さ(と法線)を求めます。
  2. 足が下へ行かなければならない(穴、低い段)なら、骨盤がアニメーションの高さの儘では脚が届かないので、骨盤を下向きのオフセットの一番大きい分だけ下げます。
  3. 各脚を、下げた股関節から其の地面の点までの2ボーンのIKの連鎖(第13章の余弦定理)として、膝が前を向く様にポールベクター付きで解きます。
  4. 各足を地面の法線に合わせて回し、段で跳ばない様に全てのオフセットを時間で滑らかにします。

計算例。 太腿と脛は其々0.45 mです。
アニメーションでは股関節が (0,0.85)、足が (±0.15,0) にあり、距離は 0.152+0.852=0.863 です。膝の曲げは arccos0.745−0.4050.405=arccos0.840=32.9° です。
後ろの足の下の地面には −0.10 の窪み、前の足の下には +0.15 の段差があります。
骨盤を0.10下げて (0,0.75) にします。
後ろの足の目標 (−0.15,−0.10) は再び0.863離れているので、膝は32.9°の儘です。
前の足の目標 (0.15,0.15) は 0.0225+0.36=0.618 離れています:cosθ=0.3825−0.4050.405=−0.056 で、膝の曲げは93.2°です。

足の接地。股関節が0.85で両足が地面の高さのアニメーションのポーズと、フットIKの後のポーズ。骨盤が0.10下がり、後ろの足はマイナス0.10の窪みで膝が32.9°、前の足はプラス0.15の段差の上で膝が93.2°

ルックアット

気になる点の方へ頭を向けるのは、其れ自体が小さなIKの問題です。
頭の前方向 𝐟 から目標の方向 𝐝 への回転は、軸が 𝐟×𝐝(正規化)、角度が arccos(𝐟⋅𝐝) の、外積と内積から作る軸と角度の回転です(線型代数 第13章の13.2節)。
𝐟=(0,0,1) で目標が横に60°なら 𝐝=(sin60°,0,cos60°)=(0.866,0,0.5) で、𝐟×𝐝=(0,0.866,0) なので軸は +y、角度は arccos0.5=60°、q=(cos30°,0,sin30°,0) です。
頭だけを回すとロボットの様に見えるので、回転を分配します。例えば背骨に20 %、首に30 %、頭に50 %(12°、18°、30°、其々恒等変換からのslerp)とし、無理の無い円錐にクランプして、其れを越えたら代わりに体を回します。

全身のソルバーは、こうした目標(手を縁に、足を地面に、頭を目標に、重心を足の上に)を、スケルトン全体に対するFABRIK風の、又は減衰付きのヤコビ行列の1回の解法でまとめます。

ラグドールとパワードラグドール

ラグドール(第14章)は、スケルトンを関節の拘束で繋いだ剛体に変えた物です。説得力のある倒れ方をしますが、自分の意志はありません。
ゲームでは、キャラクタが倒された時にラグドールへ混ぜ、ラグドールの最後のポーズに合う起き上がりのクリップで戻ります。
パワードラグドール(アクティブラグドールとも言います)は、各関節にアニメーションに付いて行こうとするモーターを加えます。
一番簡単なモーターはPD制御で、目標の角度へのバネとダンパーです:

τ=kp(θtarget−θ)−kdω

計算例。 慣性モーメント I=0.5 kg·m²、kp=200 N·m/rad、kd=20 N·m·s/radの肘が、1 rad/sで回りながらアニメーションの角度から0.3 rad遅れています:τ=200⋅0.3−20⋅1=40 N·mで、角加速度は 40/0.5=80 rad/s²です。
此のゲインは偶然ではありません。kd=2kpI=2100=20 は臨界減衰で、第14章の14.2節のバネの c=2km と同じです。関節は振動せずに速く目標に着きます。
高いゲインはアニメーションによく付いて行き、低いゲインはだらりとします。被弾のリアクションでは当たった手足のゲインを一瞬下げるので、物理が押し、アニメーションが引き戻します。
物理エンジンが関節の拘束とモーターをどう解くかは第26章です。

検定のポイント

シラバスの外ですが、分野5の2つのシラバス項目、キャラクタのアニメーションとリアルタイムアニメーション(第13章と第14章)の理解を深めます:

  • LBSは行列を混ぜ、回転の重み付き和は回転に縮みを掛けた物になる(90°で0.707、180°で0)。曲げで細り、キャンディラッパーになる。DQSは同じウェイトで剛体変換をデュアルクォータニオンとして混ぜるので、全ての頂点が剛体として動く。潰れないが、曲げで少し膨らみ、180°を越える捻りで反転する。
  • スキニング行列は 𝐖j𝐁j−1 で、バインドポーズでは単位行列。
  • ローカル空間で関節ごとにslerp又はnlerpで混ぜる。2次元のブレンドスペースは3つのサンプルの重心座標を重みに使う。同期したクリップは正規化した位相を共有する。
  • クロスフェードは2つのクリップを評価し、慣性化は1つを評価してオフセットを減衰させる。
  • IK:FABRIKは位置を直線に沿って動かし、ヤコビ行列の方法は角度を動かす(転置法、擬似逆行列、減衰最小二乗法。最後の物は特異姿勢の近くでも安定)。

25.7 フェイシャルアニメーションを深く

FACSに基づくリグとコンビネーションシェイプ

第13章でFACS(Facial Action Coding System)と其のアクションユニット(AU)、そしてAUごとに大体1つのブレンドシェイプを持つFACSに基づくリグを紹介しました。
ですが本物の顔は線型には足し算されません。顎を下げた(AU26)状態の笑顔(AU12)は、2つのシェイプの和とは違う形に唇を伸ばします。
そこでリグはコンビネーションシェイプを加えます。AUの組や3つ組の為に彫った補正で、其のウェイトは関わるウェイトの積です。
AU12が0.8、AU26が0.5なら、コンビネーションシェイプは 0.8⋅0.5=0.4 です。どちらかが切れていれば0、両方が全開なら全開です。
映画品質の顔のリグには、何百もの基本シェイプとコンビネーションシェイプに加え、眼球の上を滑る瞼やくっつく唇の補正もあります。

キャプチャからブレンドシェイプのウェイトをソルブする

パフォーマンスキャプチャは、顔を動く点として記録します。マーカー、映像で追跡した特徴、又は密に復元したメッシュです。
リグを動かすには、キャプチャをブレンドシェイプのウェイトに変えなければなりません。ブレンドシェイプのソルブです。
追跡した点の無表情からの変位をベクトル 𝐝 に並べ、各シェイプの同じ点の変位を行列 𝐁 の列に入れます。そして 𝐝 を最小二乗の意味で一番良く再現するウェイト 𝐰 を求めます:

min𝐰‖𝐁𝐰−𝐝‖2⇒𝐁⊤𝐁𝐰=𝐁⊤𝐝

(正規方程式)。顔のリグはキャプチャが区別出来るより多くのシェイプを持つので、普通は 0≤wk≤1 という条件と、ウェイトを疎に保つ小さなペナルティを加えます。

シェイプ2つ、測った数3つの計算例です。口角の高さ、口角の横の位置、顎の高さです。
笑顔:(0.40,0.30,0.10)、顎を下げる:(−0.20,0,−0.80)、測定値 𝐝=(0.17,0.20,−0.41)。

𝐁⊤𝐁=[0.26−0.16−0.160.68],𝐁⊤𝐝=[0.0870.294]

行列式 0.26⋅0.68−0.162=0.1512 を使うと、

wsmile=0.68⋅0.087+0.16⋅0.2940.1512=0.702,wjaw=0.16⋅0.087+0.26⋅0.2940.1512=0.598

作り直した変位 (0.161,0.211,−0.408) は測定値から0.014しか外れません。キャプチャは70 %の笑顔で顎を60 %開けた物に、どのシェイプの組み合わせでも説明出来ない少しのノイズが乗った物で、最小二乗法が捨てるのは丁度其のノイズです。

執筆時点では、一般向けの機器が此のソルブをリアルタイムで行います。例えばAppleのARKitのフェイストラッキングは、jawOpenやeyeBlinkLeftの様な名前付きの係数を0(無表情)から1(最大)で出力し、対応するシェイプを持つリグをすぐに動かせます。

パフォーマンスキャプチャ

映画やAAAのゲームでは、パフォーマンスキャプチャで体と顔と声を一緒に記録し、顔はヘッドマウントカメラで撮ります。
映像を追跡し、先ず俳優自身のデジタルダブルにソルブし、共通のAUの名前や学習した対応でキャラクタにリターゲットし、アニメーターが仕上げます(目、p・b・mの唇の接触)。

25.8 群衆とアニメーション圧縮

群衆のアニメーションLOD

第14章で群衆とアニメーションのLODを紹介しました。
スタジアムや合戦の典型的な段階分け:

  • 近く:完全なスケルトン、ブレンド、IK
  • 中くらい:ボーンを減らし、15〜30 Hzで更新、IK無し
  • 遠く:スケルトン無し。頂点アニメーションテクスチャ(VAT)が各フレームのスキニングした頂点位置をテクスチャの行に保存し、頂点シェーダーが頂点番号と時間で読みます
  • 一番遠く:インポスター。第12章のビルボードに、アニメーションのフレームをパラパラ漫画の様に入れた物

2,000頂点のキャラクタで60フレームのサイクル、位置を16ビットのfloat 4つ(8バイト)で保存するVATは 2,000⋅60⋅8=960,000 バイトで、1 MB未満です。GPUのインスタンシングを使えば、何千ものコピーが違う位相でほぼ只で再生出来ます。

アニメーション圧縮

其の儘だと、クリップは関節とフレームごとに平行移動(float 3つ)、回転(4つ)、拡大縮小(3つ)の40バイトを保存します。60関節を30 fpsなら1フレーム2,400バイト、1分で4.32 MBです。
アニメーション圧縮は幾つもの層で行います:

  • 一定のトラックを除く。 殆どの関節は拡大縮小せず、平行移動するのはルートだけです。60個の回転(各16バイト)とルートの平行移動1つ(12バイト)を残すと1フレーム972バイトで、2.5分の1になり、損失は一切ありません。
  • キーフレーム削減。残ったキーの間の補間で許容誤差内に再現出来るキーを除きます。フレーム0〜4のサンプルが0、1.02、1.98、3.01、4.0のチャンネルは、最初と最後を通る直線から0.02以内なので、許容誤差0.05ならキーは2つだけ残ります。スプライン(第8章)による曲線の当てはめは更に進みます。
  • 量子化。各トラックが実際に使う範囲の中で、値を少ないビットで保存します。

回転の古典的な技は最小3成分(smallest three)です。単位クォータニオンは w2+x2+y2+z2=1 を満たすので、成分を1つ捨てて作り直せます。
絶対値が一番大きい成分を捨て(どれかを2ビットで示す)、其れが負ならクォータニオン全体の符号を反転します(q と −q は同じ回転)。すると残りの3つは [−12,12] に入り、其々10ビットで 1.41421023=0.00138 刻みに覆えます。
回転1つが128ビットではなく32ビットです。

計算例。 q=(0.9659,0,0,0.2588)、z軸周りに30°です。
w を捨てます。z のコードは round(0.2588+0.70711.4142⋅1023)=round(698.72)=699 で、復号すると0.2592です。x と y(0)はコード512になり、復号すると0.0007です。丁度0に当たるコードが無いからです。
作り直すと w=1−0.00072−0.00072−0.25922=0.9658 で、復号した回転は元から0.12°ずれます。
回転を4バイト、ルートの平行移動を16ビットの値3つにすると、1フレームは 60⋅4+6=246 バイトで、元の2,400の9.8分の1です。

誤差は連鎖に沿って積み重なる(股関節で0.05°ずれると1 m下の足は 1000⋅0.05⋅π180=0.87 mmずれる)ので、良い圧縮器は連鎖の末端で誤差を測り、各トラックに丁度足りるだけのビットを与えます。

25.9 まとめ

  • スキニング行列は 𝐖j𝐁j−1 で、バインドポーズでは単位行列。LBSは其れらを平均し、2つの回転の平均は回転に cosβ−α2 を掛けた物になる。曲げの細りとキャンディラッパー。
  • デュアルクォータニオン qr+ε12tqr(ε2=0)が剛体変換を表す。DQSは其れらをLBSのウェイトで(符号を確認して)混ぜて正規化するので、各頂点が剛体として動く。90°の肘は半径0.5を保つ(LBSは0.354)。曲げで少し膨らみ、180°を越える捻りで反転し、拡大縮小は直接扱えない。
  • 補正シェイプは関節の角度で駆動し、スキニングの前に足す。ポーズスペースデフォメーションは彫った補正をRBFで補間する(例から離れると0に戻る)。デルタマッシュは粗い変形を平滑化して細部を戻す。
  • ポーズはローカル空間で関節ごとに混ぜる。ブレンドスペースは重みを補間する(1次元は位相の同期付き、2次元は三角形分割の重心座標)。加算レイヤーは差分を足し、マスクはレイヤーを体の部分に限り、ルートモーションはクリップでキャラクタを動かす。
  • 慣性化はクリップを即座に切り替え、オフセットを5次式で減衰させ、2つではなく1つのクリップを評価する。
  • モーションマッチングは、正規化して重みを付けた特徴(未来の軌跡、足の位置と速度)がクエリに一番合うフレームをモーションキャプチャのデータベースから探す。学習型モーションマッチングは其れをネットワークに圧縮する。
  • FABRIKは後ろ向きと前向きの段階で位置を使ってIKを解く。ヤコビ行列の転置法、擬似逆行列、減衰最小二乗法は角度で解き、DLSは特異姿勢の近くでも安定。可動域、足の接地、ルックアットは其の上に作る。
  • パワードラグドールは、PD制御で物理の関節をアニメーションへ向けて動かす(臨界減衰 kd=2kpI)。
  • 顔:FACSのシェイプとコンビネーションシェイプ。ウェイトはキャプチャから範囲付きの最小二乗法でソルブする。
  • 群衆はボーンを減らし、更新を下げ、頂点アニメーションテクスチャとインポスターを使う。圧縮は一定のトラックを除き、キーを減らし、量子化する(最小3成分:回転1つ32ビット)。

25.10 用語

English 日本語
bind pose, inverse bind matrix バインドポーズ、逆バインド行列
skinning matrix, matrix palette スキニング行列、行列パレット
linear blend skinning (LBS) リニアブレンドスキニング
dual number 双対数
dual quaternion デュアルクォータニオン(双対四元数)
dual quaternion skinning (DQS) デュアルクォータニオンスキニング
dual quaternion linear blending (DLB) デュアルクォータニオン線型ブレンド(DLB)
flipping artifact, bulge 反転アーティファクト、膨らみ
twist bone ツイストボーン
corrective blend shape 補正ブレンドシェイプ
pose-space deformation (PSD) ポーズスペースデフォメーション(PSD)
radial basis function (RBF) 放射基底関数(RBF)
delta mush デルタマッシュ
pose ポーズ
blend space ブレンドスペース
synchronization, normalized time 同期、正規化時間
barycentric coordinates 重心座標
additive animation 加算アニメーション
layered blending, bone mask レイヤーブレンド、ボーンマスク
root motion ルートモーション
cross-fade クロスフェード
inertialization 慣性化(イナーシャライゼーション)
motion matching モーションマッチング
feature vector 特徴ベクトル
learned motion matching 学習型モーションマッチング
FABRIK FABRIK
Jacobian transpose ヤコビ行列の転置法
pseudo-inverse 擬似逆行列
damped least squares (DLS) 減衰最小二乗法
singular pose 特異姿勢
joint limit 可動域
foot IK, foot placement フットIK、足の接地
look-at ルックアット
ragdoll, powered ragdoll ラグドール、パワードラグドール(アクティブラグドール)
PD controller PD制御
action unit, combination shape アクションユニット、コンビネーションシェイプ
blend shape solving ブレンドシェイプのソルブ
performance capture パフォーマンスキャプチャ
vertex animation texture (VAT) 頂点アニメーションテクスチャ(VAT)
animation compression アニメーション圧縮
keyframe reduction キーフレーム削減
quantization, smallest three 量子化、最小3成分

25.11 課題

  1. ★ 25.2節の肘が90°ではなく60°曲がります(ボーン1が (2,0) の周りにz軸周りで回り、頂点 (2,−0.5) のウェイトは0.5と0.5)。LBSの位置と肘からの距離を求め、次にDQSの位置(肘の周りの曲げの半分の回転)を求めて、LBSの距離が 0.5cos30° である事を確かめて下さい。
  2. ★ 25.4節の2次元のブレンドスペースで、入力 (0.5,3.0) と (0.6,0.6) の重みを求め、其々が入力を再現する事を確かめて下さい。次に、歩き(1.5 m/s、サイクル1.2秒)と走り(4.5 m/s、サイクル0.8秒)の間の3.5 m/sでの1次元の重みと、同期したサイクルの長さを求めて下さい。
  3. ★★ 慣性化がオフセット x0=−12°、オフセットの速度 v0=30°/s、T=0.25 sで始まります。x(s) を書き、x(1)=0 を確かめ、x(0.5) を求めて下さい。次に v0=−30°/sでやり直し、其の速度を先に0にする理由を説明して下さい。
  4. ★★ l1=4、l2=3 の腕がx軸に沿って真っ直ぐで、目標は (3,5) です。FABRIKの反復を1回(後ろ向きと前向きの段階)手で行い、残りの誤差を答えて下さい。肘を第13章の余弦定理による2つの解析解と比べて下さい。FABRIKはどちらに近付き、其れは何故ですか?
  5. ★★★ 2本のボーンが同じ点を通る同じ軸の周りに、其々0と φ 回ります。2本目に重み w を掛けたDLBが、同じ軸と点の周りの角度 2atan2(wsinφ2,1−w+wcosφ2) の回転になる事を示して下さい。φ=90°、w=0.25 で計算してslerpと比べ、25.2節の膨らみがどこから来るのか説明して下さい。

選択問題

問1 ★ 次のうち、デュアルクォータニオンスキニングについて正しい物はどれか。

あ ボーンのスキニング行列の要素をウェイトで混ぜる。リニアブレンドスキニングと全く同じだが、ボーンあたり8個の数を使う。
い リニアブレンドスキニングとは別のスキンウェイトが必要なので、ウェイトを塗り直さなければならない。
う ボーンの軸周りに180°捻った関節では、リニアブレンドスキニングと同じ様にスキンがボーンの上に潰れる。
え 単位デュアルクォータニオンは任意のアフィン変換を表せるので、ボーンの非一様な拡大縮小を直接扱える。
お ボーンの剛体変換をデュアルクォータニオンとして混ぜて結果を正規化するので、各頂点は剛体変換で動かされる。キャンディラッパーの潰れは避けられるが、曲げた関節でスキンが少し膨らむ事がある。

問2 ★ 次のうち、アニメーションブレンドとモーションマッチングについて正しい物はどれか。

あ 三角形分割した2次元のブレンドスペースでは、全てのサンプルのクリップが0でない重みでポーズに寄与する。
い 慣性化は、クロスフェードと同じ様に、遷移の間ずっと旧いクリップと新しいクリップの両方を評価する。
う モーションマッチングには、全てのクリップの組の間に遷移を持つ、手で作ったステートマシンが必要である。
え モーションマッチングは、アニメーションのフレームのデータベースから、特徴ベクトル(例えば未来の軌跡の点と足の位置)が、今のポーズとプレイヤーの入力から作ったクエリに一番近いフレームを探す。
お 加算アニメーションは関節の絶対的な回転を保存するので、単独でしか再生出来ない。

問3 ★★ 次のうち、インバースキネマティクスについて正しい物はどれか。

あ FABRIKは、毎回の反復でヤコビ行列を使って関節の角度を計算する。
い 減衰最小二乗法は逆行列を取る前に 𝐉𝐉⊤ に λ2𝐈 を加える。此れにより、腕を伸ばし切った様な特異姿勢の近くでも段階が小さく保たれるが、収束は遅くなる。
う 擬似逆行列の段階は、目標がどこであっても、1段階後にヤコビ行列の転置法の段階より必ず小さな誤差を残す。
え CCD法は、連鎖の全ての関節を同時に誤差の勾配に沿って動かす。
お 足の接地では、両脚が地面に届く様に、骨盤を一番高い足の目標の高さまで上げる。

25.12 実践課題

階段の段に脚を乗せ、膝をスキニングし、遷移を滑らかにします。全て定規、コンパス、分度器、電卓で行います。
方眼紙(1単位 = 1 cm)、定規、コンパス、分度器、鉛筆、2色のペン、電卓を用意して下さい。

脚は太腿 l1=4 と脛 l2=4 です。
股関節は H=(0,7.5) に固定され、バインドポーズでは脚は真っ直ぐ下に垂れています。膝は K0=(0,3.5)、足首は (0,−0.5) です。
キャラクタは +x を向いているので、膝は前(+x の方)へ曲がらなければなりません。
足は F=(2,1.5) の段の上に置かなければなりません。

  1. 届くか。 H から F までの距離を測って計算し、l1+l2 より小さい事を確かめて下さい。
  2. 作図によるFABRIK、2回の反復。 後ろ向きの段階:足首を F に置き、F から今の膝への直線を引き、其の直線の上で F から4の所にコンパスで新しい膝の印を付けます。前向きの段階:H から新しい膝への直線を引き、H から4の所に膝の印を付け、次に膝から F の方へ4の所に足首の印を付けます。足首から F までの残りの距離を測ります。もう1回繰り返します。1色目のペンを使います。
  3. 可動域。 膝がどこに来たか見て下さい。後ろ(−x の方)を向いていたら、直線 HF に関して鏡映します(第13章のポールベクターが選ぶ膝です)。次に余弦定理で解析的な膝の位置を2つとも計算し、比べて下さい。
  4. 膝をスキニングする。 バインドポーズで膝の前の (0.5,3.5) に頂点があり、ウェイトは0.5(太腿)と0.5(脛)です。太腿と脛が真っ直ぐ下を向いた状態から回った角度を測ります。剛体のコピー1:頂点を H の周りに太腿の角度だけ回します。剛体のコピー2:脛と一緒に動かします(K0 の周りに脛の角度だけ回してから、K0 を新しい膝へ移します)。LBSは2つのコピーの中点です。DQSは(ウェイトが等しく、両方のコピーが K0 の行き先で一致するので)頂点を K0 の周りに2つの角度の平均だけ回し、K0 を新しい膝へ移します。両方を2色目のペンで印し、膝からの距離を測って下さい。
  5. 膝を慣性化する。 再生していたアニメーションでは膝は20°曲がって止まっていて、IKの結果は段階3の膝の曲げです。x0=20°−θIK、v0=0、T=0.2 sとし、s=0,0.25,0.5,0.75,1 での膝の曲げ θIK+x(s) を計算し、5つの点を時間に対してプロットして下さい。

自分で確かめる方法:

  • 段階1:‖F−H‖=40=6.325<8。
  • 段階2:描いた全てのボーンが丁度4でなければなりません。1回目の反復の後の誤差は約0.31、2回目の後は約0.03で、反復ごとに大体10分の1に縮みます。真っ直ぐな脚から始めると、FABRIKは此の膝を後ろへ曲げ、約 (−1.3,3.7) に来ます。
  • 段階3:cosθ=40−16−1632=0.25 で、膝の曲げは75.5°。2つの解析的な膝は約 (3.32,5.28) と (−1.32,3.72) です。鏡映した膝は1つ目に近い筈で、どちらの解析的な膝も H から4、F から4の筈です。
  • 段階4:太腿は約56.2°、脛は約−19.3°回ります(差の75.5°が膝の曲げ)。DQSの頂点は膝から0.5で、約 (3.80,5.43) の筈です。LBSの頂点は膝から約 0.5cos75.5°2=0.395 で、約 (3.70,5.40) です。
  • 段階5:係数 1−10s3+15s4−6s5 は1、0.896、0.5、0.104、0なので、膝は20°、25.8°、47.8°、69.8°、75.5°と進みます。s=0.5 で丁度半分、始めと終わりは平らで、75.5°を越える事はありません。


コメント(0)

まだコメントはありません。