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

OpenGL中的FBX SDK导入器和LH坐标系,opengl,directx,fbx,Opengl,Directx,Fbx,我们正在使用OpenGL开发渲染器。其中一个功能是能够导入FBX模型。导入器模块使用FBX SDK(2017)。 众所周知,OpenGL是一个右手坐标系。正方向是从正到负,右方向是右方向,上方向向量是向上的。在我们的应用程序中,要求正方向向量,类似于DirectX。 在应用层,我们通过用-1缩放投影矩阵的Z来设置它。 使用glm数学: glm::mat4 persp = glm::perspectiveLH (glm::radians (fov), width / height, mNearPl

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

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模型时

如果使用

OpenGL.ConvertScene(pSceneFbx)

然后使用节点的全局变换变换变换顶点,其计算如下:

 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());
  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;
几何体面是反转的。(OpenGL中的默认CCW缠绕顺序)

如果使用DirectX转换器:

  FbxAxisSystem::DirectX.ConvertScene(pSceneFbx);
模型是倒置和翻转的

OpenGL转换:

DirectX已转换:

我们所发现的解决这个问题的方法是,对矩阵中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());
  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模型的顶点将得到所需的结果:

顺便说一句,我们如何知道结果是理想的?我们将其与Unity3D游戏引擎进行比较

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

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


(三维模型源为3DsMax,使用Y-up导出。)

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

这些将是基本步骤

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

当从RHC转换到LHC和visa转换到LHC时,最简单的方法是只要X是左右的,Y是上下的,你需要做的就是对每个z坐标求反,但这只适用于平移,而不适用于旋转。现在,如果某个应用程序有RHC系统,而你使用的是LHC系统,但该应用程序有它的模式l数据保存在上方向向量为Z,进出方向向量为Y的位置,然后首先需要用Z交换每个Y,然后需要在交换后对新Z求反,这同样适用于仅平移而不旋转

以下是转换的基本公式:

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

假设M1是一个附加的4x4仿射变换矩阵,它从您想要的左手坐标系映射到您当前拥有的右手坐标系

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


从这里可以找到:*数学或公式在两个方向上的转换是相同的。

“现在,我们都知道,OpenGL是右手坐标系。”我们知道吗?我很确定OpenGL使用您喜欢的任何坐标系。@Nicolas,当然!然后我需要调整几何体,使其看起来正确,或者更改缠绕顺序。顺便说一句,如果在我的情况下更改为CW,网格将沿X轴翻转。转换左-右坐标始终涉及轴翻转和缠绕顺序反转翻转缠绕顺序状态,或翻转几何体中的索引。这里没有什么新内容,GL/DX也没有什么新内容,因为我们使用着色器,唯一的要求是在单位立方体中提供投影顶点(唯一的GL和DX差异,它们在可见的Z范围内变化)。@galop1n所以您只需确认我所做的是“正式的”还有路要走吗?