Search code examples
c++openglvertextriangulationvertex-array-object

How to correctly populate vertex array


I am confused with how to populate my vertex array for it to be correctly drawn. The OpenGL code that I am using is:

float vertices[size];
//Here I have a method to populate the array with my values from a 2D matrix

glGenVertexArrays(1, &vaoID[0]); // Create our Vertex Array Object
glBindVertexArray(vaoID[0]); // Bind our Vertex Array Object so we can use it

glGenBuffers(1, vboID); // Generate our Vertex Buffer Object
glBindBuffer(GL_ARRAY_BUFFER, vboID[0]); // Bind our Vertex Buffer Object
glBufferData(GL_ARRAY_BUFFER, (size) * sizeof(GLfloat), vertices, GL_STATIC_DRAW); // Set the size and data of our VBO and set it to STATIC_DRAW

glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0); // Set up our vertex attributes pointer
glEnableVertexAttribArray(0); // Disable our Vertex Array Object
glBindVertexArray(0); // Disable our Vertex Buffer Object

glBindVertexArray(vaoID[0]); // Bind our Vertex Array Object
glDrawArrays(GL_TRIANGLES, 0, size/3); // Draw
glBindVertexArray(0); // Unbind our Vertex Array Object

Hopefully the image makes this easier to understand. In image 'A' the 3x3 matrix on the left shows what I am currently getting when I draw triangles, the matrix on the right is what I am trying to achieve.

Currently when I create the array of vertices to draw from, I populate it in the order shown in image 'B'. I add values to the array as I work along a row, then step down a column and repeat. So for example:

vertexArray[0] = matrixElement[0].x  
vertexArray[1] = matrixElement[0].y  
vertexArray[2] = matrixElement[0].z  
vertexArray[3] = matrixElement[1].x  
vertexArray[4] = matrixElement[1].y  
vertexArray[5] = matrixElement[1].z  

This order of input into the vertexArray gives results that can be seen in image 'C', where the wrong triangles are created.

My question is: what is the sensible way of populating a vertex array, in an order that is going to create triangles on a large matrix of vertices in a way that looks like the right hand diagram in image 'A'.

Image of issue


Solution

  • If you want to avoid any redundancy, and draw the whole mesh with a single draw call, one option is to use a combination of:

    • GL_TRIANGLE_STRIP as the primitive type.
    • Indexed drawing.
    • Primitive restart (requires OpenGL 3.1 or later).

    The primitive restart feature is needed because you can't easily represent this mesh with a single triangle strip. Using the numbering from your sketch, you can for example describe the topology with two triangle strips using these two index sequences:

    0 3 1 4 2 5
    3 6 4 7 5 8
    

    Setting up and enabling primitive restart can be done with these calls:

    glEnable(GL_PRIMITIVE_RESTART);
    glPrimitiveRestartIndex(0xFFFF);
    

    You can chose the index, but the common policy (and only option supported in OpenGL ES) is to use the largest possible value, which is 0xFFFF if you use GLushort as the type of the indices.

    Once primitive restart is enabled, the "special" index will then start a new triangle strip. So the two triangle strips above can now be given by a single index sequence:

    0 3 1 4 2 5 0xFFFF 3 6 4 7 5 8
    

    You should be able to find plenty of examples that show how to set up an index buffer (you can recognize them by their use of GL_ELEMENT_ARRAY_BUFFER), so I'll skip that part. The draw call is then:

    glDrawElements(GL_TRIANGLE_STRIP, 0, 13, GL_UNSIGNED_SHORT, 0);
    

    where 13 is the number of indices used.

    There are alternatives to using primitive restart:

    • Use repeated vertices. This was a common trick before primitive restart was widely available. I wouldn't recommend it anymore. Just for illustration, the index sequence in this case would be:

      0 3 1 4 2 5 5 3 3 6 4 7 5 8
      

      The two repeated indices will result in a degenerate (0 pixel) triangle that "connects" the two triangle strips.

    • Use glMultiDrawElements() to draw multiple triangle strips with a single draw call.