2
\$\begingroup\$

I am creating a 2D program in OpenGL 3.2 Now to render what I do is have a VBO for each texture and add the elements of the objects that use that texture to this vbo. Now as you know the order you render objects will be the (fliped) order you see them on the screen.

Here is the problem, as you know its possible to sort objects by their Z value in the same VBO (by inserting them to the vbo according to their Z value). But when you want to sort them between VBOs thats impossible because they have to be in the correct VBO to have the correct texture, Meaning the VBOs that are drawn last will have positive Z value priority over VBOs that are drawn first.

This causes the Z value to transfer from the actual objects, to their textures and makes the Z value be grouped for all objects that use the same texture.

That was kinda confusing so to recap, because of the way VBOs work this is what is going to happen:

Turtule texture id 1

Box texture id 2

(Each textures has its own VBO)

There is one turtle and one box, the turtle (z = -1) is behind the box (z = 0) . if I render the box VBO then the Turtle VBO What will happen is we will see the turtle over the box even though it has a z value that is less then the box.

Here is some art I drew to help you visualize the problem:

enter image description here

^^^ That makes the Z value of the actual turtle meaning less compared to the " Z " value of the VBO. This can be worked around by having a Z value for each VBO but that limits the program because if the Z of the boxes is bigger than turtles no matter what every turtles is gonna be hidden behind every box. And its a stupid way of doing it.

So finally to the question:


What can I do to fix this, to have a meaning full Z value to each OBJECT.


Notes:

  • I cannot draw the object individually because I use drawElements and because I add them to texture coresponding VBOs to reduce the VBO count and texture binding count.

Stuff I have tried* : I have tried using the actual opengl Z-buffer, but I cannot blend layers because of that so its not a solution.

\$\endgroup\$
  • \$\begingroup\$ Are you aware that in OpenGL Z=-1 is actually in front of Z=0 using standard depth range / projection matrices? I think if you used an alpha test to discard transparent fragments you could probably get away with using the depth buffer for this; that's what it's meant for, after all. Transform your objects in the +/- Z direction depending on how you want them to be layered. In modern GL you would use a per-OBJECT model->world transform matrix for this instead of fussing with vertex data manually. \$\endgroup\$ – Andon M. Coleman Sep 7 '13 at 1:40
  • \$\begingroup\$ I am aware it's the opposite of what I said, your suggestion could work if you don't have any semi-transparent objects, which in this case I do. \$\endgroup\$ – gopgop Sep 7 '13 at 10:04
2
\$\begingroup\$

Put all the objects inside the same VBO, and use a texture atlas to put all your sprites on the same texture.

A texture atlas should look like this:

enter image description here

All you have to do is the get UV of each sprite in the atlas (which you can do using a tool like spritesheetmaker).

Don't be afraid to make a 1024x1024 texture, or a 2048x2048 texture, or even 4096x4096 textures seem to have reasonable support on GPUs these days.

\$\endgroup\$
  • \$\begingroup\$ I guess this is one way, though this is quite a limit when you create an entire game with one texture. Also this game is designed to work on systems as old as OpenGL 1.1 so I don't know if their graphics cards will support such textures. \$\endgroup\$ – gopgop Sep 6 '13 at 23:31
  • \$\begingroup\$ Once I go over 2 textures, the problem starts to appear again, as I can not have 1 texture for an entire game. \$\endgroup\$ – gopgop Sep 7 '13 at 15:15
2
\$\begingroup\$

Z values by themselves do relatively little other than change the projection of the individual vertex which for a 2D orthographic sprite game basically means they do nothing at all. If you want to have the API take Z values into account when rendering overlapping items the way you want, you need to be sure to turn on Z buffering. As you stated, though, this does not play well with blending by default; you need something more. Translucent objects must be sorted or a non-trivial order-independent translucency technique (like depth peeling) must be used.

Typically in a 2D app, however, you just sort the objects back-to-front. This is called the painter's algorithm. Sorting manually is quite easy. You don't need to change the VBO setup I think you're using (I'm not 100% clear), just change the order of your draw calls.

Even high-end 3D games still have to do this, as translucency all but requires it and high-performance rendering actually benefits quite a bit from it for opaque objects (by sorting closest first with Z-buffering on, to avoid overdraw and keeping fillrates low).

You can do the same thing for 2D, of course: sort front-to-back for opaque or "cut-out" objects (those with alpha values of only either 1 or 0), and then draw back-to-front for translucent objects. Use Z-buffering in both passes but only enable Z-writing for the first opaque object pass. This is relatively easy, gives good performance (especially on fillrate-limited hardware like most mobile devices), and shouldn't require many changes to your code.

You should also highly consider @bobobobo's advice. Atlasing is a huge benefit to performance, and is very much possible on GL 1.1 systems. You don't need a single atlas, you can have more than one. The primary benefit is that it lets you reduce state (texture changes) and minimize draw calls, which can have a huge performance impact. You can automatically generate texture atlases from your images at runtime which fits whatever the current driver's maximum texture size is, if you're really worried about compatibility with ancient/deficient drivers.

\$\endgroup\$
  • \$\begingroup\$ I heared about depth peeling but I was told not to get into it if it's for a simple 2d application. Also I know about the fact that you can simply sort the objects and render back to front but it's impossible because of the way I render currently. Read above. And about you one before last suggestion, I'll give it a shot and report in. \$\endgroup\$ – gopgop Sep 7 '13 at 10:12
  • \$\begingroup\$ Again, I cannot sort because of the VBO issue. \$\endgroup\$ – gopgop Sep 7 '13 at 15:11
  • \$\begingroup\$ Also depth peeling seems like a lot of work for a simple 2D application. Still looking for an answer. \$\endgroup\$ – gopgop Sep 7 '13 at 15:14
  • \$\begingroup\$ There are zero good reasons why you should not be able to sort; if you think you can't, you must be doing something really wacky and unnecessary. There's no simple fix for "I did my whole rendering engine wrong." \$\endgroup\$ – Sean Middleditch Sep 7 '13 at 17:30
  • 2
    \$\begingroup\$ Sorting by texture is an optimization. Sorting by depth is, as you describe it, a requirement. You optimized too early. Roll back your optimization, sort your draw calls in a way that gets the output you want, THEN optimize as necessary. \$\endgroup\$ – Justin Sep 11 '13 at 16:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.