.x(Direct X)ファイルからスケルトンアニメーションを適用するにはどうすればよいですか?


8

.x形式を使用してBlenderからモデルをエクスポートすると、メッシュ、アーマチュア、アニメーションをロードできます。ゲームでメッシュを生成してモデルを表示しても問題はありません。さらに、アニメーションとアーマチュアが適切なデータ構造に適切に読み込まれています。

私の問題は、アニメーションをモデルに適切に適用することです。モデルを適用するためのフレームワークと、アニメーションを選択してフレームをステップ実行するためのコードがあります。

私が理解していることからAnimationKeyAnimationSetサプライ内のは、バインドポーズをアニメーションフレーム内のポーズに変換する変換を提供します。小さな例として:

Animation {
  {Armature_001_Bone}
  AnimationKey { 
    2; //Position
    121; //number of frames
    0;3;     0.000000, 0.000000, 0.000000;;,
    1;3;     0.000000, 0.000000, 0.005524;;,
    2;3;     0.000000, 0.000000, 0.022217;;,
    ...
  }
  AnimationKey { 
    0; //Quaternion Rotation 
    121;
    0;4;   -0.707107, 0.707107, 0.000000, 0.000000;;,
    1;4;   -0.697332, 0.697332, 0.015710, 0.015710;;,
    2;4;   -0.684805, 0.684805, 0.035442, 0.035442;;,
    ...
  }
  AnimationKey { 
    1; //Scale
    121;
    0;3;     1.000000, 1.000000, 1.000000;;,
    1;3;     1.000000, 1.000000, 1.000000;;,
    2;3;     1.000000, 1.000000, 1.000000;;,
    ...
  }
}

したがって、フレーム2を適用するには、フレーム2から位置、回転、スケールを取得し、Transform_Aそれらから変換行列(と呼びます)を作成し、その行列をArmature_001_Bone重みで制御される頂点に適用します。だから私はTransformA私のシェーダーに詰め込み、頂点を変換します。何かのようなもの:

vertexPos = vertexPos * bones[ int(bfs_BoneIndices.x) ] * bfs_BoneWeights.x;

どこbfs_BoneIndicesbfs_BoneWeights現在の頂点に固有の値です。

メッシュの頂点をロードするときに、rootTransformとによってそれらを変換しますmeshTransform。これにより、バインドポーズを表示するために、向きとスケーリングが正しく行われます。

問題は、その変換マトリックスを(アニメーションの位置、回転、スケールを使用して)作成すると、頂点が適切に変換されないことです。アニメーションデータを使用するだけではありません。また、ボーントランスフォーム階層を適用しようとしましたが、まだダイスはありません。基本的に私はいくつかのねじれたモデルになってしまいます。また、私はopenGLで作業しているため、適用する必要がある可能性のある行列の転置についても検討する必要があります。

.xアニメーションをモデルに適用するために、どのデータが必要で、どのように組み合わせるのですか?

これが役立つ場合に備えて、これがどのように見えるかをいくつかモックアップしました。

最初に翻訳をテストしたかったのですが、これはボブヘッドで、Blenderでは次のようになります。

http://i.stack.imgur.com/NAc4B.gif

そして、それはゲームでどのように見えるか(色を気にしないでください):

http://i.stack.imgur.com/nj2du.gif

次に、回転だけの場合、アニメーションは頭が垂直軸を中心に360度回転します。これはゲームでは次のようになります。

http://i.stack.imgur.com/gVyUW.gif

チルトはなく、メリーゴーランドのような回転のみであることに注意してください。

更新

アニメーションの翻訳部分を動かしています。しかし、それは一種のハックに感じられ、ローテーションに適用する方法がわかりません。

翻訳は次の手順で機能します。

  1. アニメーションフレームから位置を取得しyz値を入れ替えます
  2. 変更された位置で変換行列を変換します
  3. 変換行列を転置する
  4. 頂点に変換行列を適用する

それそれがどのように機能するかですが、位置、スケール、回転に対して一般的にどのように機能するはずですか?


2
スキニングを適用する前に、逆バインドポーズマトリックスで頂点を変換しましたか?
r2d2rigo 2012

私は持っています、それは効果がありますが、それは望ましいものではありません。これは、モデルの視覚的表現に影響を与えます(すべての法線を反転させたように見えます)が、移動/回転/スケールには影響しません。
MichaelHouse

問題を示すスクリーンショットを投稿していただけませんか?
r2d2rigo 2012

@ r2d2rigoアニメーションGIFで更新しました。
MichaelHouse

1
ここでは主にブレーンストーミングを行っていますが、ベクトルの順序(xyz、zxy)が.xとopenglで異なる場合は、面白い効果が得られます。転置が役立つということは、位置に列(または行)ベクトルを使用することを意味します。その逆ではありません。en.wikipedia.org/wiki/…には、2Dの回転に関する説明があります。四元数を変更する方法はよくわかりません。または、.x形式とopenGL形式の違い
Daniel Carlsson

