13

私は自分のCOLLADAインポーターの作成に取り組んでいます。メッシュやマテリアルなどをロードして、かなり遠くまで到達しました。しかし、私はアニメーション、特に関節の回転に問題を抱えています。

メッシュのスキニングに使用している式は単純です。

weighted;
for (i = 0; i < joint_influences; i++)
{
    weighted += 
        joint[joint_index[i]]->parent->local_matrix * 
        joint[joint_index[i]]->local_matrix * 
        skin->inverse_bind_pose[joint_index[i]] * 
        position * 
        skin->weight[j];
}
position = weighted;

そして、文献に関する限り、これは正しい公式です。現在、COLLADAは、ローカルとグローバルの2種類の関節の回転を指定しています。ジョイントのローカル変換を取得するには、回転を連結する必要があります。

COLLADAドキュメントで区別されていないのは、ジョイントのローカル回転とジョイントのグローバル回転です。しかし、私が見たほとんどのモデルでは、ローテーションはrotate(グローバル)またはjointOrient(ローカル)のいずれかのIDを持つことができます。

グローバルローテーションを無視してローカルローテーションのみを使用すると、モデルのバインドポーズが取得されます。しかし、ジョイントのローカル変換にグローバルローテーションを追加すると、奇妙なことが起こり始めます。

これは、グローバルローテーションを使用しない場合です。

バインドポーズ

そして、これはグローバルローテーションです。

変

どちらのスクリーンショットでも、線を使用してスケルトンを描画していますが、最初はジョイントがメッシュの内側にあるため、スケルトンは表示されていません。2番目のスクリーンショットでは、頂点がいたるところにあります。

比較のために、これは2番目のスクリーンショットどのように見えるかです:

Colladaビューア

見づらいですが、2番目のスクリーンショットで関節が正しい位置にあることがわかります。

しかし、今は奇妙なことです。COLLADAで指定されている逆バインドポーズを無視し、代わりにジョイントの親ローカル変換にジョイントのローカル変換を掛けたものを逆にすると、次のようになります。

ここに画像の説明を入力してください

このスクリーンショットでは、各頂点から影響力のあるジョイントまで線を引いています。数式が次のようになるため、バインドポーズを取得するという事実はそれほど奇妙ではありません。

world_matrix * inverse_world_matrix * position * weight

しかし、COLLADAの逆バインドポーズが間違ったスペースにあるのではないかと思われます。

だから私の質問は:COLLADAはどのスペースでその逆バインドポーズを指定しますか?そして、どうすれば逆バインドポーズを必要なスペースに変換できますか?

4

2 に答える 2

12

自分の値を、Assimp (オープン ソースのモデル ローダー) から読み取った値と比較することから始めました。コードをステップ実行して、バインド マトリックスと逆バインド マトリックスが作成された場所を調べました。

最終的にSceneAnimator::GetBoneMatricesは、以下を含む になりました。

// Bone matrices transform from mesh coordinates in bind pose to mesh coordinates in skinned pose
// Therefore the formula is offsetMatrix * currentGlobalTransform * inverseCurrentMeshTransform
for( size_t a = 0; a < mesh->mNumBones; ++a)
{
    const aiBone* bone = mesh->mBones[a];
    const aiMatrix4x4& currentGlobalTransform
        = GetGlobalTransform( mBoneNodesByName[ bone->mName.data ]);
    mTransforms[a] = globalInverseMeshTransform * currentGlobalTransform * bone->mOffsetMatrix;
}

globalInverseMeshTransformメッシュは何も変換しないため、常に同一です。currentGlobalTransformバインド マトリックスであり、ジョイントのローカル マトリックスと連結されたジョイントの親のローカル マトリックスです。そしてmOffsetMatrix、スキンから直接得られる逆バインド マトリックスです。

私はこれらの行列の値を自分のものとチェックしました (ああ、ウォッチ ウィンドウでそれらを比較しました)。それらはまったく同じで、おそらく 0.0001% ずれていましたが、それは重要ではありません。では、式は同じなのに、Assimp のバージョンは機能し、私のバージョンは機能しないのはなぜですか?

これが私が得たものです:

逆さ!

Assimp がマトリックスを最終的にスキニング シェーダーにアップロードすると、次の処理が行われます。

helper->piEffect->SetMatrixTransposeArray( "gBoneMatrix", (D3DXMATRIX*)matrices, 60);

ちょっと待って。彼らはそれらを転置してアップロードしますか?それほど簡単ではありませんでした。とんでもない。

ここに画像の説明を入力

うん。

私が間違っていた何か:スキニング マトリックスを適用する前に、座標を正しいシステム (センチメートルからメートル) に変換していました。マトリックスは元の座標系用に設計されているため、完全に歪んだモデルになります。

未来のグーグル社員

  • すべてのノード変換 (回転、平行移動、スケールなど) を受け取った順に読み取ります。
  • それらをジョイントのローカル マトリックスに連結します。
  • ジョイントの親を取り、それをローカル マトリックスで乗算します。
  • それをbind matrixとして保存します。
  • スキン情報を読みます。
  • ジョイントの逆バインド ポーズ マトリックスを保存します。
  • 各頂点のジョイントウェイトを保存します。
  • バインド マトリックス逆バインド ポーズ マトリックスを掛けて転置しスキニング マトリックスと呼びます。
  • スキニング マトリックス位置とジョイントウェイトを乗算し、それを重み付けされた位置に加算します。
  • 加重位置を使用してレンダリングします。

終わり!

于 2011-10-02T06:28:44.687 に答える