Hi all, I’m experimenting with basic heightmap rendering, where the terrain is split into 100x100 chunks composed of triangles (no strips yet):
I currently have one separate VBO for each chunk, and each chunk is rendered using glDrawArrays
. Instead, I would like to render the entire frustum-culled visible terrain with one draw call, i.e. upload all terrain data to one VBO and use glMultiDrawArrays
to render the chunks that are in the current view frustum.
I have used glMultiDrawArrays
before and am confident using it, but I would like to optimise this terrain rendering further. Since the X and Z positions are the same in every chunk - only the altitude varies per-vertex - I’d like to only have to define the X and Z positions once and re-use it for each chunk.
I created a VBO with just the X and Z position of each triangle (not using a strip yet), then uploaded the altitudes of each vertex in each chunk to a separate large instance buffer.
I can now render all chunks at once with glMultiDrawArraysIndirect
, however the same instance data is being used for each call. Each terrain chunk is positioned using gl_DrawID
:
Details
The base heightmap VBO has 6144 vertices and uses 2 ushorts for its X and Z position:
Gl.EnableVertexAttribArray(0);
Gl.VertexAttribIPointer(0, 2, VertexAttribType.UnsignedShort, 4, IntPtr.Zero);
Gl.VertexAttribDivisor(0, 0); // Advance once per vertex
The instance buffer can store 8 million integers. Each chunk then stores 6144 integers in this buffer (1 int per vertex)
Gl.EnableVertexAttribArray(1);
Gl.VertexAttribIPointer(1, 1, VertexAttribType.Int, 4, IntPtr.Zero);
Gl.VertexAttribDivisor(1, 0); // Advance once per vertex
One indirect command is used for each chunk, where offset
is the position that the chunk’s instance data is stored:
{
count = 6144,
instanceCount = 1,
first = 0,
baseInstance = (uint)offset
}
As a test I also rendered one indirect command with multiple instances, but this produced the same result:
{
count = 6144,
instanceCount = visibleChunkAmount,
first = 0,
baseInstance = 0
}
Questions:
- Am I using
glMultiDrawArraysIndirect
incorrectly, or is it simply not possible to have per-vertex instance data? Or not possible to incrementbaseInstance
per command? - The
baseInstance
value -0
,6144
,800000
, etcin the indirect command doesn't affect anything, whether I'm rendering 1 instance or
visibleChunkAmount` instances. Shouldn’t this offset the instance data that’s used for each internal draw call? - Is there another
glDraw*
method that’s more appropriate for this?
As a last resort I could upload the terrain altitude data to an SSBO and sample that in the vertex shader, but I’m suspicious that would be slower. I’d rather use VBOs / instancing correctly.