回答:


3

また、私はopenGLで作業しているため、適用する必要がある可能性のある行列の転置についても検討する必要があります。

多分それが鍵です、DirectX左手座標からOpenGL右手座標に変換するためにいくつかのZ座標を無効にしてみましたか?

変換を適用する前にすべてのZを否定し、完了したら再度否定して、出力が改善されるかどうかを確認できます。

編集

Y座標とZ座標の入れ替えは、利き手を変更する別の方法です。したがって、変換を調整する必要があります。詳細についてはこのペーパーを確認してください

行列を転置する必要があるという事実は、行優先から列優先に、またはその逆に移動することも意味します。これは処理がそれほど複雑ではないはずです。おそらく、パイプラインでそれを行うための適切な「場所」を見つける必要があるだけです。私は推測しているだけですが、数学ライブラリがこのオプションを提供している場合、四元数でそれを行うと、それらを行列に変更し、転置し、四元数に戻すことができます。


私がここですでに知っていることを私に話しているようなものです
マイケルハウス

1
さて、あなたはあなたの質問で利き手を述べていませんでした、そして私はそれがあなたの問題の少なくとも一部であると思います。DirectXとOpenGLがこの点に関して異なる規則に従っていることを知っている、または知らないとは思いません(これは実際には少し複雑です)。これは、この質問を読んでいる他の人が言及を確認するのに役立つ可能性があることに注意してください。これは、このWebサイト全体の要点の1つです。
Laurent Couvidou 2012

十分に公正です、あなたは正しいです。OpenGLを使用することについて述べたときに、必要な変換と発生する必要のある転置について知っているという事実だけをほのめかしました。
MichaelHouse

2

OK、私はこれにもっと時間をかけ、それを複数の骨に対して機能させ、ハックのようなものはかなり少なくしました(まだ少しですが)。私が元々持っていたシステムはほぼ正しいものでした。ただし、主な問題は次のとおりです。

  1. ブレンダーは、クォータニオンをW、X、Y、Z形式で保存します。X、Y、Z、Wをロードしていました
  2. クォータニオンをマトリックスに変換するためのコードが正しくありませんでした。なぜこれが正しくないのかはまだわかりませんが、Quaternionクラスの一部として、次のコードが機能することはわかっています。

    public Matrix4f toMatrix() {
        Matrix4f tmpMatrix = new Matrix4f();
        if (this.length() > 0)
            this.normalise();
    
        float xx = this.x * this.x;
        float xy = this.x * this.y;
        float xz = this.x * this.z;
        float wx = this.x * this.w;
    
        float yy = this.y * this.y;
        float yz = this.y * this.z;
        float wy = this.y * this.w;
    
        float zz = this.z * this.z;
        float wz = this.z * this.w;
        //note the "* -1"s
        //I needed those to make the same matrices Blender was expecting
        tmpMatrix.m00 = (1.0f - 2.0f * (yy + zz)) * -1;
        tmpMatrix.m01 = (       2.0f * (xy - wz)) * -1;
        tmpMatrix.m02 =        2.0f * (xz + wy);
        tmpMatrix.m03 = 0;
    
        tmpMatrix.m10 =        2.0f * (xy + wz);
        tmpMatrix.m11 = 1.0f - 2.0f * (xx + zz);
        tmpMatrix.m12 = (       2.0f * (yz - wx)) * -1;
        tmpMatrix.m13 = 0;
    
        tmpMatrix.m20 =        2.0f * (xz - wy);
        tmpMatrix.m21 =        2.0f * (yz + wx);
        tmpMatrix.m22 = (1.0f - 2.0f * (xx + yy)) * -1;
        tmpMatrix.m23 = 0;
        return tmpMatrix;
    }

それ以外はそれだけです:

finalMatrix = boneMatrix * skinMatrix * invertedBindMatrix;

boneMatrixは、バインドマトリックスの代わりに、各ボーンのアニメーションマトリックスを使用する必要があることに注意してください。また、次のようにすべての親行列を取得する必要があります。

private Matrix4f getBoneMatrix() {
        if (parent == null) {
            return Matrix4f.mul(Matrix4f.mul(parentArmature.getBindMatrix(), parentArmature.getArmatureRootMatrix(), null), boneMatrix, null);
        } else {
            return Matrix4f.mul(parent.getBoneMatrix(), boneMatrix, null);
        }
    }

そしてそれは機能します。説明が必要な場合はコメントを残してください。


私はこれを私のブログで動作させることについてのいくつかのより一般的な情報を書きました:byte56.com/2012/12/the-blender-connection.html
MichaelHouse
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.