selective depth testing?

Started by
9 comments, last by slicer4ever 12 years, 1 month ago
hello all, i'm trying to achieve a certain effect in my game. basically, i want to depth test all my objects against a single object, and then disable depth testing to draw some objects ontop of others.

here's an example picture:

examplelt.png

basically, object B, C, and D, should be tested against object A, if object B and C are in front of A, then they should be drawn in front of object D.

my draw order currently is: Object A, Object D, Object B, Object C.

this is basically what it'd look like from the camera's pov:
example2o.png

any way this can be achieved easily?
Check out https://www.facebook.com/LiquidGames for some great games made by me on the Playstation Mobile market.
Advertisement
Hi!

You can turn off the writing of depth values when rendering the object D. This way D will perform the depth test against the object A, but will not alter the depth buffer. So, B and C will also only test against object A. Since they are rendered after D they will appear in front of D. That’s what you’d like to achieve, right?

So you could do something like this:
RenderA()
glDepthMask(GL_FALSE);
RenderD();
glDepthMask(GL_TRUE);
RenderBC();


Hope it helps! smile.png
thanks for the tip, and sorry that it's been awhile since i've checked back.

i thought i had come up with a decent enough solution, by forcing the object closer to the camera, but this is not proving to be the best solution.

so, my problem is a bit more complicated than i had posted, and your solution would defiantly work in the scenario i originally posted, but not for what i was trying to work on.

ok, so basically, i have a 3D tile game, and objects sit above the tiles, as such:


[media]
[/media]

in the video, i fire "Lightning balls", now then, the lightning ball should be atop the object which it occupies the tile with, but behind other objects if the camera moves as such.

(for example: i shoot a lightning ball, the inital object it's on the ball should be drawn over, but if i angled the camera so that the other monster was in front of the lightning ball, the ball shouldn't be visible.)

here's a few images to demostrate the point:
1zwgfwm.png
^^The world as it is^^

r242kn.png
^^View A, object A should be infront of object b^^


21nppax.png
^^View B, as you can see, both A+B should be hidden behind object C^^

2afk0w4.jpg
^^View B, how it should look^^

i hope i get my point across well enough.
Check out https://www.facebook.com/LiquidGames for some great games made by me on the Playstation Mobile market.
Hi!

Alright, I think I see what you like to achieve.
So, what we have:

  • Lightning balls (A)
  • Objects that are on a tile on which a lightning ball is (B)
  • Objects that are on a tile on which no lightning ball is (C )
  • Ground tiles (D)

And, what we know of the scenario:

  • (D) will always be occluder
  • (C ) will always be occluder
  • (B) will be occluder to everything else, except the (A) in their own tile.
  • (A) will be occluder, but always overdraws the (B) in the own tile.

Alright, from this we see that sooner or later every object needs to write its depth values to the depth buffer, in order to occlude other objects.
As for (A) and (B) we need some special treatment. If we turn off depth writing we can determine the rendering order. Afterwards we can make use of glColorMask which turns off the writing of color values, when we fill the depth buffer.

Okay, how do we put this in a rendering loop?
// Render (D) and (C) to the back buffer and depth buffer.
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
Render(D);
Render(C);

