Jump to content
  • Advertisement
Sign in to follow this  
Trapper Zoid

OpenGL Algorithms for 2D sprites in OpenGL

This topic is 4108 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
Advertisement
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
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!