I successfully can render two triangles (with a texture) that looks like the side of a crate and also "walk" around it. Also, a small .obj file parser is available which gives me a float array of the vertices, texture coords and an unsigned int array of the indices (of a simple cube).

The vertices and texture coords look like the following:

float vertices[] =
     // positions         // texture coords
     0.5f,  0.5f, 0.0f,   1.0f, 1.0f,
     0.5f, -0.5f, 0.0f,   1.0f, 0.0f,
    -0.5f, -0.5f, 0.0f,   0.0f, 0.0f,
    -0.5f,  0.5f, 0.0f,   0.0f, 1.0f

And the indices:

unsigned int indices[] =
    0, 1, 3, // first triangle
    1, 2, 3  // second triangle

To assign the vertices and tex coords I use the following:

unsigned int VBO;
glGenBuffers( 1, &VBO );
glBindBuffer( GL_ARRAY_BUFFER, VBO );
glBufferData( GL_ARRAY_BUFFER, 5 * 4 * sizeof( float ), vertices, GL_STATIC_DRAW );

Creating an IBO

call glEnableVertexAttribArray()

Creating a shader, a texture etc.. and draw it with glDrawElements().

This works all very well, I just can't wrap my head around how to use just a single VBO with the "split" data that my .obj parser gives back.
I now (when using the .obj parser) have the vertices and texture coords separated; how do I put that two arrays into one VBO?

The picture I have in mind is to have fewer draw calls and also prevent redundant code if I, for example, use the same model for 100 times.

  • \$\begingroup\$ For vertex arrays, simply concatenate them as is, but for indices you will need to add offset for new consequitive stream of index data. That is, for each index of the new index array you add current size of the concatenated array. index = index + concatenated_ib_size; \$\endgroup\$
    – Ocelot
    Jun 27 '19 at 18:10
  • \$\begingroup\$ @Ocelot That sounds promising, can you please put it in an answer so I can accept it? Also, can you please explain how to use adjust glEnableVertexAttribArray() and glVertexAttribPointer() in this particular case? \$\endgroup\$ Jun 28 '19 at 11:35
  • \$\begingroup\$ Wait... Do you want to pack multiple vertex attributes into one VBO or you want to concatenate multiple vertex arrays into one? \$\endgroup\$
    – Ocelot
    Jun 28 '19 at 19:11
  • \$\begingroup\$ I want to pack multiple vertex attributes into one VBO \$\endgroup\$ Jun 28 '19 at 20:20
  • \$\begingroup\$ Well then you can't pack more than 4 floats per vertex attribute in one VBO. Specification says that glVertexAttribPointer() parameter "size" must be in range [1; 4] which corresponds to the types from float to vec4 in your shader program. You can, at best, pack 2D position and UV coordinates which will be two 2D vectors packed in one 4D vector. Specification page for glVertexAttribPointer() : khronos.org/registry/OpenGL-Refpages/gl4/html/… \$\endgroup\$
    – Ocelot
    Jun 28 '19 at 20:30

As I'm now a bit more advanced with OpenGL, I can tell that I couldn't express what I was looking for. What I was really looking for was a way to draw multiple objects in a performance saving way, i.e. batch rendering or instancing.

I solved my problem in choosing instanced rendering. It roughly boils down to:

  • Set up everything as usual

  • Specify a mat4 attribute for the position of each object in your shader

  • Submit the data to the shader (make sure to specify GL_STREAM_DRAW in the glBufferData() function

  • Here's how I do it, but this should be put into a separate class to make it more flexible to use for other attributes:

// Set transform attribute
p_int modelMatrixLocation_0 = MeshCollection->m_UsedShaders[m_ShaderName]->GetAttribLocation( "a_ModelMatrix" );
p_int modelMatrixLocation_1 = modelMatrixLocation_0 + 1;
p_int modelMatrixLocation_2 = modelMatrixLocation_0 + 2;
p_int modelMatrixLocation_3 = modelMatrixLocation_0 + 3;

if ( modelMatrixLocation_0 != -1 ) { Platf::glBindBuffer( GL_ARRAY_BUFFER, m_ModelMatrixID ); Platf::glBufferData( GL_ARRAY_BUFFER, m_ModelMatrix.size() * sizeof( Maths::mat4 ), &m_ModelMatrix[0][0], GL_STREAM_DRAW );

Platf::glEnableVertexAttribArray( modelMatrixLocation_0 ) ; Platf::glVertexAttribPointer( modelMatrixLocation_0, 4, GL_FLOAT, GL_FALSE, sizeof( Maths::mat4 ), ( const void * )( 0 * sizeof( Maths::vec4 ) ) ); Platf::glEnableVertexAttribArray( modelMatrixLocation_1 ) ; Platf::glVertexAttribPointer( modelMatrixLocation_1, 4, GL_FLOAT, GL_FALSE, sizeof( Maths::mat4 ), ( const void * )( 1 * sizeof( Maths::vec4 ) ) ); Platf::glEnableVertexAttribArray( modelMatrixLocation_2 ) ; Platf::glVertexAttribPointer( modelMatrixLocation_2, 4, GL_FLOAT, GL_FALSE, sizeof( Maths::mat4 ), ( const void * )( 2 * sizeof( Maths::vec4 ) ) ); Platf::glEnableVertexAttribArray( modelMatrixLocation_3 ) ; Platf::glVertexAttribPointer( modelMatrixLocation_3, 4, GL_FLOAT, GL_FALSE, sizeof( Maths::mat4 ), ( const void * )( 3 * sizeof( Maths::vec4 ) ) ); Platf::glVertexAttribDivisor( modelMatrixLocation_0, 1 ); Platf::glVertexAttribDivisor( modelMatrixLocation_1, 1 ); Platf::glVertexAttribDivisor( modelMatrixLocation_2, 1 ); Platf::glVertexAttribDivisor( modelMatrixLocation_3, 1 ); }
  • Then in the shader calculate it like that gl_Position = u_ProjectionViewMatrix * a_ModelMatrix * vec4( a_InVertexPosition, 1.0f );
  • And finally, call Platf::glDrawElementsInstanced( GL_TRIANGLES, _IndeciesCount, GL_UNSIGNED_INT, nullptr, _InstancedCount )

There are some really good tutorials on this subject, for example, this one at learnopengl.com:

As for batch rendering, I've found these two tutorials which should give a basic knowledge of how to implement it:


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.