My 2D OpenGL game is going to have 5 different types of objects I want to render.


-unTextured AABBs

-unTextured Polys

-Textured AABBs

-Textured Polys

Text will have its own shader and thats fine.

I want things to be simple to code but not insanely slow either. I'm willing to do a little bit of batching and texture atlasing for bulk things like Tiles which can be drawn all at once, but otherwise objects will be drawn in an unpredictable order and therefore requires some state switching.

Polys will need a matrix for rotation passed as a uniform but obviously AABBs won't. So I'm not sure if they need to be separate or not.

One way I thought of uses 3 shaders. One for text and two for rendering textured and untextured polys and AABBs. For AABBs I could just pass an identity matrix instead of a rotation matrix.

Another way just has 5 separate shaders.

I'm concerned with the amount of switching between different shaders I'll need to do since I already know my GUI renderer will have to switch between rendering text and AABBs (though I don't expect more than a few dozen total widgets and containers).

Since the tiles will be batched and are by far the most numerous object in my game, I expect an asbolute maximum of less than 150 different draw calls (with shader and texture switches).

Just wondering which approach between the two seems the most sensible or if there's another suggestion.


Why not use a single shader not only for polys and sprites, but also for textured and untextured objects?

You can do that if you pass both texture coordinates and color information into your shader, together with a coefficient controlling the proportion in which they should be mixed:

final_color = color * (1 - n) + texture2D(blah, blah) * n;

As a bonus, if you do that separately for RGB and alpha, it will allow you to add color overlay to your sprites as needed.

Here is a simplified version of the shader I'm currently using, adjusted for your purposes:

/* Vertex */   

#version 330 compatibility

uniform mat4 u_matrix_view;
uniform mat4 u_matrix_model;

attribute vec2 a_position;
attribute vec4 a_color;
attribute vec2 a_tex_coord;
attribute float a_color_factor; // I used two separate float attributes for readability, but
attribute float a_alpha_factor; //   in reality you should merge them into a vec2.

varying vec4 v_color;
varying vec2 v_tex_coord;
varying float v_color_factor;
varying float v_alpha_factor;

void main()
    gl_FragCoord = u_matrix_view * u_matrix_model * vec4(a_position, 0, 1);
    v_color        = a_color;
    v_tex_coord    = a_tex_coord;
    v_color_factor = a_color_factor;
    v_alpha_factor = a_alpha_factor;

/* Fragment */

#version 330 compatibility

uniform sampler2D u_texture;

varying vec4 v_color;
varying vec2 v_tex_coord;
varying float v_color_factor;
varying float v_alpha_factor;

void main()
    vec4 tex_color = texture2D(u_texture, v_tex_coord);
    gl_FragColor.rgb = a_color.rgb * (1 - a_color_factor) + tex_color.rgb * a_color_factor;
    gl_FragColor.a   = a_color.a   * (1 - a_alpha_factor) + tex_color.a   * a_alpha_factor;
    // If you need premultiplied alpha: gl_FragColor.rgb *= gl_FragColor.a

Also, about the font shader: I assume you have a reason to make a separate shader for font rendering (are you using SDFs for them maybe?). But just in case your fonts are rendered as simple sprites, I'd consider using the same shader for them too.

| improve this answer | |
  • 1
    \$\begingroup\$ Thanks, this is very helpful, such a simple and elegant solution using that coefficient . And yes I think I might use distance fields for the font rendering. Also, It seems I might be able to implement some simple lighting effects in this same way, using the coefficient? \$\endgroup\$ – Curtis Sherwood Jun 26 '17 at 2:04
  • \$\begingroup\$ @CurtisSherwood Yup, you should be able to. Also, if you set a_color.a to 0, a_alpha_factor will work as opacity. \$\endgroup\$ – HolyBlackCat Jun 26 '17 at 11:13
  • \$\begingroup\$ Do you have a method for rendering circles? \$\endgroup\$ – Curtis Sherwood Jun 26 '17 at 16:37
  • \$\begingroup\$ Not really. You could make them from triangles, use a sprite or make a new shader. \$\endgroup\$ – HolyBlackCat Jun 26 '17 at 16:43

Your Answer

