父母问题的骨骼动画

时间:2014-10-11 09:28:38

标签: opengl animation 3d lua skeletal-animation

tl; dr:在为模型设置动画时,每个关节都会正确移动,但不会相对于其父关节。

enter image description here

我正在使用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

完整代码为here以供进一步检查。相关代码位于/libs/iqe.lua中,位于函数IQE:buffer()和IQE:send_frame()的底部附近。此代码在自定义版本的LOVE游戏框架上运行,并包含Windows二进制文件(和批处理文件)。

最后注意:我们的矩阵代码已经过其他实现和多项测试的验证。

1 个答案:

答案 0 :(得分:3)

父骨骼的转换应该影响他们孩子的转变。实际上,这是通过在其父母的框架中指定特定骨骼的变换来实现的。因此,通常在其局部坐标系中指定骨骼的变换,这取决于它的父亲。如果任何父母转变,这种转变会影响所有的孩子,即使他们的地方转变没有改变。

在您的情况下,您曾经缓存每个节点的所有绝对(相对于根,准确)转换。然后使用缓存更新每个节点的本地转换,并且不更新缓存。那么,如果更新子节点时使用缓存而不是实际的父变换,节点的局部变换如何影响它的子节点呢?

还有一个问题。为什么要做以下事情?

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()函数中删除所有与动画相关的代码

P.S。通常,通过遍历树来更新所有节点。但是,您可以通过列表更新节点。你应该知道,你必须保证,对于任何一个节点,它的孩子都会追求它。