I’ve started working on a 2D tile rendering system. So far it draws 8x8 tiles on the screen. At 640x480 resolution, there can be up to 4941 tiles on screen at a time, and the number quickly increases at higher resolutions. I’m looking for an efficient way to render them all.
[ATTACH=CONFIG]1712[/ATTACH]
Additionally, each tile will be a solid color. On the application side, I have a vec4[MAX_COLORS] storing each color and I send an int colorIndex to each instance.
I’m using instanced rendering right now, but several things about my approach make me wonder if it’s inefficient. I’m storing the colors in a uniform in the vertex shader, which I realize isn’t the best way. I’m just getting the hang of openGL so I’m also not sure if instanced rendering would be the method here. Here’s the relevant code:
Constant data, structs:
static float colors[MAX_COLORS][4];
static float tileRect[] =
{
0.0f, 0.0f,
0.0f, TILE_SIZE,
TILE_SIZE, 0.0f,
TILE_SIZE, TILE_SIZE
};
struct
{
vec2 positions[MAX_TILES];
int colorIndex[MAX_TILES];
} tiles;
struct
{
GLuint vertexShader;
GLuint fragShader;
GLuint programID;
GLuint vao;
} tileShader;
I bind the rectangle, positions, and color index as vertex attributes:
glVertexArrayVertexBuffer(tileShader.vao, 0, buffers.rect, 0, 2 * sizeof(float));
glVertexArrayVertexBuffer(tileShader.vao, 1, buffers.position, 0, 2 * sizeof(float));
glVertexArrayVertexBuffer(tileShader.vao, 2, buffers.colIdxs, 0, 1 * sizeof(int));
glVertexArrayAttribFormat(tileShader.vao, 0, 2, GL_FLOAT, GL_FALSE, NULL);
glVertexArrayAttribFormat(tileShader.vao, 1, 2, GL_FLOAT, GL_FALSE, NULL);
glVertexArrayAttribIFormat(tileShader.vao, 2, 1, GL_INT, 0);
//...
glVertexArrayBindingDivisor(tileShader.vao, 0, 0);
glVertexArrayBindingDivisor(tileShader.vao, 1, 1);
glVertexArrayBindingDivisor(tileShader.vao, 2, 1);
Store the colors and and transform as uniforms:
GLint cl = glGetUniformLocation(tileShader.programID, "colors");
glUniform4fv(cl, MAX_COLORS, &colors);
GLint mv = glGetUniformLocation(tileShader.programID, "modelview");
glUniformMatrix4fv(mv, 1, GL_FALSE, worldTransform);
The render loop:
while (!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, MAX_TILES);
glfwSwapBuffers(window);
glfwPollEvents();
}
Vertex shader: (OpenGL 4.5)
#version 450 core
uniform vec4 colors[2];
uniform mat4 modelview;
layout(location = 0) in vec2 iPos;
layout (location = 1) in vec2 iOff;
layout (location = 2) in int iColIdx;
out vec4 vCol;
void main()
{
gl_Position = modelview * vec4(iPos + iOff, 0.0f, 1.0f);
vCol = colors[iColIdx];
}
Other approaches I’ve considered:
Drawing the tiles:
- Draw the static tiles as point sprites
- Just send the tile’s color to each instance.
- Perhaps there’s a way to connect all the static quads into one large quad, and just render that?
For storing all the colors, I’m thinking either a 1D texture array or an ssbo.
So my question is, for upwards of 20,000 tiles (at higher resolutions), is this method sufficient, or is there a better way to handle this?
Thanks