FBX SDK 2020导入蒙皮动画网格法线与Maya显示不一致
FBX SDK 2020 导入蒙皮网格法线偏差问题
问题背景
目前基于FBX SDK 2020开发FBX导入器,校验导入数据准确性的方式为:将FBX文件拖入Maya,对比Maya组件编辑器(Component Editor)中显示的数据与导入器读取结果。
当前仅针对**绑定到骨骼、带动画数据的蒙皮网格(Skinned Mesh)**出现异常:FBX SDK导入的法线数值和Maya显示值存在偏差,顶点位置(Position)数值完全匹配。
验证流程
将FBX拖入Maya后将模型调整至绑定姿势(Bind Pose),在组件编辑器中查看对应数据做对比:
- 自研FBX导入器读取的法线数据截图:

- Maya组件编辑器中同一顶点的法线数据截图:

对比可见法线值在百分位小数位存在误差,位置值基本一致。 - 删除骨骼后从Maya重新导出的新FBX文件同项数据截图:

问题复现
使用带骨骼的单位球体模型测试,可稳定复现相同问题,且该模型的法线偏差幅度更大。
以下为基于FBX SDK 2020编写的可复现问题的代码:
namespace test { FbxSample::FbxSample() { } void FbxSample::ImportMesh(FbxNode& node, int depth) { auto log = std::ofstream("C:\\Projects\\FbxSamples\\log.txt"); auto& globalTransform = node.EvaluateGlobalTransform(FBXSDK_TIME_INFINITE); FbxMesh* fbxMesh = (FbxMesh*)node.GetNodeAttribute(); int triangleCount = fbxMesh->GetPolygonCount(); auto meshControlPoints = fbxMesh->GetControlPoints(); int meshControlPointsCount = fbxMesh->GetControlPointsCount(); auto normals = fbxMesh->GetElementNormal(); int vertexCount = 0; for (int polygonId = 0; polygonId < triangleCount; ++polygonId) { int polygonSize = fbxMesh->GetPolygonSize(polygonId); for (int j = 0; j < polygonSize; ++j, ++vertexCount) { int vertexIndex = fbxMesh->GetPolygonVertex(polygonId, j); auto position = meshControlPoints[vertexIndex]; FbxVector4 normal; switch (normals->GetMappingMode()) { case FbxGeometryElement::eByControlPoint: switch (normals->GetReferenceMode()) { case FbxGeometryElement::eDirect: normal = normals->GetDirectArray().GetAt(vertexIndex); break; case FbxGeometryElement::eIndexToDirect: { int id = normals->GetIndexArray().GetAt(vertexIndex); normal = normals->GetDirectArray().GetAt(id); } break; } break; case FbxGeometryElement::eByPolygonVertex: switch (normals->GetReferenceMode()) { case FbxGeometryElement::eDirect: normal = normals->GetDirectArray().GetAt(vertexCount); break; case FbxGeometryElement::eIndexToDirect: { int id = normals->GetIndexArray().GetAt(vertexCount); normal = normals->GetDirectArray().GetAt(id); } break; } break; } FbxVector4 normal2; fbxMesh->GetPolygonVertexNormal(polygonId, j, normal2); log << "Vertex Count: " << vertexCount << " Vertex Index: " << vertexIndex << std::endl; log << "Position: X: " << position[0] << " Y: " << position[1] << " Z: " << position[2] << " W: " << position[3] << std::endl; log << "Normal: X: " << normal[0] << " Y: " << normal[1] << " Z: " << normal[2] << " W: " << normal[3] << std::endl; log << "Normal2: X: " << normal2[0] << " Y: " << normal2[1] << " Z: " << normal2[2] << " W: " << normal2[3] << std::endl; log << std::endl; } } log.flush(); log.close(); } void FbxSample::Walk(FbxNode& node, int depth) { auto nodeAttribute = node.GetNodeAttribute(); if (nodeAttribute) { FbxNodeAttribute::EType attributeType = nodeAttribute->GetAttributeType(); switch (attributeType) { case fbxsdk::FbxNodeAttribute::eMesh: ImportMesh(node, depth); break; } } for (int i = 0; i < node.GetChildCount(); i++) { Walk(*node.GetChild(i), depth + 1); } } void FbxSample::Walk(FbxScene& fbxScene) { FbxNode* node = fbxScene.GetRootNode(); if (node) { for (int i = 0; i < node->GetChildCount(); ++i) { Walk(*node->GetChild(i), 1); } } } void FbxSample::Test() { FbxManager* sdkManager = FbxManager::Create(); if (!sdkManager) { FBXSDK_printf("Error: Unable to create FBX Manager!\n"); exit(1); } else { FBXSDK_printf("Autodesk FBX SDK version %s\n", sdkManager->GetVersion()); } FbxIOSettings* ios = FbxIOSettings::Create(sdkManager, IOSROOT); ios->SetBoolProp(IMP_FBX_MATERIAL, true); ios->SetBoolProp(IMP_FBX_TEXTURE, true); ios->SetBoolProp(IMP_GEOMETRY, true); ios->SetBoolProp(IMP_FBX_MODEL, true); ios->SetBoolProp(IMP_SKINS, true); ios->SetBoolProp(IMP_FBX_ANIMATION, true); ios->SetBoolProp(IMP_FBX_GLOBAL_SETTINGS, true); sdkManager->SetIOSettings(ios); auto importer = FbxImporter::Create(sdkManager, ""); importer->Initialize("C:\\Projects\\FbxSamples\\SphereWithBones.fbx", -1, sdkManager->GetIOSettings()); auto fbxScene = FbxScene::Create(sdkManager, "Scene"); importer->Import(fbxScene); Walk(*fbxScene); } }
代码运行输出如下:
Vertex Count: 0 Vertex Index: 20 Position: X: 0.293893 Y: -0.951057 Z: -0.0954916 W: 0 Normal: X: 0.298177 Y: 0.267987 Z: -0.916119 W: 1 Normal2: X: 0.298177 Y: 0.267987 Z: -0.916119 W: 1 Vertex Count: 1 Vertex Index: 0 Position: X: 0.148778 Y: -0.987688 Z: -0.0483409 W: 0 Normal: X: 0.18352 Y: 0.228479 Z: -0.956095 W: 1 Normal2: X: 0.18352 Y: 0.228479 Z: -0.956095 W: 1 Vertex Count: 2 Vertex Index: 21 Position: X: 0.25 Y: -0.951057 Z: -0.181636 W: 0 Normal: X: 0.246448 Y: 0.378525 Z: -0.892179 W: 1 Normal2: X: 0.246448 Y: 0.378525 Z: -0.892179 W: 1
从输出结果可见,绑定姿势下的单位球体法线本应与顶点位置的X/Y/Z值接近,但当前读取到的法线值完全错误。如果删除同一FBX文件中的骨骼后重新运行上述代码,读取到的法线可与Maya中的数值正确匹配。
内容的提问来源于stack exchange,提问作者Ilan Keshet
相关产品推荐
相关产品推荐

