PDA

View Full Version : rendering multiple sprites



blubee
12-10-2015, 12:44 PM
This might be a simple question but I am stuck.

I have this data structure


typedef struct quad_2d {
float angle_in_rad;
GLshort vertex_count;
GLfloat vertices[12];
GLfloat colors[16];
GLshort indices[6];
GLfloat tex_coords[8];
}sprite;


init code looks like this


glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

glGenBuffers(1, &vert_buff);
glBindBuffer(GL_ARRAY_BUFFER, vert_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(sprite->vertices),
sprite->vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat),
(GLvoid*)0);
glEnableVertexAttribArray(0);

glGenBuffers(1, &col_buff);
glBindBuffer(GL_ARRAY_BUFFER, col_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(sprite->colors),
sprite->colors, GL_STATIC_DRAW);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat),
(GLvoid*)0);
glEnableVertexAttribArray(1);

glGenBuffers(1, &tex_buff);
glBindBuffer(GL_ARRAY_BUFFER, tex_buff);
glBufferData(GL_ARRAY_BUFFER, sizeof(sprite->tex_coords),
sprite->tex_coords, GL_STATIC_DRAW);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
(GLvoid*)0);
glEnableVertexAttribArray(2);

glGenBuffers(1, &ind_buff);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ind_buff);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(sprite->indices),
sprite->indices, GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);


render code looks like this


glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glClearDepth(1.0f);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_id);
glUniform1i(tex_loc, 0);

glUseProgram(ce_get_default_shader()->shader_program);
glBindVertexArray(vao);

glBindTexture(GL_TEXTURE_2D, tex_id);
glUniform1i(tex_loc, 0);

mat4_multi(&mmvp_mat, &vview_mat, model_mat);
mat4_multi(&mmvp_mat, &pproj_mat, &mmvp_mat);
glUniformMatrix4fv(mvp_matrix_loc, 1, GL_FALSE, mmvp_mat->data);

glUniformMatrix4fv(model_mat_loc, 1, GL_FALSE, model_mat->data); //model matrix data that changes
glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, vview_mat->data);
glUniformMatrix4fv(proj_matrix_loc, 1, GL_FALSE, pproj_mat->data);

glDrawElements(GL_TRIANGLES, 6,
GL_UNSIGNED_SHORT, 0);
glBindVertexArray(vao);



this renders one sprite to the screen. Now I don't need to update the vbo since all the sprites are the same, only thing that changes is the model matrix that renders their position on screen which I commented above.

Since the vbo doesn't change at all how can I pack these into a bigger buffer to render say 10 in one draw call?

Anyone can help me solve this?

GClements
12-10-2015, 01:22 PM
how can I pack these into a bigger buffer to render say 10 in one draw call?
If the matrix needs to change for each sprite, then it can't be a uniform. The main options are

1. Add a vertex attribute for the matrix. This means 4 copies of each matrix, one for each vertex.

2. Add a vertex attribute which is an index into a uniform array of matrices. Then you only need 4 copies of the index rather than 4 copies of the the matrix.

3. Use instanced rendering and add an instance attribute for the matrix. But instancing has a cost. Instanced
rendering requires OpenGL 3.1, per-instance attributes require OpenGL 3.3.

4. Transform the vertices on the client and upload them each frame.

If you do opt for instanced rendering, you can probably reduce the size of the sprite data somewhat (an axis-aligned rectangle only needs 2 vertices rather than 4).

For options 1-3, you can probably "compress" the matrix by storing e.g. translation, rotation, scale and computing the matrix in the vertex shader.

blubee
12-10-2015, 01:28 PM
If the matrix needs to change for each sprite, then it can't be a uniform. The main options are

1. Add a vertex attribute for the matrix. This means 4 copies of each matrix, one for each vertex.

2. Add a vertex attribute which is an index into a uniform array of matrices. Then you only need 4 copies of the index rather than 4 copies of the the matrix.

3. Use instanced rendering and add an instance attribute for the matrix. But instancing has a cost. Instanced
rendering requires OpenGL 3.1, per-instance attributes require OpenGL 3.3.

4. Transform the vertices on the client and upload them each frame.

If you do opt for instanced rendering, you can probably reduce the size of the sprite data somewhat (an axis-aligned rectangle only needs 2 vertices rather than 4).

For options 1-3, you can probably "compress" the matrix by storing e.g. translation, rotation, scale and computing the matrix in the vertex shader.

I looked into instanced rendering, this is pretty easy to follow: http://www.opengl-tutorial.org/intermediate-tutorials/billboards-particles/particles-instancing/
but since I have to target opengles 2.0 that's a no go for now.

I currently pass the model view projection matrix to the shader, then calculate the vertex position in the vertex shader.

Do you have any code samples or examples of doing what you're saying?

blubee
12-10-2015, 03:30 PM
actually thinking about this situation I am starting to get some ideas.

for example my current vertex shader looks like this:


#version 330 core

layout (location = 0) in vec3 a_pos;
layout (location = 1) in vec4 a_col;
layout (location = 2) in vec2 a_tex;

uniform mat4 u_mvp_mat;
uniform mat4 u_mod_mat;
uniform mat4 u_view_mat;
uniform mat4 u_proj_mat;

out vec4 f_color;
out vec2 f_tex;

void main()
{
gl_Position = u_mvp_mat * vec4(a_pos, 1.0);

f_tex = a_tex;
f_color = a_col;
}


since this is a simple sprite renderer and I want to batch things what I could do is remove the matrix from the shader and do the rotation on the cpu, then pass the position to the shader.

so to rotate around the z axis i'd need to use the upper 3x3 corner of the 4x4 mvp matrix like this


| cos (angle) -sin(angle) 0 |
| sin (angle) cos(angle) 0 |
| 0 0 0 |


doing that on the cpu and passing that result as a per vertex attribute means that I will have done my mvp * vector multiplication on the cpu.

With that being done, I should be able to put all the vertices data in 1 huge batch.

Am I on the right path with this train of thought?

GClements
12-10-2015, 06:33 PM
Am I on the right path with this train of thought?
Any component of the overall MVP matrix which is per-sprite would need to be applied to the vertices in the client and removed from the matrix used by the shader. You probably know that certain matrix elements will always be zero, which allows the calculation to be simpler than a full 4x4 transformation.

blubee
01-06-2016, 07:54 AM
Any component of the overall MVP matrix which is per-sprite would need to be applied to the vertices in the client and removed from the matrix used by the shader. You probably know that certain matrix elements will always be zero, which allows the calculation to be simpler than a full 4x4 transformation.

I finally made it back to graphics programming and for a minute things were going just fine.

Thank you now that I rotate the quads on the cpu I can batch them together and send them to the gpu for one massive draw call which while isn't necessarily great drawing only a few sprites it provides a great base where this method can be used on devices supporting opengles 2.0 and above now I can develop all the main features then build on top of that.