Archived

This topic is now archived and is closed to further replies.

Telamon

ARRRGG!!! Depth-testing and Transparency

Recommended Posts

Hi. I''m working on a school project with two other people and we are adding a particle engine to our game. As a base for this, we are using the code from NeHe''s particles tutorial (I think it''s number 19). Anyways, when we turn on depth testing, the particles don''t blend right - you can see where the edges are. NeHe uses a weird alpha blend function to blend the black pixels of bitmaps onto the background (his particle images don''t have an alpha channel). When we turn depth testing off in our game to render particles, they look great. But with depth testing they don''t draw completely transparently onto what''s behind them. This is a problem because we want the particles to be occuldable. I tried turning on depth testing in NeHe''s lesson 9 (the one with the spinning particles) just to see what would happen, and it completely messed up his drawing code, just like it is in our game. What gives? Any advice would be greatly appreciated! ---------------------------------------- Let be be finale of seem, seems to me. ---------------------------------------- Coding: http://www.stanford.edu/~jjshed/coding Miscellany: http://www.stanford.edu/~jjshed

Share this post


Link to post
Share on other sites
I''ve had trouble with multiple transparent objects drawn out of order before which is (I think) what you''re having a problem with. What I did to fix it was to use alpha testing (look up glAlphaFunc) to test each fragments alpha value against a constant value. Hope that helps

Share this post


Link to post
Share on other sites
The normal way of drawing transparant stuff is:
1. Draw all solid stuff (with z-check and z-write)
2. Sort all you transparant stuff
3. Draw all you transparant stuff, back to front (with z-check, but not z-write)

Problem with this is that the transparant stuff don't occlude, but since you draw them last it usually isn't a problem.

Edit: Another thing, if you use additive blending, sorting isn't needed.

[edited by - Jolle on November 17, 2003 1:35:35 PM]

Share this post


Link to post
Share on other sites
I understand the problem: NeHe uses something called masking to get rid of the edges. The problem is when you change the color of a transparent object, the black changes color slightly to what you are changing the color to (im assuming you are using a white particle with a black background and want to turn it another color to simular fire or water or something). Also, if you have fog the color will be changed. Tell me if either of these things im saying is true then I can post a resolution to one/both

Share this post


Link to post
Share on other sites
I am having the EXACT same problem with my game right now too, I know exactly what that looks like. I think the way to do it is just what Jolle said, but can I get a description of what Z-check & Z-write mean? Is that just depth testing of some sort?

Thanks

Share this post


Link to post
Share on other sites
Hey, this is just off the top of my head so i may be wrong but I think if you do this:

glDepthMask(false);
//Draw Particles
glDepthMask(true);

that should solve the problem.

Luke.

Hyperdev

"To err is human, to really mess up requires a computer"

Share this post


Link to post
Share on other sites
Z-check: Compare z-value against the value in the z-buffer and only draw the fragment if it is greater/smaller/whatever. To disable z-check, glDepthFunc(GL_ALWAYS) can be used. To turn it back on, use glDepthFunc(GL_LEQUAL) (or GL_LESS/GL_GREATER/whatever).

Z-write: Whatever you write the fragments z-value to the z-buffer or not. To disable, use what Lukerd said.

Another note: On a second thought, if you actually sort the transparant stuff, disabling Z-write isn''t needed.

So you shouldn''t then have to worry at all about z-write/z-check. Simply use what you usually use.

Share this post


Link to post
Share on other sites
quote:
Original post by Lukerd
Hey, this is just off the top of my head so i may be wrong but I think if you do this:

glDepthMask(false);
//Draw Particles
glDepthMask(true);

that should solve the problem.

Luke.



what he said.


if its mixed with solid objects, try this


glEnable(GL_DEPTH_TEST);
DrawNonAlphaObjects();
glDepthMask(false);
DrawAlphaObjects();
glDepthMask(true);
glDisable(GL_DEPTH_TEST);


using teh depth mask tells it to check, but not update the depth buffer. Very handy (and it also increases speed).

Share this post


Link to post
Share on other sites
Also, the Blending Factors in BlendFunc that are used in NeHe's code are kind of stupid. Try out different Blending Factors to see what works best.

The eight blending factors are:

GL_ZERO
GL_ONE
GL_SRC_COLOR
GL_ONE_MINUS_SRC_COLOR
GL_SRC_ALPHA
GL_ONE_MINUS_SRC_ALPHA
GL_DST_ALPHA
GL_ONE_MINUS_DST_ALPHA

Or something like that.

If it has no Alpha Channel, you are somewhat limited, but if you want black to be transparent, use (GL_ONE,GL_ONE_MINUS_SRC_COLOR). I think that should work.

Edit: You could also use (GL_ONE,GL_ONE) which would be like screening in Photoshop. The Dark Colors would not show up at all, but the Light ones will. This would probably be better.

--------------------------------------
I am the master of stories.....
If only I could just write them down...

