Sign in to follow this  

OpenGL Algorithms for 2D sprites in OpenGL

This topic is 3948 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi everyone, I am presently reworking the architecture for my 2D sprite based engine using OpenGL with SDL, coded in C++. I am currently using intermediate mode to render textured quads for use as sprites. In my new architecture I would like to play around with some of the more streamlined rendering techniques, such as vertex arrays, to see how they affect performance. However I am trying to wrap my head around what is the best way to design the sprite rendering step of the game engine if I were to use something like vertex arrays. My main concern is assigning the textures; if I understand the process correctly only one texture is assigned for each vertex array. This may be fine if I were able to define all my sprites in the same texture as a single sprite sheet, but in most games I will need more than one. I could try to draw every sprite that is defined on the same sheet as a batch lot, although that will require some forward planning on which sprites go on which sheet and the rendering order. But then things will get tricky with the order of drawing for depths; I presently use a painter's algorithm approach, rendering from back to front, and this will be broken if I follow this approach (particularly for alpha blended sprites). I may have overlooked something as I am not yet very knowledgable about OpenGL and its capabilities. Does anyone have any suggestions on how they have or would go about the steps of rendering a series of 2D sprites using OpenGL? I only need a high level description of the process algorithm you'd use so I can wrap my head around it and look up the relevant material. Thanks!

Share this post


Link to post
Share on other sites
Since your post hasn't gotten any replies yet, I'll go ahead and bump it up :)

I'm not an expert in the area of rendering and the optimization thereof, but for what it's worth, the sprite system I'm currently using is similar to what you've described. Specifically, sprites are grouped together in sheets, and the game entities are drawn from back to front, both for occlusion and to facilitate alpha blending.

I haven't put that much thought into it, but I've tried to set up the sprite sheets logically so that sprites in the same layer are likely to be on the same sheet. In this particular game a given sprite will almost always be rendered in the same layer; if your game is different in this respect, it might be harder to group sprites together in a logical fashion.

