2009年06月23日

CCD-IKについて

多自由度のIKを計算するためには数値計算で求めるためにヤコビアンとかやらないといけないかなと思いましたが、参考サイトのPMDファイルのフォーマットを見てみると、IK1値が再帰計算回数ということですのでCCD-IKやParticle-IKなど、再帰計算するアルゴリズムだと思われます。IK2値はとりあえず置いておいてCCD-IKを試してみたいと思います。

参考文献
http://blog.goo.ne.jp/torisu_tetosuki/e/445cbbbe75c4b2622c22b473a27aaae9
http://www.tmps.org/index.php?CCD-IK%20and%20Particle-IK
http://monsho.hp.infoseek.co.jp/dx/dx48.html

実行結果



参考サイトを見るとわかりやすいですが、面白い計算方法ですね。実装も簡単そうですし。
// 注目ジョイント、エフェクタのジョイント番号、目標座標、繰り返し回数、残り繰り返しボーン数、最大ボーン数
void CFigure::ccdIKSolver(CJoint *joint, int effector, btVector3 &targetPos, int numIteration, int numBones, int numMaxBones)
{
// エフェクタのワールド座標
btVector3 effectorPos = m_vecJoints[effector]->m_vWorldPosition;
// 注目ジョイントのワールド座標
btVector3 jointPos = joint->getParent()->m_vWorldPosition;

// 注目ジョイントからの座標系に変換するための行列
btMatrix3x3 invCoord = joint->getParent()->m_mWorldRotation.inverse();

// ターゲット座標をローカル座標系に変換
btVector3 localEffectorPos = invCoord * (effectorPos - jointPos);
btVector3 localTargetPos = invCoord * (targetPos - jointPos);

// (1) 基準関節→エフェクタ位置への方向ベクトル
btVector3 basis2Effector = (localEffectorPos).normalized();
// (2) 基準関節→目標位置への方向ベクトル
btVector3 basis2Target = (localTargetPos).normalized();

// (1)を(2)の位置に向ける回転行列の計算
float rotationDotProduct = basis2Target.dot(basis2Effector);
float rotationAngle = acos(rotationDotProduct);
// 角度が小さい時は処理しない
if (rotationAngle > 1.0e-5f)
{
// 外積が回転軸
btVector3 rotationAxis = basis2Effector.cross(basis2Target);
rotationAxis.normalize();
// 回転軸と回転角度からクォータニオンを生成
btQuaternion rotationQt;
rotationQt.setRotation(rotationAxis, rotationAngle);
// クォータニオンから回転行列を生成
btMatrix3x3 rotationMatrix;
rotationMatrix.setRotation(rotationQt);
// 注目ジョイントの姿勢を更新
joint->getParent()->m_mLocalRotation *= rotationMatrix;
// 注目ジョイントからすべての子ジョイントの位置を更新
updateAllPosition(joint->getParent());
}

// 再帰/終了処理
numBones--;
if (numBones == 0)
{
numIteration--;
// 繰り返し回数繰り返したら終了
if (numIteration == 0)
{
return;
}
// ボーンの数とエフェクタのジョイントに初期化して再帰計算
numBones = numMaxBones;
ccdIKSolver(m_vecJoints[effector], effector, targetPos, numIteration, numBones, numMaxBones);
}
else
{
// エフェクタからボーンの数だけ親ジョイントに向かって再帰計算
ccdIKSolver(joint->getParent(), effector, targetPos, numIteration, numBones, numMaxBones);
}
}
この関数もほぼ参考サイトそのままですね。困ったものです。しかしまぁこれで1リンク(足先)でも2リンク(足)でも3リンク(ネクタイ)でも5リンク(髪)でもIKできるようになりました。結局IK値2を使っていないのが気になりますが・・・。

次はPMDファイルから読み込んだIK情報を使ってVMDファイルを再生ですね。うまく動くといいですが・・・。
posted by シンドラー at 21:37| Comment(0) | TrackBack(0) | OpenGL & Metasequoia | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください

この記事へのトラックバック
×

この広告は90日以上新しい記事の投稿がないブログに表示されております。