OpenGL中的FBX SDK导入器和LH坐标系

时间:2016-12-19 17:16:40

标签: opengl directx fbx

我们正在使用OpenGL开发渲染器。其中一个功能是导入FBX模型。导入器模块使用FBX SDK(2017)。 现在,众所周知,OpenGL是一个右手坐标系。前进是从正到负,右是向上向上向上。在我们的应用中要求前向向量为正,类似于DirectX。 在应用程序级别,我们通过将投影矩阵的Z缩放为-1来设置它。 使用glm math:

glm::mat4 persp = glm::perspectiveLH (glm::radians (fov), width / height, mNearPlane, mFarPlane);

与此相同:

glm::mat4 persp = glm::perspective (glm::radians (fov), width / height, mNearPlane, mFarPlane);
persp = glm::scale (persp , glm::vec3 (1.0f, 1.0f, -1.0f));

到目前为止一直很好。有趣的部分来自我们导入FBX模型。

如果使用

FbxAxisSystem :: OpenGL.ConvertScene(pSceneFbx);

然后使用Node的全局变换转换顶点,该变换的计算方式如下:

 FbxSystemUnit fbxUnit = node->GetScene()->GetGlobalSettings().GetSystemUnit();
    FbxMatrix globalTransform = node->EvaluateGlobalTransform();
    glm::dvec4 c0 = glm::make_vec4((double*)globalTransform.GetColumn(0).Buffer());
    glm::dvec4 c1 = glm::make_vec4((double*)globalTransform.GetColumn(1).Buffer());
    glm::dvec4 c2 = glm::make_vec4((double*)globalTransform.GetColumn(2).Buffer());
    glm::dvec4 c3 = glm::make_vec4((double*)globalTransform.GetColumn(3).Buffer());

几何面被反转。 (OpenGL中的默认CCW绕线顺序)

如果使用DirectX转换器:

  FbxAxisSystem::DirectX.ConvertScene(pSceneFbx);

模型倒置并倒置翻转。

OpenGL转换:

enter image description here

DirectX转换:

enter image description here

我们发现解决了这个问题,就是否定了该矩阵中3列的Z.并且还绕Z轴旋转了180度。否则模型的前面就是它的背面(是的,声音很棘手,但它在比较OpenGL和DirectX坐标系差异时有意义。

所以,整个“转换”矩阵现在看起来像这样:

  FbxSystemUnit fbxUnit = node->GetScene()->GetGlobalSettings().GetSystemUnit();
    FbxMatrix globalTransform = node->EvaluateGlobalTransform();
    glm::dvec4 c0 = glm::make_vec4((double*)globalTransform.GetColumn(0).Buffer());
    glm::dvec4 c1 = glm::make_vec4((double*)globalTransform.GetColumn(1).Buffer());
    glm::dvec4 c2 = glm::make_vec4((double*)globalTransform.GetColumn(2).Buffer());
    glm::dvec4 c3 = glm::make_vec4((double*)globalTransform.GetColumn(3).Buffer());

   glm::mat4    mat =
    glm::mat4(1, 0, 0, 0,
              0, 1, 0, 0,
              0, 0, -1, 0,//flip z to get correct mesh direction  (CCW)
              0, 0, 0, 1);
//in this case the model faces look right dir ,but the model
//itself needs to be rotated because the camera looks at it from the
//wrong direction.
    mat = glm::rotate(mat, glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f));

    glm::mat4 convertMatr = glm::mat4(c0, c1, c2, c3)  *mat;

然后,用该矩阵变换FBX模型的顶点得到所需的结果:

enter image description here

顺便问一下,我们怎么知道结果是否合适?我们将它与Unity3D游戏引擎进行比较。

现在,这是我第一次被要求执行这样的黑客攻击。感觉就像是一个非常讨厌的黑客。特别是当涉及到蒙皮网格时,我们需要使用转换矩阵转换骨骼矩阵,姿势矩阵和什么不......

问题是,在这种情况下,当我们需要保持CCW缠绕顺序并在OpenGL中有正向前进时。这是使几何变换正确的唯一方法吗?

(3d模型源是3DsMax,使用Y-up导出。)

1 个答案:

答案 0 :(得分:0)

我在OpenGL中完成的大部分编程都使用左手系统而不是DirectX的右手系统。关于OpenGL与DirectX的理解是OpenGL没有内置的相机对象。您必须创建/提供自己的相机,通过这样做,您可以将坐标系设置为RHC或LHC,具体取决于您在大多数情况下设置存储矩阵的默认设置是LHC。 DirectX和OpenGL之间的另一个主要区别是,当您移动相机或旋转相机时,使用DirectX将场景和相机放在3D空间中;它是实际移动的相机,就像在OpenGL中一样,移动的相机不是固定的,而是相对于相机移动的整个场景。因此,说明转换的最简单方法是理解或了解多个矩阵计算的操作,并了解MVP(模型 - 视图 - 投影)矩阵的顺序。在模型文件中打开和加载后,应完成从RHC到LHC的转换并预先计算,然后将其存储到模型矩阵中。然后,一旦你有了合适的模型矩阵,其余的MVP计算应该是准确的。

这些将是基本步骤

  • 打开模型文件,提取所需或需要的数据并将内容加载到特定于应用程序的自定义数据结构中
  • 如有必要,可以从一个坐标系转换为另一个坐标系并保存回自定义结构。
  • 使用自定义结构创建MVP并使用所需着色器通过批处理将其发送到图形卡
  • 将对象渲染到每个帧缓冲区的屏幕。

以下是关于在一个坐标系与另一个坐标系之间进行转换的一些非常好的读物:

从RHC转换为LHC并且反之亦然最简单的事情是,只要X左右,Y上下,你需要做的就是否定每个z坐标,但这只是好的翻译而不是轮换。现在,如果某个应用程序有一个RHC系统并且您正在使用LHC系统,但该应用程序保存了模型数据,其中说它们的向量为Z且它们的输入和输出向量为Y,那么您首先需要交换每个Y使用Z然后你需要在交换后否定新的Z,再次这只是翻译而不是旋转。

以下是转化的基本公式:

  

设M0为给定的4x4变换矩阵。

     

设M1是一个额外的4x4仿射变换矩阵,它从你想要的左手坐标系映射到你当前的右手坐标系。

     

然后总变换= inv(M1)* M0 * M1

可以在这里找到:mathworks:mathlab *数学或公式在任一方向都是相同的转换。