Again, I'm not an expert on rendering, but it seems to me that there are inherent trade-offs involved. You can only render a single layer in a single draw call, and you can only bind a single texture in a single draw call, so you'll probably have to make some decisions about where to place your priorities to get the best batching behavior. (Note that I'm not considering multitexturing here...)

Share this post


Link to post
Share on other sites
Thanks for the reply Jesse; from my searchings around the forums I wasn't sure whether people were interested in 2D sprite engines anymore. There's a lot of helpful beginner tips on how to render sprites to the screen, but not a lot of the intermediate details on how to optimise the process.

Quote:
Original post by jyk
I'm not an expert in the area of rendering and the optimization thereof, but for what it's worth, the sprite system I'm currently using is similar to what you've described. Specifically, sprites are grouped together in sheets, and the game entities are drawn from back to front, both for occlusion and to facilitate alpha blending.

That seems to be the approach I see as standard from what I've read between the lines in journals and posts here, as well as from observing the art assets used in 2D games. It's the alpha blending that seems to really require the painter's algorithm approach; you can use the depth buffer to do occulsion but alpha blending doesn't work unless you render in the correct order.

Quote:
I haven't put that much thought into it, but I've tried to set up the sprite sheets logically so that sprites in the same layer are likely to be on the same sheet. In this particular game a given sprite will almost always be rendered in the same layer; if your game is different in this respect, it might be harder to group sprites together in a logical fashion.

In my current little games I'm developing as a test for the system it splits quite naturally into a series of sprite sheets. However I'm wanting to plan the base architecture in such a fashion that it can be extended to more complicated 2D games.

I haven't yet fully considered the nature of the layering system. In my present implementation there's a natural layering that arises simply from processing various stages of the graphics pipeline in different lots; one function call for the background, one for the effects on top of the background, one for the walls, one for the player character etc. I haven't yet made up my mind whether I should leave the layering very application specific, whether I should put in some inherent layering into the graphics module itself (emulating the kind of layering used in the 2D consoles), or whether I can rely on the floating point z-axis depth of polygons to give an almost infinite number of layers.

Quote:
Again, I'm not an expert on rendering, but it seems to me that there are inherent trade-offs involved. You can only render a single layer in a single draw call, and you can only bind a single texture in a single draw call, so you'll probably have to make some decisions about where to place your priorities to get the best batching behavior. (Note that I'm not considering multitexturing here...)

That seems right to me. The way things look it might just be down to the smart use of textures based on the application. This would be pretty easy these days if I were to limit my games to low resolutions. However I'd also like to play around with higher resolution modes, but unfortunately that means larger sprites and thus less that I can fit on a sprite sheet.

Thanks again for the reply. There doesn't seem to be much out there on the nitty gritty of optimising 2D engines on today's hardware; everything seems to be focused on 3D these days.

Share this post


Link to post
Share on other sites
After pondering the problem some more, I've decided it's overkill to go for the completely modular self-managed sprite graphics system. Given I'm writing this for myself I can assume that I'm savvy enough to jump through a small number of hoops to play nice with the limitations of the graphics system for the sake of efficiency.

I'll go with a sprite sheet and layer approach. This approach assumes that the game will be designed around drawing batches of sprites in layers in order for the sprites to be organised onto a single texture. Ideally each layer will have a single texture to use as a sprite sheet, although I'll put in some fallback code if that's not the case.

The idea is that the game will have to specify in advance which sprites belong to each layer. I'll support two methods; the first and easiest is to just specify a predefined texture to be the sprite sheet, which assumes I can organise the sheet beforehand in a graphics program to contain all the sprites for that sheet. The second approach is for the layer to construct its own sprite sheet; the game will give a list of sprites to be assigned to the layer, and the layer will attempt to construct a single texture which contains all the sprites. If such a texture cannot be constructed then it will form a "virtual sprite sheet" with all the same functionality of a layer but uses immediate mode with multiple textures instead. This approach assumes the game knows the maximum texture size and can make its own textures, which I'm assuming can be done in OpenGL (copying a texture of the back buffer, maybe?)

Then when the game wants to render a layer, it can give an OpenGL-like command to the graphics system to switch to a new layer, and then go through a list of game objects to render. The graphics system can then build up a vertex array if using a single sprite sheet texture, or just use immediate mode if using a virtual sprite sheet.

I think that should work reasonably well. I can design the game around using 1024 by 1024 sprite sheets as a maximum, but if for some reason the player has an old video card that cannot manage that then the game should still run, albeit slowly.



Share this post


Link to post
Share on other sites
The sprite system I'm using at the moment (which is pinched from someone elses library) handles this quite well. Each frame all the sprites are sorted, first according to texture, then according to layer/z depth. Because a stable sort is used, the overall result is a correct sorting for the depth, but with objects at the same layer sorted per texture.

Then the code goes through this list sequentially and builds bactches from the sprites next to each other using the same texture. At the end we get a sequence of batches which are as big as possible whilst still respecting the depth drawing order. The nice thing about this method is that it'll always produce a good-ish set of batches regardless of what layers or sprite sheets you throw at it, but you can also hand tweek your sprite sheets (like, putting all your background tiles together) and it'll automatically give you better batching.

The amount of information about optimal 2d rendering using OpenGL/D3D is dissappointingly slim. [sad] Which is a shame because lots of 3d techniques don't carry over well - you can't use the depth buffer because proper blending forces you to do back to front rendering. In touching individual vertices on the CPU is a big no-no if you want good throughput, but for 2d the poly count is tiny enough that you can do that in order to produce more optimum batching each frame.

Share this post


Link to post
Share on other sites
Quote:
Original post by OrangyTang
The sprite system I'm using at the moment (which is pinched from someone elses library) handles this quite well. Each frame all the sprites are sorted, first according to texture, then according to layer/z depth. Because a stable sort is used, the overall result is a correct sorting for the depth, but with objects at the same layer sorted per texture.

Then the code goes through this list sequentially and builds bactches from the sprites next to each other using the same texture. At the end we get a sequence of batches which are as big as possible whilst still respecting the depth drawing order. The nice thing about this method is that it'll always produce a good-ish set of batches regardless of what layers or sprite sheets you throw at it, but you can also hand tweek your sprite sheets (like, putting all your background tiles together) and it'll automatically give you better batching.

That's really nice. I was considering doing something like this earlier, but I thought it might be overkill at this early stage (and I have a tendency to get sucked into elaborate sorting systems). But reading your description makes me think it really isn't that hard to do. I'll see if I can factor it into the design. The graphics pipeline is really giving me the irrits at the moment as it's hard to keep it modular without putting slowdowns in the rendering code; it seems it has to be fairly tightly coupled with the game object or actor code.

Quote:
In touching individual vertices on the CPU is a big no-no if you want good throughput, but for 2d the poly count is tiny enough that you can do that in order to produce more optimum batching each frame.

Forgive my cluelessness, but what does "in touching" mean with respect to vertices?

Share this post


Link to post
Share on other sites
Quote:
Original post by Trapper Zoid
Quote:
Original post by OrangyTang
The sprite system I'm using at the moment (which is pinched from someone elses library) handles this quite well. Each frame all the sprites are sorted, first according to texture, then according to layer/z depth. Because a stable sort is used, the overall result is a correct sorting for the depth, but with objects at the same layer sorted per texture.

Then the code goes through this list sequentially and builds bactches from the sprites next to each other using the same texture. At the end we get a sequence of batches which are as big as possible whilst still respecting the depth drawing order. The nice thing about this method is that it'll always produce a good-ish set of batches regardless of what layers or sprite sheets you throw at it, but you can also hand tweek your sprite sheets (like, putting all your background tiles together) and it'll automatically give you better batching.

That's really nice. I was considering doing something like this earlier, but I thought it might be overkill at this early stage (and I have a tendency to get sucked into elaborate sorting systems). But reading your description makes me think it really isn't that hard to do. I'll see if I can factor it into the design. The graphics pipeline is really giving me the irrits at the moment as it's hard to keep it modular without putting slowdowns in the rendering code; it seems it has to be fairly tightly coupled with the game object or actor code.

Yeah, it's a pain that 2d really requires correct back-to-front rendering, as it reduces the options you've got for batching and minimising state switching. Reducing your drawn sprites to just the visible ones is an obvious help, as does packing sprites into sheets. Beyond that you've just got to do the best you can with what you've got for that frame.

I'm currently struggling with how to extend this method to cover extra GL state (currently it's just regular single-textured sprites and blending). When you add multitexturing and shaders it gets even more tricky trying to decide how to build up the baches. Even more so when you add different types of geometry (sprites rendered as a triangle, or a triangle strip for example).

Quote:
Quote:
In touching individual vertices on the CPU is a big no-no if you want good throughput, but for 2d the poly count is tiny enough that you can do that in order to produce more optimum batching each frame.

Forgive my cluelessness, but what does "in touching" mean with respect to vertices?

Erk, that should be "In 3D, touching individual vertices on the CPU is a big no-no".

Share this post


Link to post
Share on other sites
It's also worth noting that I had to write an entirely new renderer for Snowman Village (because I needed different types of geometry). The external interface is pretty close to what I'm after, but the insides are a complete mess. It doesn't even do any sorting by state, just back-to-front and hopes that the sprite sheets will avoid too much texture changing. Theres no batching either, everything is drawn in immediate mode. And it's still plenty snappy enough to get 60fps without breaking too much of a sweat.

The conclusion I draw from this is that I'd recommend spending a decent chunk of time getting the external interface nice so it's easy to use, and just do the simplest possible internal implementation to start with.

Share this post


Link to post
Share on other sites
Quote:
Original post by OrangyTang
The conclusion I draw from this is that I'd recommend spending a decent chunk of time getting the external interface nice so it's easy to use, and just do the simplest possible internal implementation to start with.

That's my plan too. I'm mainly pondering the internals of the implmenentation at the moment so I can get a good feel of what architecture I'll need. From the ideas being brought forth here, I'm thinking now it's probably best to have some sort manager component that deals with all the nitty gritty of rendering; game objects only need to have some sort of internal sprite data structure and the rendering manager can deal with the details of displaying them on the screen. To start with that can be as simple as immediate mode rendering, but then the capability is there to extend that later.

Share this post


Link to post
Share on other sites
Quote:
Original post by Trapper Zoid
Quote:
Original post by OrangyTang
The conclusion I draw from this is that I'd recommend spending a decent chunk of time getting the external interface nice so it's easy to use, and just do the simplest possible internal implementation to start with.

That's my plan too. I'm mainly pondering the internals of the implmenentation at the moment so I can get a good feel of what architecture I'll need. From the ideas being brought forth here, I'm thinking now it's probably best to have some sort manager component that deals with all the nitty gritty of rendering; game objects only need to have some sort of internal sprite data structure and the rendering manager can deal with the details of displaying them on the screen. To start with that can be as simple as immediate mode rendering, but then the capability is there to extend that later.

Yeah, that sounds pretty close to what I've got now. It's pretty quick to get running but provides you with lots of scope for internal reshuffling and optimisation in the future.

Share this post


Link to post
Share on other sites
*tags topic for later*

This'll prove useful when I rewrite my engine.

Also a question: are display lists a good or a bad thing when doing 2D?

Share this post


Link to post
Share on other sites
Quote:
Original post by Lazy Foo
*tags topic for later*

This'll prove useful when I rewrite my engine.

Also a question: are display lists a good or a bad thing when doing 2D?


DL are fine, but with the newer OpenGL models coming down the road I would stay away from DL if possible and use VBOs.

Share this post


Link to post
Share on other sites
Quote:
Original post by MARS_999

DL are fine, but with the newer OpenGL models coming down the road I would stay away from DL if possible and use VBOs.


Are there any good articles about the changes coming to OpenGL?

Share this post


Link to post
Share on other sites
Here is a link on what's coming

http://www.khronos.org/library/detail/gdc_2007_opengl_21_and_beyond/

Share this post


Link to post
Share on other sites
Vertex buffer objects look pretty intriguing; I'm not very knowledgable about OpenGL yet so I don't know much about them. I am planning to go with indexed vertex arrays for my sprite engine but I don't know enough about VBOs to know if they're a viable alternative.

A possibly newbie question: Are VBOs standard in most OpenGL driver implementations, and thus safe to use for a system wanting to target 2D games? If they're a new feature I might be best off sticking with something that's more likely to be compatible with older drivers.

Share this post


Link to post
Share on other sites
I did some quick experiments with VBO a while back, the interface is quite nice, so it's pretty easy to have a class/wrapper which uses VBO if available or regular vertex arrays otherwise.

My test was filling up a VBO with sprite data each frame and drawing that, as you'd expect for sprites. I didn't find any speed difference between using VBOs and using vertex arrays. VBOs started being faster when the same sprite data was re-rendered (maybe as some kind of multi-pass stuff?) but I havn't found a good use for this yet. So VBOs don't seem to be as useful as you'd expect for 2d stuff. [sad]

Share this post


Link to post
Share on other sites
Thing is VBO's aren't any hardered to setup than vertex arrays... So you might as well learn them instead... Plus they will be as fast and might be faster in certain situations.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Similar Content

    • By xhcao
      Does sync be needed to read texture content after access texture image in compute shader?
      My simple code is as below,
      glUseProgram(program.get());
      glBindImageTexture(0, texture[0], 0, GL_FALSE, 3, GL_READ_ONLY, GL_R32UI);
      glBindImageTexture(1, texture[1], 0, GL_FALSE, 4, GL_WRITE_ONLY, GL_R32UI);
      glDispatchCompute(1, 1, 1);
      // Does sync be needed here?
      glUseProgram(0);
      glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
      glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                     GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture[1], 0);
      glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
       
      Compute shader is very simple, imageLoad content from texture[0], and imageStore content to texture[1]. Does need to sync after dispatchCompute?
    • By Jonathan2006
      My question: is it possible to transform multiple angular velocities so that they can be reinserted as one? My research is below:
      // This works quat quaternion1 = GEQuaternionFromAngleRadians(angleRadiansVector1); quat quaternion2 = GEMultiplyQuaternions(quaternion1, GEQuaternionFromAngleRadians(angleRadiansVector2)); quat quaternion3 = GEMultiplyQuaternions(quaternion2, GEQuaternionFromAngleRadians(angleRadiansVector3)); glMultMatrixf(GEMat4FromQuaternion(quaternion3).array); // The first two work fine but not the third. Why? quat quaternion1 = GEQuaternionFromAngleRadians(angleRadiansVector1); vec3 vector1 = GETransformQuaternionAndVector(quaternion1, angularVelocity1); quat quaternion2 = GEQuaternionFromAngleRadians(angleRadiansVector2); vec3 vector2 = GETransformQuaternionAndVector(quaternion2, angularVelocity2); // This doesn't work //quat quaternion3 = GEQuaternionFromAngleRadians(angleRadiansVector3); //vec3 vector3 = GETransformQuaternionAndVector(quaternion3, angularVelocity3); vec3 angleVelocity = GEAddVectors(vector1, vector2); // Does not work: vec3 angleVelocity = GEAddVectors(vector1, GEAddVectors(vector2, vector3)); static vec3 angleRadiansVector; vec3 angularAcceleration = GESetVector(0.0, 0.0, 0.0); // Sending it through one angular velocity later in my motion engine angleVelocity = GEAddVectors(angleVelocity, GEMultiplyVectorAndScalar(angularAcceleration, timeStep)); angleRadiansVector = GEAddVectors(angleRadiansVector, GEMultiplyVectorAndScalar(angleVelocity, timeStep)); glMultMatrixf(GEMat4FromEulerAngle(angleRadiansVector).array); Also how do I combine multiple angularAcceleration variables? Is there an easier way to transform the angular values?
    • By dpadam450
      I have this code below in both my vertex and fragment shader, however when I request glGetUniformLocation("Lights[0].diffuse") or "Lights[0].attenuation", it returns -1. It will only give me a valid uniform location if I actually use the diffuse/attenuation variables in the VERTEX shader. Because I use position in the vertex shader, it always returns a valid uniform location. I've read that I can share uniforms across both vertex and fragment, but I'm confused what this is even compiling to if this is the case.
       
      #define NUM_LIGHTS 2
      struct Light
      {
          vec3 position;
          vec3 diffuse;
          float attenuation;
      };
      uniform Light Lights[NUM_LIGHTS];
       
       
    • By pr033r
      Hello,
      I have a Bachelor project on topic "Implenet 3D Boid's algorithm in OpenGL". All OpenGL issues works fine for me, all rendering etc. But when I started implement the boid's algorithm it was getting worse and worse. I read article (http://natureofcode.com/book/chapter-6-autonomous-agents/) inspirate from another code (here: https://github.com/jyanar/Boids/tree/master/src) but it still doesn't work like in tutorials and videos. For example the main problem: when I apply Cohesion (one of three main laws of boids) it makes some "cycling knot". Second, when some flock touch to another it scary change the coordination or respawn in origin (x: 0, y:0. z:0). Just some streng things. 
      I followed many tutorials, change a try everything but it isn't so smooth, without lags like in another videos. I really need your help. 
      My code (optimalizing branch): https://github.com/pr033r/BachelorProject/tree/Optimalizing
      Exe file (if you want to look) and models folder (for those who will download the sources):
      http://leteckaposta.cz/367190436
      Thanks for any help...

    • By Andrija
      I am currently trying to implement shadow mapping into my project , but although i can render my depth map to the screen and it looks okay , when i sample it with shadowCoords there is no shadow.
      Here is my light space matrix calculation
      mat4x4 lightViewMatrix; vec3 sun_pos = {SUN_OFFSET * the_sun->direction[0], SUN_OFFSET * the_sun->direction[1], SUN_OFFSET * the_sun->direction[2]}; mat4x4_look_at(lightViewMatrix,sun_pos,player->pos,up); mat4x4_mul(lightSpaceMatrix,lightProjMatrix,lightViewMatrix); I will tweak the values for the size and frustum of the shadow map, but for now i just want to draw shadows around the player position
      the_sun->direction is a normalized vector so i multiply it by a constant to get the position.
      player->pos is the camera position in world space
      the light projection matrix is calculated like this:
      mat4x4_ortho(lightProjMatrix,-SHADOW_FAR,SHADOW_FAR,-SHADOW_FAR,SHADOW_FAR,NEAR,SHADOW_FAR); Shadow vertex shader:
      uniform mat4 light_space_matrix; void main() { gl_Position = light_space_matrix * transfMatrix * vec4(position, 1.0f); } Shadow fragment shader:
      out float fragDepth; void main() { fragDepth = gl_FragCoord.z; } I am using deferred rendering so i have all my world positions in the g_positions buffer
      My shadow calculation in the deferred fragment shader:
      float get_shadow_fac(vec4 light_space_pos) { vec3 shadow_coords = light_space_pos.xyz / light_space_pos.w; shadow_coords = shadow_coords * 0.5 + 0.5; float closest_depth = texture(shadow_map, shadow_coords.xy).r; float current_depth = shadow_coords.z; float shadow_fac = 1.0; if(closest_depth < current_depth) shadow_fac = 0.5; return shadow_fac; } I call the function like this:
      get_shadow_fac(light_space_matrix * vec4(position,1.0)); Where position is the value i got from sampling the g_position buffer
      Here is my depth texture (i know it will produce low quality shadows but i just want to get it working for now):
      sorry because of the compression , the black smudges are trees ... https://i.stack.imgur.com/T43aK.jpg
      EDIT: Depth texture attachment:
      glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT24,fbo->width,fbo->height,0,GL_DEPTH_COMPONENT,GL_FLOAT,NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fbo->depthTexture, 0);
  • Popular Now