3

I'm working on some sprite based games. For z-ordering sprites there are basically the alternatives
1) draw in correct order - can be troublesome for batching
2) use orthographic projection and depth testing - can be troublesome with translucency

I am favoring 2) however I would like to keep a perspective projection to allow some easy 3d animations. Cards being flipped in 3d etc. If I do that, the z-buffer differences may/will result in scaling of the sprites, since the objects are at different distances to the camera.
I thought about scaling the sprites based on their distance to undo the projection. But then i will get interference with 3d animated objects on top of the then "non-flat" 2d scene.

I guess the sane way is, to go for an orthographic 2d scene and do rare 3d animations in a second step with "cloned" objects on top of it. But maybe someone has a different idea?



mikkokoos answer led to a solution. Via an additional depth value the vertex shader can adjust the z coords of all vertices in clip space to push them to different depth layers. The real 3d coords can stay at z=0. Changing in clip space eliminates the perspective problems, so perspective projection can be used in the scene.
To allow for different camera angles than just perpendicular to the sprite plane, instead of pushing the sprite vertices to fixed layers they should be adjusted by "layerOffset * layerIdx". So for every layer a vertex gets pushed one step closer to the camera.

Note: With fixed layers it can be very tricky to find out the correct clip space coordinates, since the xy plane is not necessarily in the center.

Open questions: Resolution of clip space? Smallest coord difference in clip space? Possible interference between pushed sprites and real 3d elements? To prevent plane sprites from overdrawing 3d objects, all non-sprite-plane-objects should be pushed by the maximum used layer offset. Calculating the layer offset from available space in front of the camera would probably be a good idea.

Achieved: Sprites are organized on a 2d plane in a perspective projected 3d scene. Sprites are occluded based on fragment depth, calculated in the shaders. Drawing can still be done in texture batches.

Note2: I still consider doing two batched draw runs. 1st one for 2d sprites with adjusted depth, 2nd one for sprites or objects that are not "attached" to the sprite plane. Clearing depth buffer inbetween and adding an invisible sprite plane for clipping. (if no objects behind the stage are allowed)
Pics:
http://www.imagebam.com/image/109f6e356918267
http://www.imagebam.com/image/a2085f356918275

J-S
  • 425
  • 3
  • 17
  • 2
    you can write the depth yourself after the projection. https://www.opengl.org/sdk/docs/man/html/gl_FragDepth.xhtml To clarify: Render your objects with a perspective projection all at the same depth. After that use gl_FragDepth to position your objects in the right order. – dari Oct 07 '14 at 20:02
  • 1
    Damn, my mistake. I should have mentioned OpenGL ES 2.0. Afaik there is no gl_FragDepth support. Sorry! Should have said that earlier. Thank you anyway! – J-S Oct 07 '14 at 20:37
  • 1
    Use perspective projection with `glPolygonOffset` instead. https://www.opengl.org/sdk/docs/man/html/glPolygonOffset.xhtml – dari Oct 07 '14 at 20:48
  • 1
    I thought about that. But it would break apart the batched rendering calls. I would have to set the offset per sprite instance. – J-S Oct 07 '14 at 20:52

2 Answers2

2

You could use the perspective projection to render all sprites and have a uniform float "depth" for each which will substitute the gl_Position.z at the end of the vertex shader:

gl_Position.z=(u_zOffset-1.0)*gl_Position.w;

This will "flatten" the mesh, so if your meshes are more complex and depending on the used DepthFunc, you might want to have a range instead. (visible range is from -1 to 1):

gl_Position.z=((1.0+(gl_Position.z/gl_Position.w))*u_zRange+u_zOffset-1.0)*gl_Position.w;

Where u_zRange is 2f/(number_of_layers) and u_zOffset is u_zRange*layer_id. Layer 0 is the closest.

EDIT: Summary: Put everything through the MVP matrix at the same distance from the camera, then modify the Z value in the vertex shader to order them on the screen.

