从 *.FBX 文件读取和显示动画

Posted

技术标签:

【中文标题】从 *.FBX 文件读取和显示动画【英文标题】:Read and display animations from *.FBX file 【发布时间】:2016-12-21 10:54:55 【问题描述】:

我想从fbx 文件中读取 3d 模型并将其显示在 iPhone 上的 openGL es 2.0 引擎中(不使用 Unity),并且我还想显示读取 3d 对象的动画。

如何从fbx 文件中获取动画?

据我所知,目前我能够获取姿势名称列表,其中包含转换矩阵、图层、堆栈、图层和许多曲线中姿势的完整列表。

如何组合所有信息以显示正确的动画?

我也尝试解析TakeInfo中的一些信息,但结果对我来说有点奇怪,例如:

    FbxTakeInfo *ltakeInfo  =  pScene->GetTakeInfo(lAnimStack->GetName());
    FbxTime start = ltakeInfo->mLocalTimeSpan.GetStart();
    FbxTime end = ltakeInfo->mLocalTimeSpan.GetStop();

    self.startTime = start.GetSecondDouble();
    self.endTime = end.GetSecondDouble();

这里我得到了每个解析层的start = 0end = 0.014,所以我猜这是不正确的(我要显示的fbx 文件包含1 个网格和简单的5 秒持续时间动画)。


更新

经过几个小时的调查,我得到了以下信息:

作为参考,这是我要显示的测试 obj 的结构:

在这里你可以看到很多骨头(更具体 - 19) 我能够(如上所述)在列表中获得 19 个动画 obj(如骨骼/obj 的名称)和 19 组曲线,每组 151 帧(帧速率为 30 正好 5 秒的动画。- 30*5=150 + 1 个最后一个单位矩阵)。

如果我尝试将每个曲线组一个一个地用于我的网格(我只能解析 1 个网格),我会看到应用于所有网格的网格不同部分的动画(例如垂直旋转或水平平移),所以我认为每组中的这条曲线应该完全应用于特定的骨骼,因此我将为我的网格获得动画。问题是我不知道如何只为选定的骨骼顶点部分设置动画。

现在的问题是如何将所有动画分成特定于骨骼的单独组到整个 obj(因为我只有一个网格)?

如何从包含所有曲线组的列表中为每个帧获取 1 个全局曲线列表?


更新2

感谢@codetiger 的建议,我遵循评论中提供的链接中的说明,使用该技术,我能够检索具有开始和结束时间以及所需变换的骨骼特定垫的列表,但这与我之前得到的几乎相同与曲线 - 与曲线的唯一区别是我应该从 9 条曲线创建垫子(平移/缩放/旋转 xyz)而不是使用完整矩阵,但问题仍然存在 - 我如何将它们组合到 1 个全局矩阵中?

我使用的代码(找到几个链接):

FbxNode* modelNode = _fbxScene->GetRootNode();
FbxAMatrix geometryTransform = GetGeometryTransformation(modelNode);
for (unsigned int deformerIndex = 0; deformerIndex < numOfDeformers; ++deformerIndex) 
    FbxSkin* currSkin = reinterpret_cast<FbxSkin*>(mesh->GetDeformer(deformerIndex, FbxDeformer::eSkin));
    if (!currSkin) 
        continue;
    
    unsigned int numOfClusters = currSkin->GetClusterCount();
    for (unsigned int clusterIndex = 0; clusterIndex < numOfClusters; ++clusterIndex) 
        FbxCluster* currCluster = currSkin->GetCluster(clusterIndex);
        std::string currJointName = currCluster->GetLink()->GetName();

        FbxAMatrix transformMatrix;
        FbxAMatrix transformLinkMatrix;
        FbxAMatrix globalBindposeInverseMatrix;

        currCluster->GetTransformMatrix(transformMatrix);   
        currCluster->GetTransformLinkMatrix(transformLinkMatrix);   
        globalBindposeInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix * geometryTransform;

        FbxAnimStack* currAnimStack = _fbxScene->GetSrcObject<FbxAnimStack>(0);
        FbxString animStackName = currAnimStack->GetName();
        char *mAnimationName = animStackName.Buffer();
        FbxTakeInfo* takeInfo = _fbxScene->GetTakeInfo(animStackName);
        FbxTime start = takeInfo->mLocalTimeSpan.GetStart();
        FbxTime end = takeInfo->mLocalTimeSpan.GetStop();
        FbxLongLong mAnimationLength = end.GetFrameCount(FbxTime::eFrames24) - start.GetFrameCount(FbxTime::eFrames24) + 1;

        for (FbxLongLong i = start.GetFrameCount(FbxTime::eFrames24); i <= end.GetFrameCount(FbxTime::eFrames24); ++i) 
            FbxTime currTime;
            currTime.SetFrame(i, FbxTime::eFrames24);

            FbxAMatrix currentTransformOffset = modelNode->EvaluateGlobalTransform(currTime) * geometryTransform;
            FbxAMatrix mat = currentTransformOffset.Inverse() * currCluster->GetLink()->EvaluateGlobalTransform(currTime);

        
    

在这里我收到 121 矩阵而不是 151 - 但是某些矩阵变换的持续时间超过 1 帧绘制的持续时间,所以我认为这里的 q-ty 也是正确的

