1
votes

I'm trying to render a textured model using data from a FBX file with OpenGL but the texture coordinates are wrong.

A summary of the model data given by a FBX file includes UV coordinates for texture referencing that are mapped to the model vertices.

  • Number of Vertices: 19895
  • Number of PolygonVertexIndices: 113958

  • Number of UVs: 21992

  • Number of UVIndices: 113958

It's pretty clear that the model has 113958 vertices of interest. To my understanding, the "PolygonVertexIndices" point to "Vertices" and the "UVIndices" point to "UV" values.

I am using glDrawElements with GL_TRIANGLES to render the model, using the "PolygonVertexIndices" as GL_ELEMENT_ARRAY_BUFFER and the "Vertices" as GL_ARRAY_BUFFER. The model itself renders correctly but the texturing is way off.

Since I'm using "PolygonVertexIndices" for GL_ELEMENT_ARRAY_BUFFER, it is to my understanding that the same indexing will happen for the attribute array for the UV coordinates. I don't think OpenGL can use the exported UV indices, so I make a new buffer for UV values of size 113958 which contains the relevant UV values corresponding to the "PolygonVertexIndices".

I.e. for a vertex i in [0:113958], I do

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

and then bind new_UVs as the UV coordinate attribute array.

However, the texturing is clearly all wrong. Is my line of thinking off?

I feel like I'm misunderstanding how to work with UV buffers when using OpenGL's indexed rendering glDrawElements. It also feels wrong to expand my UV buffer to match the number of vertices to 113958 since the advantage glDrawElements should be to save on duplicate vertex values and the UV buffer will likely contain duplicates.

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?

Any thoughts/ideas/suggestions are appreciated.

2

2 Answers

2
votes

You are correct that OpenGL only supports one index buffer.

Your UV assignment code is incorrect. You can't just copy the UVs, as the vertex and UV arrays have different sizes. What you need to do is create duplicate vertices for the ones that have multiple UVs and assign a UV to each copy.

Think of the UVs and vertex coordinates as a single struct containing both and work with that. Example:

struct Vertex
{
   float3 position;
   float2 UV;
};

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

This also allows you to easily interleave the data and upload the whole resulting array into one VBO and render it.

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?

That is not a question of performance. It is literally the only way.

0
votes

The previous post only applies to OpenGL prior to 4.3. If you have 4.3+, then it is entirely possible to use multiple indices for the verts (although it might not be the most efficient way to render the geometry - it depends on your use case).

First you need to specify the vertex and texcoord indices as varying vertex params in your vertex shader, e.g.

in int vs_vertexIndex;
in int vs_uvIndex;

Make sure you specify those params with glVertexArrayAttribIFormat (NOTE: the 'I' is important!! Also note, glVertexAttribIFormat will also work).

The next step is to bind the vertex & UV arrays as SSBO's

layout(std430, binding = 1) buffer vertexBuffer
{
    float verts[];
};
layout(std430, binding = 2) buffer uvBuffer
{
    float uvs[];
};

And now in your main() you can extract the correct vert + uv like so:

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);

At that point skip glDrawElements, and just use glDrawArrays instead (where the vertex count is the number of indices in your mesh).

I'm not saying this is the fastest approach (building your own indexed elements is probably the most performant). There are however cases where this can be useful - usually when you have data in a format such as FBX/USD/Alembic, and need to update (for example) the vertices and normals each frame.