ovikoomikko
  • 577
  • 6
  • 11
  • Ok, this looks like an embarrassingly simple solution. But I'm not sure if i completely understand. You are not suggesting, i should just press them all flat on the same projection plane in the fragment shader, but in a certain range? Are the calls to gl_Position not going to influence the depth values of the resulting fragments and thereby breaking the z-order? – J-S Oct 09 '14 at 10:02
  • You are in clip-space at that point (see [this](http://www.songho.ca/opengl/gl_transform.html)) so they don't appear flat on the screen (normal/lighting etc calculations are not affected). What this does is influencing the z-buffer to get what you want from OpenGL. So we are not breaking it but "fixing" it :) – ovikoomikko Oct 09 '14 at 12:42
  • You are right of course. Then how would i handle 3d transformations? If i apply a 3dTransform to a sprite patch and make it stick through the 2d plane, i would have to "offset" the z coordinates accordingly? How do i make sure that all the changes in clip space don't interfere with the rendering of not aligned sprites? – J-S Oct 09 '14 at 15:07
  • The effect would be similar (same actually) to rendering each set of primitives separately to Gimp layers, then piling all the layers on top of each other. Other than that you can do to them whatever you wish. All the layers have identical perspectives but can never cross each other during one frame. If you wish some of the primitives to interact, you need to put those into the same layer. If you change the layer of a primitive, it will stay on the exact same spot on the screen (but might suddenly be behind something). – ovikoomikko Oct 09 '14 at 15:42
  • But then i have to split "planar" sprites and such ones that have been transformed out of the 2dplane into different calls? In one call without changing the fragment shader it is not possible? Then i could just do the switch between ortho and perspective anyway. That would be a bit disappointing. – J-S Oct 09 '14 at 16:02
  • You can control the layers with attributes. You need to do the z-manipulation in the vertex shader. – ovikoomikko Oct 09 '14 at 18:27
  • Its a bit more tricky i think. Please look at these images http://www.imagebam.com/image/774397356711773 http://www.imagebam.com/image/3e72a4356711777 I implemented a shader with "virtual depth" like you suggested. You can see how every second col of the blue androids is in front of the green ones. In 3d they are all at z=0. The red android is sticking through the xy plane but i cant get the occlusion to work right. If i don't set gl_position.z manually it will not overwrite any pixels of the blue androids. – J-S Oct 09 '14 at 22:46
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/62796/discussion-between-mikkokoo-and-j-s). – ovikoomikko Oct 10 '14 at 06:19
  • Ok, i got some better results. The tricky part is to find xy plane inside clip space. (esp. if you use z==0 in the shader...) – J-S Oct 10 '14 at 19:24
  • Ok, i got some better results. The tricky part is to find xy plane inside clip space. (esp. if you use z==0 in the shader...) http://www.imagebam.com/image/85745e356891934 But it only works for perpendicular cameras. http://www.imagebam.com/image/c56f29356891939 As soon as you "look at the card table from a different angle" the clip coordinates have to be adjusted. I'm not sure how hard that will be, but I',ll look into it some more. http://www.imagebam.com/image/dc0bc3356891946 – J-S Oct 10 '14 at 19:30
1

I don't know what kind of game you are developing, and how what follows might fit into your system, but have you considered the possibility of using both projections?

If the object you need to normally display are not the same as the one you need to animate in 3D, why not use the ortographic projection for the first ones and the perspective projection for the second?

Actually, even if they overlap you can probably pull off some kind of trick (if not a plain linear transition...) where the ortographic projection is substituted with the perspective one right before animating, using appropriate parameters (your mileage might vary with this however, and I confess I didn't do the math...).

Maybe it's a crazy idea, nonetheless I hope this helps

Rick77
  • 3,121
  • 25
  • 43
  • 1
    Yes, that is what i planned to do. (Until i read mikkokoos answer) The objects are the same. That's why i mentioned clones. E.g. you have a 2d card game, but you want to have some 3d animations when you draw or flip a card. Or you have a tile grid as background and want all tiles to turn around themselves or explode like particles etc. – J-S Oct 09 '14 at 09:44
  • pretty much the same usage scenarios I was thinking about (e.g. Magick the Gathering or, for the nostalgics like me, Rick Dangerous 2 http://www.mobygames.com/game/rick-dangerous-2 :) ). Kudos to mikkokoos for his (very good) answer, though I wonder, for the sake of being a killjoy, whether the use of perspective and the rounding errors will result in an unwanted scaling, if ever slight (on cheap hardware, maybe?). [Sorry, I'm doing shading work on my Nexus5 MALI and I can't help but to be on the pessimistic side, lately... :/ ] – Rick77 Oct 09 '14 at 10:11