gpu蒙皮的矩阵计算
Matrix calculations for gpu skinning
我正在尝试使用 Assimp 作为我的模型导入库在 OpenGL 中制作骨骼动画。
骨骼的 offsetMatrix
变量到底需要什么?我需要乘以什么?
偏移矩阵定义了在网格space中变换顶点的变换(平移、缩放、旋转),并将其转换为"bone"space。作为示例,请考虑以下顶点和具有以下属性的骨骼;
Vertex Position<0, 1, 2>
Bone Position<10, 2, 4>
Bone Rotation<0,0,0,1> // Note - no rotation
Bone Scale<1, 1, 1>
在这种情况下,如果我们将顶点乘以偏移矩阵,我们将得到顶点位置 <-10, -1, 2>。
我们如何使用它?关于如何使用这个矩阵,你有两个选择,这取决于我们如何将顶点数据存储在顶点缓冲区中。选项是;
1) 将网格顶点存储在网格中space
2) 将网格顶点存储在骨骼 space
在#1 的情况下,我们将采用 offsetMatrix 并将其应用于在构建顶点缓冲区时受骨骼影响的顶点。然后当我们为网格设置动画时,我们稍后会为该骨骼应用动画矩阵。
在#2 的情况下,在转换存储在顶点缓冲区中的顶点时,我们将结合使用 offsetMatrix 和该骨骼的动画矩阵。所以它会是这样的(注意:你可能必须在这里切换矩阵连接);
anim_vertex = (offset_matrix * anim_matrix) * mesh_vertex
这有帮助吗?
正如我已经假设的那样,mOffsetMatrix
是逆绑定姿势矩阵。 This tutorial 说明线性混合蒙皮所需的正确变换:
您首先需要评估您的动画状态。这将为您提供从动画骨骼 space 到每个骨骼的世界 space 的系统转换(教程中的 GlobalTransformation
)。 mOffsetMatrix
是从世界 space 到绑定姿势骨骼 space 的系统变换。因此,蒙皮操作如下(假设特定顶点受单个骨骼影响):将顶点转换为骨骼 space 和 mOffsetMatrix
。现在假设一个动画骨骼并将中间结果从动画骨骼 space 转换回世界 space。所以:
boneMatrix[i] = animationMatrix[i] * mOffsetMatrix[i]
如果顶点受多个骨骼的影响,LBS 只是对结果进行平均。这就是重量发挥作用的地方。蒙皮通常在顶点着色器中实现:
vec4 result = vec4(0);
for each influencing bone i
result += weight[i] * boneMatrix[i] * vertexPos;
通常,影响骨骼的最大数量是固定的,您可以展开for
循环。
本教程使用额外的 m_GlobalInverseTransform
作为 boneMatrix
。但是,我不知道他们为什么这样做。基本上,这会取消整个场景的整体转换。可能是用来让模型在视图中居中的。
让我们以这段代码为例,我用它来为我开发的游戏中的角色设置动画。我也使用 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]);
}
}
本质上是教程 Skeletal Animation with Assimp 中提到的 GlobalTransform
或根节点 scene->mRootNode->mTransformation
的正确转换是从局部 space 到全局 space。举个例子,当您在 3D 建模器(例如我们选择 Blender)中创建网格或加载角色时,它通常(默认情况下)位于笛卡尔平面的原点,并且其旋转设置为身份四元数。
但是您可以 translate/rotate 您的 mesh/character 从原点 (0,0,0)
到其他地方,并且在单个场景中甚至可以有多个具有不同位置的网格。当你加载它们时,特别是如果你做骨骼动画,必须将它们翻译回本地 space (即回到原点 0,0,0
),这就是为什么你必须将所有内容乘以InverseGlobal
(将您的网格恢复到本地 space)。
之后,您需要将其乘以节点变换,即 parentTransform
(树中向上一层的变换,这是整体变换)与 transform
(以前的 assimp_node->mTransformation
只是骨骼相对于节点父节点的变换)和您要应用的局部变换(任何 T * R):正向运动学、反向运动学或关键帧插值。
最终有 boneOffset (ai_mesh->mBones[i]->mOffsetMatrix
) 在绑定姿势中从网格 space 转换为骨骼 space,如文档中所述。
这里有 link 到 GitHub 如果你想看我的骨架的完整代码 class。
希望对您有所帮助。
我正在尝试使用 Assimp 作为我的模型导入库在 OpenGL 中制作骨骼动画。
骨骼的 offsetMatrix
变量到底需要什么?我需要乘以什么?
偏移矩阵定义了在网格space中变换顶点的变换(平移、缩放、旋转),并将其转换为"bone"space。作为示例,请考虑以下顶点和具有以下属性的骨骼;
Vertex Position<0, 1, 2>
Bone Position<10, 2, 4>
Bone Rotation<0,0,0,1> // Note - no rotation
Bone Scale<1, 1, 1>
在这种情况下,如果我们将顶点乘以偏移矩阵,我们将得到顶点位置 <-10, -1, 2>。
我们如何使用它?关于如何使用这个矩阵,你有两个选择,这取决于我们如何将顶点数据存储在顶点缓冲区中。选项是;
1) 将网格顶点存储在网格中space 2) 将网格顶点存储在骨骼 space
在#1 的情况下,我们将采用 offsetMatrix 并将其应用于在构建顶点缓冲区时受骨骼影响的顶点。然后当我们为网格设置动画时,我们稍后会为该骨骼应用动画矩阵。
在#2 的情况下,在转换存储在顶点缓冲区中的顶点时,我们将结合使用 offsetMatrix 和该骨骼的动画矩阵。所以它会是这样的(注意:你可能必须在这里切换矩阵连接);
anim_vertex = (offset_matrix * anim_matrix) * mesh_vertex
这有帮助吗?
正如我已经假设的那样,mOffsetMatrix
是逆绑定姿势矩阵。 This tutorial 说明线性混合蒙皮所需的正确变换:
您首先需要评估您的动画状态。这将为您提供从动画骨骼 space 到每个骨骼的世界 space 的系统转换(教程中的 GlobalTransformation
)。 mOffsetMatrix
是从世界 space 到绑定姿势骨骼 space 的系统变换。因此,蒙皮操作如下(假设特定顶点受单个骨骼影响):将顶点转换为骨骼 space 和 mOffsetMatrix
。现在假设一个动画骨骼并将中间结果从动画骨骼 space 转换回世界 space。所以:
boneMatrix[i] = animationMatrix[i] * mOffsetMatrix[i]
如果顶点受多个骨骼的影响,LBS 只是对结果进行平均。这就是重量发挥作用的地方。蒙皮通常在顶点着色器中实现:
vec4 result = vec4(0);
for each influencing bone i
result += weight[i] * boneMatrix[i] * vertexPos;
通常,影响骨骼的最大数量是固定的,您可以展开for
循环。
本教程使用额外的 m_GlobalInverseTransform
作为 boneMatrix
。但是,我不知道他们为什么这样做。基本上,这会取消整个场景的整体转换。可能是用来让模型在视图中居中的。
让我们以这段代码为例,我用它来为我开发的游戏中的角色设置动画。我也使用 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]);
}
}
本质上是教程 Skeletal Animation with Assimp 中提到的 GlobalTransform
或根节点 scene->mRootNode->mTransformation
的正确转换是从局部 space 到全局 space。举个例子,当您在 3D 建模器(例如我们选择 Blender)中创建网格或加载角色时,它通常(默认情况下)位于笛卡尔平面的原点,并且其旋转设置为身份四元数。
但是您可以 translate/rotate 您的 mesh/character 从原点 (0,0,0)
到其他地方,并且在单个场景中甚至可以有多个具有不同位置的网格。当你加载它们时,特别是如果你做骨骼动画,必须将它们翻译回本地 space (即回到原点 0,0,0
),这就是为什么你必须将所有内容乘以InverseGlobal
(将您的网格恢复到本地 space)。
之后,您需要将其乘以节点变换,即 parentTransform
(树中向上一层的变换,这是整体变换)与 transform
(以前的 assimp_node->mTransformation
只是骨骼相对于节点父节点的变换)和您要应用的局部变换(任何 T * R):正向运动学、反向运动学或关键帧插值。
最终有 boneOffset (ai_mesh->mBones[i]->mOffsetMatrix
) 在绑定姿势中从网格 space 转换为骨骼 space,如文档中所述。
这里有 link 到 GitHub 如果你想看我的骨架的完整代码 class。
希望对您有所帮助。