はじめに
自分の頭を整理するために本記事を書きます。
自分のためであり、本記事は他人に読まれることを想定していません。予めご了承ください。
回転行列 が 4x4 でも 3x3 でも同じ表記使われていますが、適宜読み替えてください
手のモデル
手のモデルは以下であるとします。
upperarm, forearm, hand が繋がっていませんが、繋がっていると考えてください。
あと、ねじれ解消用のボーンとindex(人差し指)以外の指のボーンはあえて非表示にしてあります。
肘と手首の位置
ペン先のワールド座標は、以下のようになります。
は、腕のワールド行列です。
はそれぞれ clavicle, upperarm, forearm, hand, index1, index2, index3, pen のローカル変換行列です。
ペン先を決定するパラメータは、upperarmZ, upperarmX, upperarmY, forearmX, forearmY, handZ, handX, pen の 8つです。
upperarm,forearmのX,Y,Z軸回りの回転角度を とします。
実際に腕を動かしてみれば分かりますが、上腕(upperarm)の自由度は3で、前腕(forearm)の自由度は2です。
は常に0で固定します。 は肘と手首の位置には無関係です。
よって、肘と手首の位置を決定するパラメータは、 の4つであることが分かります。
肘の位置は、 によって決まり、肘から見た手首の位置は によって決まります。
肘と手首の位置を決定する行列は なので、ちょっと書き出してみます。
モデルをいじる
腕のモデルは glTF2.0 で定義されています。
upperarm, forearm, hand の glTFを覗いてみます。
forearm, hand の translation はほぼY軸に平行であり、rotation はほぼ単位行列であり、scale はほぼ単位行列であることが分かります。
forearm, hand の translation はY軸に平行、rotation は単位行列、scale は単位行列であるとして、式 を書き直します。
translation をY軸に平行とみなす理由ですが、後で回転角度を計算するときに、回転角度が0の時の軸を とするためです。
rotation を単位行列とみなす理由ですが、単位行列でないと のように の間に回転行列 が挟まり、 を求めるのが困難になるためです。
scale を単位行列とみなす理由ですが、腕のような構造のモデルでは、拡大行列は使用しないのが普通ですし、成分によって拡大率が異なるといろいろな不都合が出るためです。
※upperarm, forearm のねじれ回避用のボーンに関しても同様の処理をしたほうが良いと思います。
式 は 4x4 ですが、回転成分だけに着目して 3x3 を考えてみます。
式 で は手首の位置に無関係なので省略しました。
肘の位置から肩の角度を求める
さて、FABRIK などの IK で肘と手首のワールド座標が求まったとします。
前述したとおり、肘の位置は、 によって決まるのでこれらを求めてみます。
upperarm の原点のワールド座標 は以下のように求まります。
肘のワールド座標を とします。
は の平行移動成分を無くした 3x3 の行列とします。
このとき、upperarm の座標系における肘の位置までの方向ベクトル は、以下のように求まります。
を単位ベクトル化します。
のときの upperarm の座標系における肘の位置までの方向ベクトルは なので計算不要です。
を に一致させるような を求めます。
まず、 を に一致させるような回転軸 と回転角度 を求めます。
回転軸 を単位ベクトル化しておきます。
次に、回転軸 と回転角度 から回転行列 を求めます。
回転行列の導出については、任意軸まわりの回転 - ロドリゲスの回転公式 を参照してください。
最後に、回転行列 から を求めます。
成分を比較すると、以下のように求まります。
のときにジンバルロックがかかりますが、その時 は を返すので、そのままでよいと思います。
私の作成しているアプリの場合、ジンバルロックを気にするような角度になることはありません。
手首の位置から肩と肘の角度を求める
手首のワールド座標を とします。
は の平行移動成分を無くした 3x3 の行列とします。
このとき、forearm の座標系における肘の位置までの方向ベクトル は、以下のように求まります。
のときの forearm の座標系における手首の位置までの方向ベクトルは なので計算不要です。
を に一致させるような を求めます。
まず、 を に一致させるような回転軸 と回転角度 を求めます。
回転軸 を単位ベクトル化しておきます。
次に、回転軸 と回転角度 から回転行列 を求めます。
回転行列の導出については、任意軸まわりの回転 - ロドリゲスの回転公式 を参照してください。
最後に、回転行列 から を求めます。
成分を比較すると、以下のように求まります。
のとき(肘を伸ばしている)にジンバルロックがかかりますが、その時 は を返すので、そのままでよいと思います。
私の作成しているアプリの場合、ジンバルロックを気にするような角度になることはありません。
参考文献
コンピュータグラフィックスの基礎 p39-p40