【问题标题】:Skeletal Animation with Parenting Issues有育儿问题的骨骼动画
【发布时间】:2014-10-12 03:08:17
【问题描述】:

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

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

最后说明:我们的矩阵代码已针对其他实现和多项测试进行了验证。

【问题讨论】:

    标签: opengl animation 3d lua skeletal-animation


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

    附:通常,通过向下遍历树来更新所有节点。但是,您通过查看列表来更新节点。您应该知道,您必须以某种方式保证任何节点的子节点都会跟随它。

    【讨论】:

    • 首先我想说非常感谢您的帮助!当我测试你的代码时,它几乎可以工作!无论如何,比我的代码好得多!我的一个动画“blah”出现了这个奇怪的错误,我想知道这是否是因为我解析列表的方式可能导致某些骨骼在被他们的孩子使用之前无法正确更新? dl.dropboxusercontent.com/u/12958391/misc/mk2.png
    • 我看过“blah”动画,似乎腿的最顶层父级的变换是错误的。我不确定,但也许这个问题与这个奇怪的 inverse_base 有关。尝试重写代码而不乘以 inverse_base[k] 并调整您正在使用的导出工具的设置。
    • 您曾说过,有些骨骼在被子代使用之前可能不会更新。如果是这样,它肯定会破坏动画。请参阅我之前写的帖子脚本。
    • 经过更多研究,我发现 IQM 规范以线性方式写入骨骼,如果按列表顺序计算,则永远不会在父级之前计算子级。我将尝试重写一些代码以删除 inverse_base,但我会注意到官方 IQM 参考演示的代码与我编写的相同。
    猜你喜欢
    • 2017-09-30
    • 2013-09-22
    • 2012-10-17
    • 2013-12-31
    • 2015-07-21
    • 2014-10-16
    • 2014-09-23
    • 2017-05-05
    • 2013-11-03
    相关资源
    最近更新 更多