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