无法使用 OpenGL 渲染 FBX 导入的纹理模型

Posted

技术标签:

【中文标题】无法使用 OpenGL 渲染 FBX 导入的纹理模型【英文标题】:Failing to render a FBX imported textured model with OpenGL 【发布时间】:2019-10-09 08:32:11 【问题描述】:

我正在尝试使用带有 OpenGL 的 FBX 文件中的数据来渲染纹理模型,但纹理坐标错误。

FBX 文件给出的模型数据摘要包括映射到模型顶点的纹理参考的 UV 坐标。

顶点数:19895

PolygonVertexIndices 数量:113958

UV 数量:21992

UVIndices 数量:113958

很明显,模型有 113958 个感兴趣的顶点。据我了解,“PolygonVertexIndices”指向“顶点”,“UVIndices”指向“UV”值。

我使用带有 GL_TRIANGLES 的 glDrawElements 来渲染模型,使用“PolygonVertexIndices”作为 GL_ELEMENT_ARRAY_BUFFER,“顶点”作为 GL_ARRAY_BUFFER。模型本身可以正确渲染,但纹理效果不佳。

由于我对 GL_ELEMENT_ARRAY_BUFFER 使用“PolygonVertexIndices”,据我了解,对于 UV 坐标的属性数组也会发生相同的索引。我不认为 OpenGL 可以使用导出的 UV 索引,因此我为大小为 113958 的 UV 值创建了一个新缓冲区,其中包含与“PolygonVertexIndices”相对应的相关 UV 值。

即对于 [0:113958] 中的顶点 i,我会这样做

new_UVs[PolygonVertexIndices[i]] = UVs[UVIndices[i]]

然后将new_UVs绑定为UV坐标属性数组。

但是,纹理显然是错误的。我的思路有问题吗?

我觉得我在使用 OpenGL 的索引渲染 glDrawElements 时误解了如何使用 UV 缓冲区。扩展我的 UV 缓冲区以将顶点数匹配到 113958 也感觉不对,因为 glDrawElements 的优势应该是保存重复的顶点值,并且 UV 缓冲区可能包含重复项。

执行索引并将“顶点”和“UV”扩展为 113958 大小并在性能方面简单地使用 glDrawArrays 会更好吗?

感谢任何想法/想法/建议。

【问题讨论】:

【参考方案1】:

你说得对,OpenGL 只支持一个索引缓冲区。

您的 UV 分配代码不正确。您不能只复制 UV,因为顶点和 UV 阵列具有不同的大小。您需要做的是为具有多个 UV 的顶点创建重复顶点,并为每个副本分配一个 UV。

将 UV 和顶点坐标视为包含两者的单个结构并使用它。 示例:

struct Vertex

   float3 position;
   float2 UV;
;

std::vector<Vertex> vertices;
// Fill "vertices" here.

这还允许您轻松地交错数据并将整个结果数组上传到一个 VBO 并进行渲染。

Would it be better to performing the indexing and expand both "Vertices" and "UVs" to be of size 113958 and simply use glDrawArrays in terms of performance?

这不是性能问题。这实际上是唯一的方法。

【讨论】:

【参考方案2】:

上一篇文章仅适用于 4.3 之前的 OpenGL。如果您有 4.3+,那么完全可以为顶点使用多个索引(尽管它可能不是渲染几何图形的最有效方式 - 这取决于您的用例)。

首先,您需要在顶点着色器中将顶点和 texcoord 索引指定为不同的顶点参数,例如

in int vs_vertexIndex;
in int vs_uvIndex;

确保使用 glVertexArrayAttribIFormat 指定这些参数(注意:'I' 很重要!另外请注意,glVertexAttribIFormat 也可以使用)。

下一步是将顶点和 UV 数组绑定为 SSBO 的

layout(std430, binding = 1) buffer vertexBuffer

    float verts[];
;
layout(std430, binding = 2) buffer uvBuffer

    float uvs[];
;

现在在您的 main() 中,您可以像这样提取正确的 vert + uv:

vec2 uv = vec2(uvs[2 * vs_uvIndex], 
               uvs[2 * vs_uvIndex + 1]);
vec4 vert = vec4(verts[3 * vs_vertexIndex], 
                 verts[3 * vs_vertexIndex + 1], 
                 verts[3 * vs_vertexIndex + 2], 1.0);

此时跳过 glDrawElements,只使用 glDrawArrays 代替(其中顶点数是网格中的索引数)。

我并不是说这是 最快 方法(构建您自己的索引元素可能是最高效的)。然而,在某些情况下,这可能很有用 - 通常当您拥有 FBX/USD/Alembic 等格式的数据,并且需要更新(例如)每帧的顶点和法线时。

【讨论】:

以上是关于无法使用 OpenGL 渲染 FBX 导入的纹理模型的主要内容,如果未能解决你的问题,请参考以下文章

unity - 将纹理导入 fbx 文件

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

opengl 纹理无法正确渲染

无法从 OpenGL 中的着色器访问先前渲染的纹理

使用现代 OpenGL 渲染纹理

我的渲染技术进阶之旅关于ARCore的标准人脸3D模型canonical_face_mesh.fbx和2D面部网格参考纹理canonical_face_mesh.psd文件