// Now the interesting part
foreach (B)
{
// Render only the color of (B) to the backbuffer
glDepthMask(GL_FALSE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
Render(B);

// Render the corresponding object (A) atop.
// If you have multiple (A) in a single (B) you should sort them by depth and render them back to front.
Render(A);

// Render object (A) and (B) again to fill the depth buffer, but turn off color writing.
// This makes sure that other objects of class (B) or (A) can be occluded if they are farer away.
glDepthMask(GL_TRUE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
Render(B);
Render(A);
}


I think that should work for you.
Hope it helps! smile.png

Cheers!
hey, thanks for the help, it's coming along pretty well now, but i'm running into one last problem.

basically object B&C are complicated models, by turning off the depth buffer writing, i disable the ability to sort the triangles, and am drawing the back of the model, instead of the closest color points to the camera.

i.e:
2dlj1n6.png
^^Before^^

2co1o9e.png
^^After^^

basically, what i need to do, i believe, is to temporary store the depth buffer, draw the model, than push back the saved depth buffer, and write in the lightning ball effect, and the model again.
Check out https://www.facebook.com/LiquidGames for some great games made by me on the Playstation Mobile market.
Right, that’s a problem.
It is not so easy to restore a depth buffer. That would require a copy and this is a little expensive I guess.
I think there is another way by using the stencil buffer.
We can create a mask where the object (A) is, so that object (B) won't write any pixels there.

// Render (D) and (C) to the back buffer and depth buffer.
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDisable(GL_STENCIL_TEST);
Render(D);
Render(C);

// Rendering (A) and (B).
foreach (B)
{
// Render the corresponding object (A).
// We write a 1 to the stencil buffer where the object (A) is.
glDepthMask(GL_FALSE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // Edit: Changed to exploit HiZ culling.
Render(A);

// Render the object (B) but render only where the stencil buffer
// is not 1.
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glStencilFunc(GL_NOTEQUAL, 1, 0xFFFFFFFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
Render(B);

// We render object (A) again to clear the stencil buffer here back to 0.
// If your objects are very complex it might be faster to just clear the
// stencil buffer here and write color and depth right away in
// the code above.
glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
Render(A);
}


That should do it. smile.png
Cheers!
wow, 10k thanks, it's up and working perfectly(well as far as my tests have done.). i had to make a minor change so that blending would work, in essence, i changed it as such:


// Render (D) and (C) to the back buffer and depth buffer.
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDisable(GL_STENCIL_TEST);
Render(D);
Render(C);

// Rendering (A) and (B).
foreach (B)
{
// Render the corresponding object (A).
// We write a 1 to the stencil buffer where the object (A) is.
glDepthMask(GL_FALSE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
//Changed: if depth buffer test failed, than obviously something was drawn in-front of the object, and thusly no stencil buffer.
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
Render(A);

// Render the object (B)
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDisable(GL_STENCIL_TEST);
//so that blending works, we can still draw the entire model, no need to stencil out the part we are going to be drawing over.
Render(B);
glEnable(GL_STENCIL_TEST)
// Render object (A), but only where not equal to 0(in short, we saved the previous valid depth buffer in the stencil buffer.)
// We render object (A) again to clear the stencil buffer here back to 0.
// If your objects are very complex it might be faster to just clear the
// stencil buffer here and write color and depth right away in
// the code above.
glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
Render(A);
}



i had thought of using the stencil buffer before, but it was a beast that i simply didn't understand, and only had a vague idea that it might be possible to achieve what i wanted with the stencil buffer, the only other solution i had thought of was using a fbo to store the depth buffer into.

with your suggestion of using it, i deceided to look a bit more into understanding how the stencil buffer works. while their's still alot for me to learn on using it as efficiently as possible, thanks for getting me started, and helping me with this problem.
Check out https://www.facebook.com/LiquidGames for some great games made by me on the Playstation Mobile market.
Hi!



//Changed: if depth buffer test failed, than obviously something was drawn in-front of the object, and thusly no stencil buffer.
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);


Right, good point. Setting fail and zfail to GL_KEEP allows the GPU to enable hierarchical Z culling.

Hm. If I'm seeing this correctly you’re not making use of the stencil buffer anymore.
Now B does also write depth values in the mask that was reserved for A. The point of using the stencil buffer was to prevent this.
This means, A can be occluded by B. That shouldn’t be possible, right?

If you want to have color values for B in the mask, you could draw B twice.
Once with depth writing enabled where stencil isn’t equal to 1 (outside of the mask).
And a second time with depth writing disabled where stencil is equal to 1 (inside the mask).

Cheers!

Hi!

[quote name='slicer4ever' timestamp='1328517295' post='4910080']

//Changed: if depth buffer test failed, than obviously something was drawn in-front of the object, and thusly no stencil buffer.
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);


Right, good point. Setting fail and zfail to GL_KEEP allows the GPU to enable hierarchical Z culling.

Hm. If I'm seeing this correctly you’re not making use of the stencil buffer anymore.
Now B does also write depth values in the mask that was reserved for A. The point of using the stencil buffer was to prevent this.
This means, A can be occluded by B. That shouldn’t be possible, right?

Cheers!
[/quote]

ah, i forgot to add a couple changes i made, which is probably confusing you, this is the fixed one, sorry:


// Render (D) and (C) to the back buffer and depth buffer.
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDisable(GL_STENCIL_TEST);
Render(D);
Render(C);

// Rendering (A) and (B).
foreach (B)
{
// Render the corresponding object (A).
// We write a 1 to the stencil buffer where the object (A) is.
glDepthMask(GL_FALSE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
//Changed: if depth buffer test failed, than obviously something was drawn in-front of the object, and thusly no stencil buffer.
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
Render(A);

// Render the object (B)
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDisable(GL_STENCIL_TEST);
//so that blending works, we can still draw the entire model, no need to stencil out the part we are going to be drawing over.
Render(B);
glEnable(GL_STENCIL_TEST)
//Changed: Render object (A), but only where not equal to 0(in short, we saved the previous valid depth buffer in the stencil buffer.)
// We render object (A) again to clear the stencil buffer here back to 0.
// If your objects are very complex it might be faster to just clear the
// stencil buffer here and write color and depth right away in
// the code above.

//I only care about stencil buffer being != 0(and restoring the previous stencil buffer at the same time):
glDisable(GL_DEPTH_BUFFER);
glStencilFunc(GL_NOTEQUAL, 0x0, 0xFFFFFFFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
Render(A);
glEnable(GL_DEPTH_TEST);
}



i'm fairly certain that encompasses all the changes, and should make it a bit more clear as to the process.

thanks again for putting me on the right track, i was racking my brain on how to achieve this with only the depth buffer.
Check out https://www.facebook.com/LiquidGames for some great games made by me on the Playstation Mobile market.
Alright, that looks good! :)

This topic is closed to new replies.

Advertisement