我正在使用 Assimp 作为我的模型导入库,在 OpenGL 中尝试进行骨骼动画。
我需要对骨骼的 offsetMatrix
变量做什么?我需要将其乘以什么?
比如说,我曾经使用以下代码在游戏中制作角色动画。我还使用了Assimp来加载骨骼信息,并且我已经阅读了Nico指出的OGL教程。
glm::mat4 getParentTransform()
{
if (this->parent)
return parent->nodeTransform;
else
return glm::mat4(1.0f);
}
void updateSkeleton(Bone* bone = NULL)
{
bone->nodeTransform = bone->getParentTransform() // This retrieve the transformation one level above in the tree
* bone->transform //bone->transform is the assimp matrix assimp_node->mTransformation
* bone->localTransform; //this is your T * R matrix
bone->finalTransform = inverseGlobal // which is scene->mRootNode->mTransformation from assimp
* bone->nodeTransform //defined above
* bone->boneOffset; //which is ai_mesh->mBones[i]->mOffsetMatrix
for (int i = 0; i < bone->children.size(); i++) {
updateSkeleton (&bone->children[i]);
}
}
GlobalTransform
,或者正确地说是根节点的变换 scene->mRootNode->mTransformation
是从局部空间到全局空间的变换。举个例子,当你在3D建模软件(比如Blender)中创建网格或加载角色时,默认情况下它通常被放置在笛卡尔平面的原点,并且旋转设置为单位四元数。
但是,您可以将网格/角色从原点 (0,0,0)
移动到其他地方,并且在单个场景中甚至可以具有具有不同位置的多个网格。当您加载它们时,特别是如果您进行骨骼动画,则必须将它们返回到局部空间(即返回到原点 0,0,0
),这就是为什么您必须通过 InverseGlobal
将所有内容乘以其逆变换(将网格返回到局部空间)的原因。
接下来,您需要将其乘以节点变换,该变换是parentTransform
(树中上一级的变换,即整体变换)和transform
(以前是assimp_node->mTransformation
,即相对于节点父级的骨骼变换)以及要应用的本地变换(任何T * R),以进行正向运动学、逆向运动学或关键帧插值。
最终,有一个boneOffset(ai_mesh->mBones[i]->mOffsetMatrix
),它在绑定姿势下从网格空间转换为骨骼空间,如文档所述。
这里有一个链接GitHub,如果您想查看我的Skeleton类的全部代码。
希望能有所帮助。
Vertex Position<0, 1, 2>
Bone Position<10, 2, 4>
Bone Rotation<0,0,0,1> // Note - no rotation
Bone Scale<1, 1, 1>
anim_vertex = (offset_matrix * anim_matrix) * mesh_vertex
这有帮助吗?
正如我之前所想的那样,mOffsetMatrix
是反向绑定姿势矩阵。这个教程说明了您需要进行线性混合蒙皮的正确变换:
首先,您需要评估动画状态。这将为每个骨骼提供从动画骨骼空间到世界空间的系统变换(教程中的GlobalTransformation
)。mOffsetMatrix
是从世界空间到绑定姿势骨骼空间的系统变换。因此,对于蒙皮,您需要执行以下操作(假设特定顶点受单个骨骼影响):使用mOffsetMatrix
将顶点转换到骨骼空间。现在假设一个动画骨骼,并将中间结果从动画骨骼空间转换回世界空间。所以:
boneMatrix[i] = animationMatrix[i] * mOffsetMatrix[i]
vec4 result = vec4(0);
for each influencing bone i
result += weight[i] * boneMatrix[i] * vertexPos;
for
循环。m_GlobalInverseTransform
来处理 boneMatrix
。然而,我不知道他们为什么这样做。基本上,这会撤销整个场景的总体变换。可能它被用来将模型居中在视图中。No assume an animated bone and transform...
。你能否重写一下?另外,如果我没记错的话,由于每个网格的顶点都存储在它们的_node space_中,所以boneMatrix
应该是(顺序从右到左):finalBoneMatrix = Inverse(offsetMatrix) * animationMatrix * offsetMatrix * meshNode.toRoot;
..? 文字顺序为:节点空间到根空间(也称世界空间),世界空间到骨骼空间,骨骼空间到动画(骨骼)空间,动画空间到根空间。我有什么地方说错了吗? - McLovinw
没有了。你在骨骼空间中拥有顶点位置,然后通过假设动画骨骼来继续转换到世界空间。有两个层次结构:骨架层次结构和网格层次结构。如果网格层次结构包含其他变换,则需要像您编写的那样添加它们。但这对于蒙皮网格而言相当不寻常。第一个 Inverse(offsetMatrix)
是错误的。这将把顶点从骨骼空间转换到世界空间。但是,在使用 animationMatrix
进行转换后,您已经处于世界空间中了。 - Nico SchertleranimationMatrix
基本上只是我从动画结构中获取的基于时间戳的T/R矩阵。为什么它会将顶点转换为世界空间? - McLovin
mOffsetMatrix
是反向绑定骨骼矩阵,你需要它来计算动画网格的实际骨骼变换。你还需要从动画中获取变换(可能是boneTransform = animTransform * mOffsetMatrix
)。这些都是每个骨骼的矩阵,你需要将它们全部上传到GPU进行换肤。如果没有动画,boneTransform
就简化为单位矩阵。 - Nico Schertler