Jump to content
  • Advertisement
Sign in to follow this  
slicer4ever

selective depth testing?

This topic is 2460 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

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?

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites

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.

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.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!