I’m rendering a number of quads (as 2 triangles), the quads don’t line up with each other, therefore the triangles to render only share one edge (2 vertices).
The naive way of rendering with VBO and glDrawArrays gives me 23XXfps whereas by using an index buffer and glDrawElements gives me 22XXfps. I was expecting glDrawElements to be slightly faster, instead of slightly slower. Because the indices are only 4 per quad, whereas without it would be 6 vertices, without any reusing of previous vertices.
I realize that this is quite a fringle application, wanting to draw quads that don’t share any edges with each other, but still a surprising outcome to me - could anyone please explain or point me to what I could be doing differently?
code snippets:
//routine to assemble the VBO:
int numVerticies = numQuads*4; //for DrawElements
//int numVerticies = numQuads*6; //for DrawArrays
std::vector<uint16_t> indices;
GLfloat* vertexBufferPositions = new GLfloat[numVerticies*2]; //each quad has 4 verticies á 2 coordinate values: x,y, meaning 8 entries per char total
GLfloat* vertexBufferUV = new GLfloat[numVerticies*2]; //each quad has four UV coordinates per vertex: u,v meaning 8 entries per char total
for(unsigned int i=0; i<numQuads; i++)
{
/*
//VBO for DrawArrays:
unsigned int vertexIndex = i*12;
unsigned int uvIndex = i*12;
//first triangle of quad
//top left vertex
vertexBufferPositions[vertexIndex] = quadList[i].topLeftX;
vertexBufferPositions[vertexIndex+1] = quadList[i].topLeftY;
vertexBufferUV[uvIndex] = quadList[i].textureTopLeftX;
vertexBufferUV[uvIndex+1] = quadList[i].textureTopLeftY;
//bottom left vertex
vertexBufferPositions[vertexIndex+2] = quadList[i].bottomLeftX;
vertexBufferPositions[vertexIndex+2+1] = quadList[i].bottomLeftY;
vertexBufferUV[uvIndex+2] = quadList[i].textureBottomLeftX;
vertexBufferUV[uvIndex+2+1] = quadList[i].textureBottomLeftY;
//bottom right vertex
vertexBufferPositions[vertexIndex+4] = quadList[i].bottomRightX;
vertexBufferPositions[vertexIndex+4+1] = quadList[i].bottomRightY;
vertexBufferUV[uvIndex+4] = quadList[i].textureBottomRightX;
vertexBufferUV[uvIndex+4+1] = quadList[i].textureBottomRightY;
////////second triangle of quad
//top left vertex
vertexBufferPositions[vertexIndex+6] = quadList[i].topLeftX;
vertexBufferPositions[vertexIndex+6+1] = quadList[i].topLeftY;
vertexBufferUV[uvIndex+6] = quadList[i].textureTopLeftX;
vertexBufferUV[uvIndex+6+1] = quadList[i].textureTopLeftY;
//bottom right vertex
vertexBufferPositions[vertexIndex+8] = quadList[i].bottomRightX;
vertexBufferPositions[vertexIndex+8+1] = quadList[i].bottomRightY;
vertexBufferUV[uvIndex+8] = quadList[i].textureBottomRightX;
vertexBufferUV[uvIndex+8+1] = quadList[i].textureBottomRightY;
//top right vertex
vertexBufferPositions[vertexIndex+10] = quadList[i].topRightX;
vertexBufferPositions[vertexIndex+10+1] = quadList[i].topRightY;
vertexBufferUV[uvIndex+10] = quadList[i].textureTopRightX;
vertexBufferUV[uvIndex+10+1] = quadList[i].textureTopRightY;
*/
//VBO and IBO for DrawElements
unsigned int vertexIndex = i*8; //4*2 -> x,y per vertex
unsigned int uvIndex = i*8; //4*2 -> u,v per vertex
unsigned int indexOffset = i*4; //4*1
////first triangle of quad
//bottom left vertex
vertexBufferPositions[vertexIndex] = quadList[i].bottomLeftX;
vertexBufferPositions[vertexIndex+1] = quadList[i].bottomLeftY;
vertexBufferUV[uvIndex] = quadList[i].textureBottomLeftX;
vertexBufferUV[uvIndex+1] = quadList[i].textureBottomLeftY;
indices.push_back(0+indexOffset);
//top left vertex
vertexBufferPositions[vertexIndex+2] = quadList[i].topLeftX;
vertexBufferPositions[vertexIndex+2+1] = quadList[i].topLeftY;
vertexBufferUV[uvIndex+2] = quadList[i].textureTopLeftX;
vertexBufferUV[uvIndex+2+1] = quadList[i].textureTopLeftY;
indices.push_back(1+indexOffset);
//bottom right vertex
vertexBufferPositions[vertexIndex+4] = quadList[i].bottomRightX;
vertexBufferPositions[vertexIndex+4+1] = quadList[i].bottomRightY;
vertexBufferUV[uvIndex+4] = quadList[i].textureBottomRightX;
vertexBufferUV[uvIndex+4+1] = quadList[i].textureBottomRightY;
indices.push_back(2+indexOffset);
////second triangle of quad
//bottom right vertex
indices.push_back(2+indexOffset); //since we already have this vertex, we only store the index
//top left vertex
indices.push_back(1+indexOffset); //since we already have this vertex, we only store the index
//top right vertex
vertexBufferPositions[vertexIndex+6] = quadList[i].topRightX;
vertexBufferPositions[vertexIndex+6+1] = quadList[i].topRightY;
vertexBufferUV[uvIndex+6] = quadList[i].textureTopRightX;
vertexBufferUV[uvIndex+6+1] = quadList[i].textureTopRightY;
indices.push_back(3+indexOffset);
}
glBindVertexArray(m_vertexArrayID); // Bind our Vertex Array Object so we can use it
if(!m_vertexbuffer)
glGenBuffers(1, &m_vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, numVerticies*2*sizeof(GLfloat), vertexBufferPositions, GL_DYNAMIC_DRAW); // Give our vertices to OpenGL.
if(!m_uvBuffer)
glGenBuffers(1, &m_uvBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_uvBuffer);
glBufferData(GL_ARRAY_BUFFER, numVerticies*2*sizeof(GLfloat), vertexBufferUV, GL_DYNAMIC_DRAW); // Give our uv's to OpenGL.
if(!m_elementBuffer)
glGenBuffers(1, &m_elementBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementBuffer); // Generate a buffer for the indices
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_numIndices*sizeof(uint16_t), &indices[0], GL_DYNAMIC_DRAW); // Give our indices to OpenGL.
//routine for rendering
//bind the texture
glBindVertexArray(m_vertexArrayID); // Bind our Vertex Array Object so we can use it
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer); // This will talk about our 'vertexbuffer' buffer
glVertexAttribPointer(
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
2, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride, can be 0 for tightly packed array, or user specified: 3*sizeof(GLfloat)
(void*)0 // array buffer offset
);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, m_uvBuffer); // This will talk about our 'vertexbuffer' buffer
glVertexAttribPointer(
1, // attribute 1. No particular reason for 1, but must match the layout in the shader.
2, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride, can be 0 for tightly packed array, or user specified: 2*sizeof(GLfloat)
(void*)0 // array buffer offset
);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementBuffer);
glUseProgram(programID);
//and more setup stuff for the shader..
// Draw the thing
//glDrawArrays(GL_TRIANGLES, 0, m_numVerticies); // Starting from vertex 0 to vertices total
glDrawElements(GL_TRIANGLES, m_numIndices, GL_UNSIGNED_SHORT, (void*)0); // last parameter is the element array buffer offset
//cleanup
glUseProgram(0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0); // Unbind our Vertex Array Object
//unbind the texture