[edited by - Nathaniel Hammen on November 21, 2003 12:49:20 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
EXCELLENT, I will test out some of those blending equations... I think the (GL_ONE,GL_ONE_MINUS_SRC_COLOR) is the setup I want... so that the black doesn''t show up. I guess if I wasn''t a lazy bumb I could have looked it up myself. THANKS!!!!

Share this post


Link to post
Share on other sites
A blending equation of GL_ONE, GL_ONE_MINUS_SRC_COLOR means that you final pixel = Rs + Rd(1 - Rs), Gs + Gd(1 - Gs), Bs + Bd(1 - Bs), where Rs, Gs and Bs = Red, Green and Blue components of the fragment you are adding to the framebuffer and Rd, Gd and Bd = Red, Green and Blue components of the pixel already in the framebuffer.

Lets test this with a black fragment blended into an arbitrary coloured pixel in the framebuffer:

final pixel = 0 + Rd(1 - 0), 0 + Gd(1 - 0), 0 + Bd(1 - 0)
final pixel = Rd, Gd, Bd

so black fragments become transparent.

And for a white fragment:

final pixel = 1 + Rd(1 - 1), 1 + Gd(1 - 1), 1 + Bd(1 - 1)
final pixel = 1, 1, 1

so white fragments are fully opaque.

People seem to get very confused about blending equations, so lets look at all the possible blending equations not involving alpha (there are 16 in total):

*There are bound to be mistakes in here somewhere - if you spot one, let me know and I''ll edit this post*

GL_ZERO, GL_ZERO:
final pixel = 0, 0, 0
final pixel is always black.

GL_ZERO, GL_ONE:
final pixel = Rd, Gd, Bd
framebuffer does not change.

GL_ZERO, GL_SRC_COLOR:
final pixel = RdRs, GdGs, BdBs
final pixel is the modulation of the two inputs. Blending in a white fragment leaves the framebuffer unchanged. Blending in a Pure green fragment removes the Red and Blue components from the framebuffer.

GL_ZERO, GL_ONE_MINUS_SRC_COLOR:
final pixel = Rd(1 - Rs), Gd(1 - Gs), Bd(1 - Bs)
pixel from frame buffer is reduced in intensity, depending on colour blended in. Blending in a white fragment will result in a black pixel output. Blending in a pure green fragment will remove the green component of the framebuffer pixel.

GL_ONE, GL_ZERO:
final pixel = Rs, Gs, Bs
framebuffer pixel is replaced by the blended fragment.

GL_ONE, GL_ONE:
final pixel = Rs + Rd, Gs + Gd, Bs + Bd
blended fragment and framebuffer pixel are added. Blending in a black fragment leaves the framebuffer unchanged. Blending in a pure green fragment makes the output pixel ''more green''.

GL_ONE, GL_SRC_COLOR:
final pixel = Rs + RdRs, Gs + GdGs, Bs + BdBs
this one is more complicated. Blending in a black fragment results in a black pixel. Blending in a pure green fragment results in a pure green pixel. Blending in a 50% blue fragment results in a blue pixel of intensity 0.5 + (framebuffer blue intensity * 0.5).

GL_ONE, GL_ONE_MINUS_SRC_COLOR:
final pixel = Rs + Rd(1 - Rs), Gs + Gd(1 - Gs), Bs + Bd(1 - Bs)
this one we''ve already seen. Blending in a pure green fragment results in a pixel with Red and Blue components from the framebuffer and pure Green component.

GL_DST_COLOR, GL_ZERO:
final pixel = RsRd, GsGd, BsBd
identical to GL_ZERO, GL_SRC_COLOR

GL_DST_COLOR, GL_ONE:
final pixel = RsRd + Rd, GsGd + Gd, BsBd + Bd
same as GL_ONE, GL_SRC_COLOR but roles of blended fragment and framebuffer pixel are reversed. Blending in a black fragment leaves the framebuffer unchanged. Blending in a pure Green fragment doubles the Green component of the framebuffer.

GL_DST_COLOR, GL_SRC_COLOR:
final pixel = RsRd + RdRs, GsGd + GdGs, BsBd + BdBs
final pixel is the double the modulation of the two inputs. Blending in a white fragment doubles the brightness of the framebuffer fragment. Blending in a Pure green fragment removes the Red and Blue components from the framebuffer and doubles the Green component.

GL_DST_COLOR, GL_ONE_MINUS_SRC_COLOR:
final pixel = RsRd + Rd(1 - Rs), GsGd + Gd(1 - Gs), BsBd + Bd(1 - Bs)
simplify the equation and you''ll see that this is exactly the same as GL_ZERO, GL_ONE.

GL_ONE_MINUS_DST_COLOR, GL_ZERO:
final pixel = Rs(1 - Rd), Gs(1 - Gd), Bs(1 - Bd)
same as GL_ZERO, GL_ONE_MINUS_SRC_COLOR but roles of blended fragment and framebuffer pixel are reversed. Blending in a black fragment results in a black pixel. Blending in a white fragment results in the inverse of the original framebuffer pixel. Blending in a pure Green fragment results in a green pixel of intensity = 1 - intensity of original framebuffer pixel.

GL_ONE_MINUS_DST_COLOR, GL_ONE:
final pixel = Rs(1 - Rd) + Rd, Gs(1 - Gd) + Gd, Bs(1 - Bd) + Bd
identical to GL_ONE, GL_ONE_MINUS_SRC_COLOR.

GL_ONE_MINUS_DST_COLOR, GL_SRC_COLOR:
final pixel = Rs(1 - Rd) + RdRs, Gs(1 - Gd) + GdGs, Bs(1 - Bd) + BdBs
identical to GL_ONE, GL_ZERO

GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR:
final pixel = Rs(1 - Rd) + Rd(1 - Rs), Gs(1 - Gd) + Gd(1 - Gs), Bs(1 - Bd) + Bd(1 - Bs)
errr... Blending in a white fragment results in the inverse of the original framebuffer pixel. Blending in a black fragment results in a black pixel. Blending in a red pixel of 40% intensity results in a red pixel of intensity = 0.4 + 0.2 * red intensity of original framebuffer pixel.

Hope this helps people to get a handle to the blending equations,

Enigma

Share this post


Link to post
Share on other sites