2

tl;dr: モデルをアニメートするとき、各ジョイントは正しく動きますが、親ジョイントに対しては動きません。

ここに画像の説明を入力

Lua でカスタム構築された IQE ローダーとレンダラーを使用して、スケルトン アニメーション システムに取り組んでいます。この時点では、アニメーション時にスケルトンがバラバラに見えることを除いて、ほぼすべてが機能しています。各関節は正しく移動、回転、スケーリングしますが、親の位置を考慮していないため、ひどい問題が発生します。

IQM の仕様とデモを参照しても、何がうまくいかないのか一生わかりません。私の Lua コードは (私が知る限り) リファレンス C++ と同じです。

ベース ジョイント マトリックスの計算:

local base = self.active_animation.base
local inverse_base = self.active_animation.inverse_base

for i, joint in ipairs(self.data.joint) do
    local pose = joint.pq

    local pos = { pose[1], pose[2], pose[3] }
    local rot = matrix.quaternion(pose[4], pose[5], pose[6], pose[7])
    local scale = { pose[8], pose[9], pose[10] }

    local m = matrix.matrix4x4()
    m = m:translate(pos)
    m = m:rotate(rot)
    m = m:scale(scale)

    local inv = m:invert()

    if joint.parent > 0 then
        base[i] = base[joint.parent] * m
        inverse_base[i] = inv * inverse_base[joint.parent]
    else
        base[i] = m
        inverse_base[i] = inv
    end
end

アニメーション フレーム マトリックスの計算

local buffer = {}
local base = self.active_animation.base
local inverse_base = self.active_animation.inverse_base
for k, pq in ipairs(self.active_animation.frame[self.active_animation.current_frame].pq) do
    local joint = self.data.joint[k]
    local pose = pq

    local pos = { pose[1], pose[2], pose[3] }
    local rot = matrix.quaternion(pose[4], pose[5], pose[6], pose[7])
    local scale = { pose[8], pose[9], pose[10] }

    local m = matrix.matrix4x4()
    m = m:translate(pos)
    m = m:rotate(rot)
    m = m:scale(scale)

    local f = matrix.matrix4x4()

    if joint.parent > 0 then
        f = base[joint.parent] * m * inverse_base[k]
    else
        f = m * inverse_base[k]
    end

    table.insert(buffer, f:to_vec4s())
end

完全なコードは、詳細な調査のためにここにあります。関連するコードは /libs/iqe.lua にあり、関数 IQE:buffer() および IQE:send_frame() の下部近くにあります。このコードは LOVE ゲーム フレームワークのカスタム バージョンで実行され、Windows バイナリ (およびバッチ ファイル) が含まれています。

最後の注意: 私たちのマトリックス コードは、他の実装といくつかのテストに対して検証されています。

4

1 に答える 1

2

親ボーンの変換は、その子の変換に影響する必要があります。実際、これは、特定のボーンの変換をその親のフレームで指定することによって実現されます。そのため、通常、ボーンの変換は、その親に依存するローカル座標系で指定されます。親のいずれかが変換された場合、ローカル変換が変更されていなくても、この変換はすべての子に影響します。

あなたの場合、各ノードのすべての絶対的な(正確にはルートに相対的な)変換を一度キャッシュします。次に、キャッシュを使用して各ノードのローカル トランスフォームを更新し、キャッシュを更新しません。では、子を更新するときに実際の親トランスフォームの代わりにキャッシュを使用すると、ノードのローカル トランスフォームの変更がその子にどのように影響するのでしょうか?

もう1つ問題があります。なぜあなたは次のことをしますか?

f = base[joint.parent] * m * inverse_base[k]

つまり、通常は次のようになります。

f = base[joint.parent] * m

アニメーションに記録された変換は絶対的なものだと思います (正確には、ルートに対して)。とても奇妙です。通常、すべての変換はローカルです。これにより多くの問題が追加されるため、この問題を確認してください。

さらに、あなたの場合、何かをキャッシュする必要はないと思います(inverse_baseを除いて、通常は必要ありません)。

IQE:send_frame()関数を次のように変更します。

local buffer = {}
local transforms = {}
local inverse_base = self.active_animation.inverse_base
for k, pq in ipairs(self.active_animation.frame[self.active_animation.current_frame].pq) do
    local joint = self.data.joint[k]
    local pose = pq

    local pos = { pose[1], pose[2], pose[3] }
    local rot = matrix.quaternion(pose[4], pose[5], pose[6], pose[7])
    local scale = { pose[8], pose[9], pose[10] }

    local m = matrix.matrix4x4()
    m = m:translate(pos)
    m = m:rotate(rot)
    m = m:scale(scale)

    local f = matrix.matrix4x4()

    if joint.parent > 0 then
        transforms[k] = transforms[joint.parent] * m
        f = transforms[k] * inverse_base[k]
    else
        f = m  * inverse_base[k]
        transforms[k] = m
    end

    table.insert(buffer, f:to_vec4s())
end

これは私にとってはうまくいきます。inverse_baseを取り除こうとすると、 IQE:buffer()関数からすべてのアニメーション関連のコードを削除できます。

PS 通常、すべてのノードはツリーをたどって更新されます。ただし、リストを調べてノードを更新します。どのノードでも、その子がそのノードの後に​​移動することを何らかの形で保証する必要があることに注意してください。

于 2014-10-11T12:16:47.723 に答える