Search code examples
c++openglglfwglm-math

Why am I getting half of the triangles displayed on the screen?


I followed this tutorial and was able to draw 20 triangles on the screen. However, in that example, both the position and the color data are stored in a single array and passed through as a single glBufferSubData call. I'm not sure whether I should follow this approach always or create separate arrays to store the vertex attributes and call glBufferSubData for each attribute. I tried to store the position and color separately and got into a strange problem. Instead of having 20 triangles drawn on the screen, I am getting only 10!

[I used glfw instead of Qt for creating the window.]

Approach 1:

I defined the global variables as follows:

float dx = 0.1f;
unsigned int triangleCount = 0;

const unsigned int TRIANGLE_MAX_COUNT = 20;
const unsigned int TRIANGLE_VERTEX_COUNT = 3;
const unsigned int VERTEX_FLOAT_COUNT = 8;
const unsigned int TRIANGLE_BYTE_SIZE = TRIANGLE_VERTEX_COUNT * VERTEX_FLOAT_COUNT * sizeof(float);

My triangle drawing function is defined as

void sendAnotherTriangle()
{
    if (triangleCount == TRIANGLE_MAX_COUNT)
        return;
    const float x = -1 + triangleCount * dx;

    glm::vec4 newTriangle[] = {
        glm::vec4(x, +0.0f, +0.0f,1.0f),
        glm::vec4(+1.0f, +0.0f, +0.0f,1.0f),
        glm::vec4(x + dx, +1.0f, +0.0f,1.0f),
        glm::vec4(+1.0f, +0.0f, +0.0f,1.0f),
        glm::vec4(x, +1.0f, +0.0f,1.0f),
        glm::vec4(+1.0f, +0.0f, +0.0f,1.0f)
    };

    glBufferSubData(GL_ARRAY_BUFFER, triangleCount * sizeof(newTriangle), sizeof(newTriangle), newTriangle);

    triangleCount++;
}

The vertex buffer layout is defined in the following way:

unsigned int vb;
glGenBuffers(1, &vb);
glBindBuffer(GL_ARRAY_BUFFER, vb);
glBufferData(GL_ARRAY_BUFFER, TRIANGLE_MAX_COUNT*TRIANGLE_BYTE_SIZE, NULL, GL_STATIC_DRAW);


glEnableVertexAttribArray(0);
glVertexAttribPointer(0, glm::vec4::length(), GL_FLOAT, GL_FALSE, 2 * sizeof(glm::vec4), (const void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, glm::vec4::length(), GL_FLOAT, GL_FALSE, 2 * sizeof(glm::vec4), (const void*)(sizeof(glm::vec4)));

As per my understanding, since the position and the color attributes are interleaved, the stride for both the attributes is: 2 * sizeof(glm::vec4) since, each attribute is of type glm::vec4. Since the position data starts at the very beginning, position offset is 0 while the color offset is sizeof(glm::vec4) as it starts after the first vertex position.

Finally, the draw call looks like this:

while (display.isRunning(window))
{
    glClear(GL_DEPTH_BUFFER_BIT);
    sendAnotherTriangle();
    glDrawArrays(GL_TRIANGLES, 0, triangleCount * TRIANGLE_VERTEX_COUNT);

    display.update();
}

This gives me the desired result i.e. 20 red triangles drawn on the screen.

Approach 2:

After that, I tried to do the same using multiple glBufferSubData calls. In this approach the modified triangle drawing function is:

void sendAnotherTriangle()
{
    if (triangleCount == TRIANGLE_MAX_COUNT)
        return;
    const float x = -1 + triangleCount * dx;

    glm::vec4 newPositions[] = {
        glm::vec4(x, +0.0f, +0.0f, +1.0f),
        glm::vec4(x + dx, +1.0f, +0.0f, +1.0f),
        glm::vec4(x, +1.0f, +0.0f, +1.0f)
    };


    glm::vec4 newColors[] = {
        glm::vec4(+1.0f, +0.0f, +0.0f, +1.0f),
        glm::vec4(+1.0f, +0.0f, +0.0f, +1.0f),
        glm::vec4(+1.0f, +0.0f, +0.0f, +1.0f)
    };

    glBufferSubData(GL_ARRAY_BUFFER, triangleCount*(sizeof(newPositions) + sizeof(newColors)), sizeof(newPositions), newPositions);
    glBufferSubData(GL_ARRAY_BUFFER, triangleCount*(sizeof(newPositions) + sizeof(newColors)) + sizeof(newPositions), sizeof(newColors), newColors);
triangleCount++;
}

And the vertex buffer layout is modified as follows:

glEnableVertexAttribArray(0);
glVertexAttribPointer(0, glm::vec4::length(), GL_FLOAT, GL_FALSE, 0, (const void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, glm::vec4::length(), GL_FLOAT, GL_FALSE, 0, (const void*)(TRIANGLE_VERTEX_COUNT * sizeof(glm::vec4)));

Since the position and the color data are stored separately, my understanding is that they are tightly packed and hence their stride is 0 and since the color data starts after the position data, the color offset is TRIANGLE_VERTEX_COUNT * sizeof(glm::vec4).

With this new setting, I expected to have 20 triangles drawn on the screen. However, I am getting only 10 triangles drawn on the screen. I guess this is due to assigning incorrect values to the strides and offsets. However, I could not figure it out.

Therefore, I wish to firstly ask which one is a better and safer approach.

Secondly, how can I get the desired result following the second approach?


Solution

  • You've a basic misunderstanding. If you've separated vertex coordinates and separated colors, then the attributes are not separated per primitive.
    All the vertex coordinates of the 20 triangles have to be in a consecutive area of the buffer and all the colors of the 20 triangles have to be in a consecutive area of the buffer.

    You've to store the 60 (3*20) vertices of the triangles in the buffer and after that the 60 colors. The offset of the colors is 20*3*sizeof(glm::vec4).

    glBufferSubData(GL_ARRAY_BUFFER, 
                    triangleCount*sizeof(newPositions),
                    sizeof(newPositions), newPositions);
    glBufferSubData(GL_ARRAY_BUFFER, 
                    triangleCount*sizeof(newPositions) + 20*3*sizeof(glm::vec4), 
                    sizeof(newColors), newColors);
    
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, glm::vec4::length(), GL_FLOAT, GL_FALSE,
                          0, (const void*)0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, glm::vec4::length(), GL_FLOAT, GL_FALSE, 
                          0, (const void*)(20*3*sizeof(glm::vec4)));