In a voxel world (where the environment is composed of blocks) each block face is composed of two triangles. This means that 6 vertices are required to create each block face, and two vertices in triangle A share the exact same data (position, normal, texture ID, etc) as two of the vertices in triangle B.
I attempted to remove this redundant data by changing from GL_TRIANGLES to GL_TRIANGLE_STRIP and providing 4 vertices per block face, but I found I had to insert two degenerate vertices between each block face, meaning I’m back at 6 vertices per block face.
Is it possible to tell OpenGL that each strip in a GL_TRIANGLE_STRIP is composed of exactly 4 vertices, meaning that degenerate vertices are not needed?
You can use primitive restarting to render multiple strips/loops/polygons with a single draw call. See glEnable for GL_PRIMITIVE_RESTART or GL_PRIMITIVE_RESTART_FIXED_INDEX.
However, you’re better off using GL_TRIANGLES. Only the vertex index needs to be duplicated. You only need one copy of the data for each vertex and the vertex shader will only be executed once for each vertex (assuming that the triangles in each pair are consecutive in the index array).
I’m hesitant to use an index buffer as my vertices are the same size as an integer in the index buffer (4 bytes), meaning for a block face I’ll have 4 vertices + 6 indices = 40 bytes total, up from 24.
I’ve looked into geometry shaders, however they seem to have issues with rewriting the produced geometry back to memory, before passing them to the fragment shader.
Mesh shaders look perfect for this task as I can send one vertex to the mesh shader and produce two triangles from it. Unfortunately they are only available on newer NVIDIA cards and some AMD cards but I’d love to see the performance difference.
and then re-use it in multiple draw calls? For example if I had three VBOs with lengths 1000, 2000 and 3000:
// Bind the index buffer, which will be used to render each VBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboHandle);
glBindVertexArray(vao0);
// Divide by 4 to get face count, multiply by 5 to get the amount of indices
glDrawElements(GL_TRIANGLE_STRIP, 1000 / 4 * 5, GL_UNSIGNED_INT, NULL);
glBindVertexArray(vao1);
glDrawElements(GL_TRIANGLE_STRIP, 2000 / 4 * 5, GL_UNSIGNED_INT, NULL);
glBindVertexArray(vao2);
glDrawElements(GL_TRIANGLE_STRIP, 3000 / 4 * 5, GL_UNSIGNED_INT, NULL);
You can use the same index buffer for multiple draw calls. If you’re using VAOs, you can bind the same index buffer in multiple VAOs.
If you’re using a single index buffer, it doesn’t really matter if it’s 20% larger (i.e. GL_TRIANGLES with 6 indices per triangle versus GL_TRIANGLE_STRIP with 5). If there’s any overhead for using primitive restarting, using it with such small primitives will exacerbate it.