OpenGL 中的 FBX SDK 导入器和 LH 坐标系统
Posted
技术标签:
【中文标题】OpenGL 中的 FBX SDK 导入器和 LH 坐标系统【英文标题】:FBX SDK importer and LH coordinates system in OpenGL 【发布时间】:2017-05-04 19:26:49 【问题描述】:我们正在使用 OpenGL 开发渲染器。其中一项功能是能够导入 FBX 模型。导入器模块使用 FBX SDK (2017)。 现在,众所周知,OpenGL是一个右手坐标系。即向前是从正到负,右是右,上向量是向上。在我们的应用程序中,前向向量的要求是正的,类似于DirectX。 在应用程序级别,我们通过将投影矩阵的 Z 缩放为 -1 来设置它。 使用 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 模型。
如果使用
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中默认的逆时针缠绕顺序)
如果使用 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());
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 游戏引擎进行比较。
现在,这是我第一次被要求执行这样的 hack。感觉就像是一个非常讨厌的 hack。特别是当涉及到蒙皮网格时,我们需要使用转换矩阵转换骨骼矩阵、姿势矩阵和什么不...
问题是,在这种情况下,当我们需要保持 CCW 缠绕顺序并在 OpenGL 中具有正向前进时。这是正确进行几何变换的唯一方法吗?
(3d模型源为3DsMax,Y-up导出)
【问题讨论】:
"现在,众所周知,OpenGL 是一个右手坐标系。" 我们知道吗?我很确定 OpenGL 使用你喜欢的任何坐标系。 @NicolBolas,当然!然后我需要调整几何形状以使其看起来正确,或更改缠绕顺序。顺便说一句,如果我在我的情况下更改为 CW,则网格沿 X 轴翻转。 转换左坐标右坐标总是涉及轴翻转和缠绕顺序反转,通过改变缠绕顺序状态或翻转几何中的索引。这里没有什么新东西,GL/DX 也不是什么,因为我们使用着色器,唯一的要求是在单位立方体中提供投影顶点(唯一的 GL 和 DX 不同,它们在它的可见 Z 范围内有所不同)。 @galop1n 所以你只需确认我正在做的是“官方”的方式吗? 【参考方案1】:我在 OpenGL 中完成的大部分编程都使用了左手系统,而不是 DirectX 的右手系统。 OpenGL 与 DirectX 的区别在于 OpenGL 没有内置的相机对象。您必须创建/提供自己的相机,通过这样做,您可以根据在大多数情况下设置存储矩阵的方式将坐标系设置为 RHC 或 LHC,默认设置为 LHC。 DirectX 和 OpenGL 之间的另一个主要区别是,当您移动或旋转相机时,一旦您使用 DirectX 将场景和相机置于 3D 空间中;真正移动的是相机,就像在 OpenGL 中一样,它不是为它移动的相机是固定的,而是整个场景相对于相机移动。因此,说明转换的最简单方法是了解或了解多个矩阵计算的操作,并了解您的 MVP(模型 - 视图 - 投影)矩阵的顺序。从 RHC 到 LHC 的转换应该在打开和加载模型文件后完成并预先计算,然后存储到您的模型矩阵中。然后,一旦你有了合适的模型矩阵,其余的 MVP 计算应该是准确的。
这些将是基本步骤
打开模型文件,提取所需或想要的数据并将内容加载到特定于应用程序的自定义数据结构中 如有必要,从一个坐标系转换到另一个坐标系并保存回自定义结构。 使用自定义结构创建 MVP,并使用所需的着色器通过批处理将它们发送到显卡 为每个帧缓冲区将对象渲染到屏幕。这里有一些关于在一个坐标系和另一个坐标系之间转换的非常好的读物:
Geometrictools techart3d从 RHC 转换为 LHC 时最简单的事情是,只要 X 是左右,Y 是上下,你需要做的就是否定每个 z 坐标,但这只是好的用于平移而不是旋转。现在,如果某个应用程序具有 RHC 系统并且您正在使用 LHC 系统,但该应用程序保存了模型数据,其中说它们的向上向量是 Z,它们的输入和输出向量是 Y,那么您首先需要将每个 Y 与 Z 交换,然后您需要在交换后否定新的 Z,这对只有平移而不是旋转很好。
这是转换的基本公式:
令 M0 为给定的 4x4 变换矩阵。
让 M1 是一个额外的 4x4 仿射变换矩阵,它从您想要的左手坐标系映射到您当前拥有的右手坐标系。
那么总变换 = inv(M1) * M0*M1
可以从这里找到:mathworks:mathlab *数学或公式在任一方向上的转换都是相同的。
【讨论】:
以上是关于OpenGL 中的 FBX SDK 导入器和 LH 坐标系统的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 FBX SDK 在 fbx 文件中加载嵌入的纹理? [关闭]