骨骼动画中的父子关系问题

tl;dr: 当模型进行动画时,每个关节的移动都正确,但是不相对于其父关节。

我正在使用自定义的 IQE 加载程序和 Lua 渲染器开发一个骨骼动画系统。现在几乎一切都运行正常,但是在动画时,骨架似乎是分离的。每个关节都可以正确地移动、旋转和缩放,但是没有考虑其父关节的位置,从而导致一些可怕的问题。

就 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 二进制文件(和批处理文件)。

最后说明:我们的矩阵代码已经通过其他实现和多个测试进行了验证。

点赞
用户2081094
用户2081094

父骨骼的变换应影响其子骨骼的变换。实现这一点的方法是,在其父的框架中指定特定骨骼的变换。因此,通常骨骼的变换指定在其本地坐标系中,该坐标系取决于其父级。如果任何一个父级被转换,这个变换会影响所有的子级,即使它们的本地变换没有改变。

在您的情况下,您曾经缓存每个节点的绝对(精确到根节点)变换。然后您使用缓存更新每个节点的本地变换,并且不更新您的缓存。那么,如果在更新孩子时使用缓存而不是实际的父变换,节点的本地变换如何影响它的子级?

还有一个问题。为什么您要这样做?

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.通常,所有节点都通过遍历树来更新。但是,您通过列表更新节点。您应该意识到,您必须以某种方式保证对于任何节点,它的子级会在它之后。

2014-10-11 12:16:47