float duration = end.GetSecondDouble() - start.GetSecondDouble(); //return 5 as and expected

我猜Autodesk SDK 有一种方法可以让每个 drawCall 获得 1 个全局变换

有什么建议吗?这可能吗?


为任何能够描述如何使用 Autodesk SDK 在 openGLES 2.0 中在 iPhone 上显示动画的人添加奖励...(抱歉,错字而不是 Facebook


我能得到什么

blender中的原始对象

如果我分别绘制骨骼(相同的 VBO 具有不同的变换,并且每个骨骼只有索引)

【问题讨论】:

请查看此链接gamedev.stackexchange.com/questions/59419/… 我看到了这个链接 - 尝试使用描述的方法获取垫子,但我收到的所有东西 - 151 个垫子(fps 30 意味着 5 秒 - 正确) - 是单位矩阵......但应该是我了解了具有特定开始和结束时间的每个网格的全局变换垫 我还尝试在来自 Autodesk 的 Blender 和 FBX Review 应用程序中播放文件 - 一切正常......堆栈和图层的使用怎么样 - 你能建议如何处理这些项目吗?跨度> 对不起,我的知识仅限于从FBX读取骨骼和其他信息,对动画数据没有太多经验。让我们等待有人回应。 你从文件中读取动画信息了吗?赏金是给你关于如何在 ios 上渲染动画的信息? 【参考方案1】:

这是在 OpenGL ES 中渲染动画网格的方法。这将为您提供有关您需要从文件中读取哪些详细信息的票价信息。

方法 1:(GPU 蒙皮)

此方法仅适用于基于硬件功能的有限数量的骨骼。通常这取决于您可以发送到着色器的矩阵数量。

使用 BindAttributes 将网格信息绑定到 GPU 一次,并将矩阵发送到制服中的着色器。

第 1 步:读取所有骨骼矩阵并创建一个矩阵数组,并将此数据以制服的形式发送到着色器。

第2步:在顶点着色器中,像下面的着色器一样计算gl_position

attribute vec3 vertPosition;
attribute vec3 vertNormal;
attribute vec2 texCoord1;
attribute vec3 vertTangent;
attribute vec3 vertBitangent;
attribute vec4 optionalData1;
attribute vec4 optionalData2;

uniform mat4 mvp, jointTransforms[jointsSize];

void main()

    mat4 finalMatrix;

    int jointId = int(optionalData1.x);
    if(jointId > 0)
        finalMatrix = jointTransforms[jointId - 1] * optionalData2.x;

    jointId = int(optionalData1.y);
    if(jointId > 0)
        finalMatrix = finalMatrix + (jointTransforms[jointId - 1] * optionalData2.y);

    jointId = int( optionalData1.z);
    if(jointId > 0)
        finalMatrix = finalMatrix + (jointTransforms[jointId - 1] * optionalData2.z);

    jointId = int( optionalData1.w);
    if(jointId > 0)
        finalMatrix = finalMatrix + (jointTransforms[jointId - 1] * optionalData2.w);

    gl_Position = mvp * finalMatrix * vec4(vertPosition, 1.0);

在这个着色器中,我在 optionalData1 和 optionalData2 属性中发送了权重绘制信息。数据是这样打包的:(BoneId1, BoneId2, BoneId3, BoneId4) 和 (Weight4Bone1, Weight4Bone2​​, Weight4Bone3, Weight4Bone4)。因此,在这种情况下,您最多只能有 4 个骨骼影响每个属性。片段着色器部分是常用的。

方法 2:(CPU 剥皮)

如果您无法忍受 GPU 蒙皮的限制,那么唯一的方法就是在 CPU 端进行计算。

第 1 步:通过将属于影响顶点的骨骼的矩阵相乘来计算当前帧中的顶点位置。

第 2 步:收集当前帧的新位置,并在 Vertex Attributes 中将信息发送到 GPU。

第 3 步:重新计算每一帧中的新顶点位置并更新属性缓冲区。

这种方法将计算的负载带到了 CPU 上。

【讨论】:

我想我遵循第二种方法,但不是通过直接获取矩阵,而是通过从图层获取动画曲线并从该曲线创建(曲线就像 tx ty tz sx sy sz rx ry rz)矩阵进行变换。 .. 当我尝试使用来自GetTransformMatrix 和其他函数的集群中的度量时 - 在屏幕上我看到一些奇怪的东西...... 我接受你的回答 - 因为这是一个很好的开始,但老实说,我希望任何人都面临与我相同的问题,并且可以更详细地建议如何使用从 fbx 中提取的骨骼制作动画文件....

以上是关于从 *.FBX 文件读取和显示动画的主要内容,如果未能解决你的问题,请参考以下文章

使用 fbx sdk c++ 从 fbx 读取纹理文件名

unity 3d发布的exe程序可以从外部(比如同文件夹下的一个fbx文件)读取模型到场景中吗?

如何正确读取 DirectX 的 FBX 2014 索引?

为啥我无法从搅拌机导出 fbx 文件中的动画?

如何从 2018 FBX SDK 中获取反向绑定姿势矩阵?

动画骨骼模型文件格式fbx