1
votes

I have the following vertex shader:

#version 450 core
...
layout (binding=2, std140) uniform MATRIX_BLOCK
{
   mat4 projection;
   mat4 view;
   mat4 model[128];
   mat4 mvp[128];
};

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
layout (location = 3) in uint object_idx;

out vec2 TexCoord;
flat out uint instance_idx;

void main()
{  
  gl_Position = mvp[object_idx] * vec4(aPos.x, aPos.y, aPos.z, 1.0);  
  TexCoord = aTexCoord;
  instance_idx = object_idx;
}

I'm using a uniform buffer to pass in 128 model and model-view-projection matrices, indexed by an object id. The object id is passed to the shader using a vertex attribute object_idx; basically every vertex, besides having x,y,z coordinates and u,v texture coordinates, also has an object id associated with it. The idea would be to be able to store the data for multiple objects in the same buffers but still use specific transformation matrices for each individual object. This is my (possibly stupid) attempt to batch together multiple objects to draw them with a single draw call, without having to rebind anything, using glDrawElements to render triangles.

However, it doesn't work. When I do

gl_Position = mvp[object_idx] * vec4(aPos.x, aPos.y, aPos.z, 1.0);

then triangles with an object_idx of 0 get rendered just fine at the expected position, but triangles with vertices with object_idx's other than 0 don't appear anywhere. I thought I might have gotten the transformation matrices wrong, so for debugging, I reduced the possible objects to just 2 (0 and 1) and inverted the indexing using

gl_Position = mvp[1-object_idx] * vec4(aPos.x, aPos.y, aPos.z, 1.0);

This resulted in all the triangles with object_idx = 0 being rendered at the expected position for mvp[1], but again, no triangles with object_idx = 1 appearing anywhere. So at least I know that the transformation matrices are correct. I then tried

gl_Position = mvp[0] * vec4(aPos.x, aPos.y, aPos.z, 1.0);

and that renders all triangles (using object 0's transformation matrix) and

gl_Position = mvp[1] * vec4(aPos.x, aPos.y, aPos.z, 1.0);

renders all of them, using object 1's transformation matrix.

So, obviously I don't understand something really fundamental about how vertex shaders or glDrawElements do their work.

So, my question:

Why don't all my triangles get rendered when I do a "dynamic" lookup of the mvp transformation matrix using object_idx, when to the best of my ability to check it all the data is passed into the vertex shader just as it's supposed to be?

1
Ensure that the maximum size for the Uniform Buffer Object is not exceeded. A Shader Storage Buffer Object would provide a much higher size limit.Rabbid76
I checked: My GPU says that GL_MAX_UNIFORM_BLOCK_SIZE is 65k. I'm only using 16k....Pascal

1 Answers

2
votes

I'm making an educated guess here:

layout (location = 3) in uint object_idx;

Using uint attribute input requires to set up the attribute pointer with the function glVertexAttribIPointer() (note the extra I in there).

Using the standard glVertexAttribPointer function will always set up float attributes (and using type GL_INT there will convert from integer to float). Technically, when you read such a float attribute as uint in the shader, it will be undefined, but it is quite likely the 0 stays 0 as the float and integer represations of that are usually identical, but that works only by accident.

Apart from that issue, storing the object index per vertex is also quite inefficient. To effectively batch your draw calls, you should have a look at multi draw calls and gl_DrawID (originally from GL_ARB_shader_draw_parameters) features. You might also find the approaching zero driver overhead (AZDO) techniques useful.