从 FBX 加载蒙皮信息
Posted
技术标签:
【中文标题】从 FBX 加载蒙皮信息【英文标题】:Loading skinning information from FBX 【发布时间】:2012-11-14 00:48:29 【问题描述】:我正在尝试将 FBX 文件的解析器编写为可在我的游戏引擎中使用的自定义模型格式,但我目前正忙于提取每个骨骼所需的矩阵。我想我也可能迷失在剥皮理论中......
我已成功提取网格的骨骼层次结构,并将变形集群的权重和索引附加到我的顶点。现在我需要提取(两个?)我需要在我的引擎中进行蒙皮的矩阵。
基本上,在我的格式中,我也希望它看起来像这样:
//骨骼ID
//ParentBoneID
//矩阵1
//矩阵2
现在,到矩阵... 我是否正确假设其中至少一个应该是“绑定姿势矩阵”,即我的骨架的默认位置?如何从 FBX 中提取它?这个矩阵对于整个网格是否均匀?
至于第二个,我真的不知道,我试过查看 ViewScene 和 ImportScene 示例,但我不明白。我读了一些关于“局部空间矩阵”的东西,我猜这是每个骨骼的单独位置、旋转和缩放?
感谢任何帮助或建议,我现在不知所措,可能是因为我对此视而不见。几乎到了放弃 FBX 转而支持 COLLADA 的地步。
Edit1:引擎还没有绘图功能,因为我想在继续之前完成这项工作。我找到了一个我认为我理解的例子,也许这里有人可以确认它是否正确。
//TEST CODE
//This lFbxLinkMatrix is the skeleton's transform when the binding happened.
//It is the same as the matrix in bindpose if the bindpose is complete.
// Multiply lClusterGlobalInitPosition by Geometric Transformation
FbxAMatrix clusterGlobalInitPosition;
cluster->GetTransformLinkMatrix(clusterGlobalInitPosition);
FbxAMatrix clusterGeometry = GetGeometry(cluster->GetLink());
clusterGlobalInitPosition *= clusterGeometry;
skeleton->at(boneListPosition).bindMatrix = clusterGlobalInitPosition;
// Compute the shift of the link relative to the reference.
//lVertexTransformMatrix = RGCP.Inverse() * CGCP * CGIP.Inverse() * RGIP * RG;
// CGCP = position of bone
// RGCP = mesh position
FbxAMatrix offsetMatrix;
FbxNode* boneNode = cluster->GetLink();
FbxAMatrix CGIP, RGIP, vertexTransformMatrix;
cluster->GetTransformLinkMatrix(CGIP);
cluster->GetTransformMatrix(RGIP);
vertexTransformMatrix = CGIP.Inverse() * RGIP;
skeleton->at(boneListPosition).localBoneMatrix = vertexTransformMatrix;
所以基本上,当我想计算我的动画时,我得到网格世界矩阵的逆矩阵,将它与代表我的动画帧的矩阵相乘,乘以我的绑定矩阵的逆矩阵,该骨骼的父绑定矩阵和最终父变换矩阵?
【问题讨论】:
【参考方案1】:首先,你需要知道mesh的原始顶点位置不在世界空间中。 (例如:你会看到你的角色在 3ds max 中站起来,但如果你直接导出网格数据,它会躺下。)
同样的事情也发生在骨架树的所有变换节点上。这意味着,即使只有一个具有单位矩阵的骨骼,网格也需要通过 FBX 场景的根节点进行变换,然后是骨骼节点的局部矩阵。 (因为根节点也是骨骼节点的父节点),
顺便说一句,如果您使用函数 FbxAxisSystem::ConvertScene() 来转换网格的轴系,那么该操作也仅适用于根节点的变换矩阵,而不适用于网格的顶点。
要正确计算顶点的位置,需要找到网格所属的FbxNode,然后调用其EvaluateGlobalTransform()函数得到全局变换矩阵,并用它来变换网格顶点的位置。 (法线的逆转置矩阵。)
我不知道您在代码的函数 GetGeometry() 中究竟做了什么:
FbxAMatrix clusterGeometry = GetGeometry(cluster->GetLink());
但这是我获取骨骼绑定姿势矩阵的方法:
FbxAMatrix BoneBindPoseMatrix;
pCluster->GetTransformLinkMatrix(BoneBindPoseMatrix).
为了获取骨骼的节点,我也使用FbxNode* pBoneNode = pCluster->GetLink()
,调用pBoneNode->EvaluateLocalTransform( CurrentTime )
获取骨骼当前时间的局部变换矩阵。但是我做了一件特别的事情:记得我是通过根节点的全局变换矩阵来转换网格的吧?所以对于骨架的根节点,我需要通过调用得到它的全局变换矩阵
FbxAMatrix BoneInitPoseGlobalTransform = pBoneRootNode->EvaluateGlobalTransform()
,
然后反转它。而我的根骨骼的局部变换矩阵将是:BoneLocalTransform = InvBoneInitPoseGlobalTransform * BoneLocalTransform;
我们只需要对骨骼的根节点进行处理即可。
当为皮肤设置动画时,我遍历整个骨架树,对于每个骨骼节点,我通过以下方式计算其全局变换矩阵:BoneGlobalTransform = BoneLocalTransform * ParentBoneGlobalTransform;
但是在将矩阵传递到着色器之前,我需要做:BlendMatrix = Bone.InvBindPoseMatrix * BoneGlobalTransform;
Don'别忘了我已经通过根节点转换了顶点的位置。
就是这样,上面的代码在 D3D 和 OpenGL 下都运行良好。我希望它可以帮助你。 :D
【讨论】:
虽然这个答案非常有用;它没有回答提取原始转换信息的问题。调用 EvaluateLocalTransform(currentTime) 需要 FBX SDK,但作者想知道是否可以检索原始“皮肤”信息,以便他们能够编写自己的 EvaluateLocalTransform 函数。您知道 FBX SDK 是否可以做到这一点? 对不起,我可能误解了这个问题。是的,您可以自己计算局部变换,但这非常复杂。首先,您需要检索与下列节点属性绑定的所有动画曲线:LclTranslation、LclRotation、PreRotation、PostRotation、RotationPivot、RotationOffset、LclScaling、ScalingPivot、ScalingOffset。 然后你需要把它们变成矩阵,并乘以它们的顺序:InvScalPivot * Scal * ScalPivot * ScalOffset * InvRotaPivot * PostRota * Rota * PreRota * RotaPivot * RotaOffset * Translation。您可以搜索类 FbxProperty 和 FbxAnimCurve 了解更多信息。 这个答案对我帮助很大(搜索轴转换),fbx 的文档记录真的很糟糕!找到做某事的正确方法很痛苦。 @SeaStar 似乎您正在描述 Maya 风格的 FBX 矩阵( [Sp]x[S]x[Sh]x[Sp]x[St]x[Rp]x[Ro]x[R ]x[Rp]x[Rt]x[T] ),但在 3DS Max 等其他 3d 建模工具中,世界变换只有 (ParentWorldTransform * T * R * S * OT * OR * OS)。以上是关于从 FBX 加载蒙皮信息的主要内容,如果未能解决你的问题,请参考以下文章
Vue2+ThreeJS踩坑记录fbx蒙皮网格模型挂载ShaderMaterial材质之后,挂载动画无效的解决办法
Vue2+ThreeJS踩坑记录fbx蒙皮网格模型挂载ShaderMaterial材质之后,挂载动画无效的解决办法
Unity3d VFX 采用skinned mesh sampling(蒙皮采样)发射粒子效果时fbx模型是否优化(Optimized)带来的问题记录