Alpha-transparent quads

Nov 14, 2008 at 1:49 PM
Edited Nov 14, 2008 at 1:52 PM
Hello all.
I decided to create transparent textured quads based on the "custom geometry" tutorial, so I changed vertex type to VertexPositionTexture, loaded some random .png texture into the material texture map and set this material alpha property to 1. In the Draw() routine I set render state's alpha blend property to AlphaBlendState.Alpha. I use orthographic camera rotated by LookAt() method.

Well, it works fine from the first glance, but something strange happens to transparency when I try to position quads in different ways. For example, there are two row of quads, the second row quads correctly overlap the first row quads, but when camera rotates in a way that the first row quads can overlap the second row quads, glitch occurs. Somehow clear buffer color is being drawn instead of transparency.

It's kinda hard to describe, so here are some pics (~30 kb each):
Rotation starts, quads overlap fine
Just before first row overlaps second row
Here is the glitch

Where could it have gone wrong? Any help is really appreciated^^
Coordinator
Nov 14, 2008 at 10:07 PM
Edited Nov 14, 2008 at 10:33 PM

Hello Vigil.

This is quite a simple problem you are describing. I'll explain:

When you are drawing your quads, your graphics card doesn't know anything about what you are intending. All it knows is that you are drawing a number of quads. These quads have a texture applied, and they also have a blend mode active (in your case, alpha blending). That is pretty much all the video card has to go on.

So what you are seeing here is actually correct behaviour - correct for what you are asking the video card to draw.

The quads are being drawn, and importantly, when they draw them they are writing depth values into the depth buffer. When the quads in the back are drawn (and look to be being drawn last) they don't get completely drawn, because the quads in front are closer to the camera, and have already written closer values into the depth buffer (so the depth test fails for part of the back row quads).

You can disable depth testing entirely (which basically turns the depth buffer off), or turn off just depth writing (this can be done in the render state).. But this won't really help the fact the quads are being drawn in the worng order when the camera is behind them.

There are many things you can do to help fix this problem.

The really easy solution is to use Alpha Testing. However this won't completly fix the problem (you will still get errors at the feathered edges of your textures).

What alpha testing does, is applies a simple equation to the pixels being drawn. If they fail to pass the equation, then they don't get drawn. The alpha testing mode you would want is a comparison of 'greater' with a reference of zero. What this would do, if for every pixel in the quad, it will only get drawn if the alpha value is greater than zero. So all pixels with an alpha of zero don't get drawn. (this may be a tad confusing, because an alpha value of zero means the pixel is completely transparent - even so, it still gets drawn, it just doesn't make a visible difference - it still gets drawn to the depth buffer).

The other option is to be performing some type of depth sorting. If you have all your dragons in rows, then the easy way is to draw each row one at a time, the problem is you should always draw back-to-front, that is, draw them in order of furthest away to closest. This is why you are having a problem when the camera rotates around behind the rows, suddenly, the order has flipped. This isn't a trivial thing to implement, and there really isn't an easier way to do it. (Sadly there is no magic graphics card feature that will fix this for you).

If you were to go this route, I would suggest that you have a special IDraw object that is in charge of sorting the elements. It could contain a List<T> of your elements, and call Sort() before it loops through drawing them. The element class would then need to implement IComparable<Element>, and the compare method would need to compare depths based on the camera location. It's certainly not an easy solution. (you would also want to pre-calculate the depth of each element before doing the sort)

The final option is to simply not rotate your camera that much :-)

I hope that helps. Let me know if something is not clear

Coordinator
Nov 14, 2008 at 10:42 PM

Here is an example of how to do sorting:

http://pastebin.com/m2e88c95

Nov 15, 2008 at 9:33 AM
Oh, I was naive. Thank you for a really great explanation, additional thanks for the code sample!^^