OpenGL alpha Blending

BlitzMax Forums/OpenGL Module/OpenGL alpha Blending

Drey(Posted 2005) [#1]
From what i read, the only way to get alpha blending to work correctly is to first render a opaque(skybox/terrain) after all the opaque meshes are rendered..you have to sort the alpha meshes where the furthest mesh is rendered first and the nearest mesh rendered last. You can't render Alpha meshes in any type of order?

I tried to do some z buffer tricks, not working out yet.

Is this something you have to manage in DirectX as well or does it handle Alphas in any order?


Tom(Posted 2005) [#2]
Correct!

Let's see if I remembered this correctly.

You can render opaque geometry in any order because the depth buffer takes care of what's drawn to the screen.

But if you had a cube and the texture had an alpha circle in the middle of each face (a hole), that geometry on the cube where the 'hole' is still gets written to depth buffer.

If you tried to draw any geometry behind it, you wouldn't see it through the hole part of the alpha cube, as the depth buffer test wouldn't allow it.

So, you turn off *writing* to the depth buffer when drawing any geometry that uses alpha textures, AND, you draw the alpha geometry last.

Note: That's writing off, testing still on!

Now because you're not writing to the depth buffer when drawing alpha geometry, when drawing successive alpha geometry you can not depth test against previously drawn alpha geometry, so you also need to sort the drawing order of the alpha geometry manualy.

How you Z sort is upto you, currently I'm just doing an entity radius check, not ideal! One solution might be what Beaker and others have done in various Blitz3D grass demos, sort the polygons based on a world axis distance check from your camera.

Example: if the camera is majorly facing -Z (forward in GL) then sort the geometry based on each polys average Z distance from the camera. Once the camera rotates over 45degrees to the right or left, check the X axis distance e.t.c.

Works well in the grass demos I've seen and the beauty of it is you only need build the axis distance arrays 1 time (so long as it's static geometry!) :)

Hope I got some of that correct :P

Cya!
Tom


Drey(Posted 2005) [#3]
ha, ok. I thought of that solution and i said "nah, there has to be a better way." Just disappointing, maybe the sorting is actually faster than what needs to be done to get the alphas to blend correctly with the Z buffer. It seems as simple as a "is alpha flag" for that pixel. If the pixel is alpha, an extra TRUE or FALSE is bound to that z buffer pixel, only when an non alpha pixel comes in may there be a write to the z buffer based on the Depth_TEST conditions. Otherwise, if the mesh is behind the Z buffer with the Alpha flag, it'll just rendered with the blend. That'll probably make it where sorting doesn't matter. The people whom make OpenGL are very smart as well, there is probably good reason why this isn't an option. Is it just pure speed? Having a glEnable( GL_DEPTH_ALPHA_TEST ) wouldn't fix that? I'm just curious now.


Chris C(Posted 2005) [#4]
errrm, I have a bunch of alpha blended trees (you can see through the branches) I dont do any special sorting
and it works just fine...
I can make 2 trees and move either one in front or behind the other and still see through the branches

AFAIK heres all the opengl states I'm using
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glEnable(GL_DEPTH_TEST)
glClearDepth(1.0)						
glDepthFunc(GL_LEQUAL)						
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)		
glEnable(GL_BLEND)						
glAlphaFunc(GL_GREATER,0.1)
glEnable(GL_ALPHA_TEST)						
glEnable(GL_TEXTURE_2D)						
glEnable(GL_CULL_FACE)



Tom(Posted 2005) [#5]
Doesn't that draw them masked ala Blitz3D? i.e. a pixel is either fully transparent or fully opaque?

I think that's what the glAlphaFunc(GL_GREATER,0.1) is all about, it tests the alpha value, if it's greater than 0.1 the pixel fragment is discarded.

This is a solution if you want to use masked textures, good for grass, foliage and other stuff.


Chris C(Posted 2005) [#6]
ah thought that was what he was after...


Leiden(Posted 2005) [#7]
I think he wants the sexy 8 bit alpha will 255 different variations of alpha blending, in which case you can take a look at this:

http://www.opengl.org/resources/tutorials/advanced/advanced97/notes/node111.html

Ill make a small BlitzMax demo up that has the alpha he is looking for.


Leiden(Posted 2005) [#8]
Here it is,


http://i2.net.nz/leiden/test.zip


Drey(Posted 2005) [#9]
well, you helped with texturing and i have to thank you for that. My issues with Alpha blending is this tho


I have Object1 and Object2, both are transparent.

Object1 renders first, then Object2.


C = camera, 1 = Object1, 2 = Object. "->" is the direction of the camera.

Diagram 1
C-> 2, 1

Aphla works like expected

Diagram 2
C-> 1,2

Object 1 does not appear transparent.

I've be reading that I'll have to render object1 after object2.


Leiden(Posted 2005) [#10]
Have you got in you're states?

glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_NICEST) 'I think thats what it is...



Leiden(Posted 2005) [#11]
I took a look at an example and saw what you meant, Unfortunately the only way to avoid that is to:

1) Disable depth testing while using blending.
2) Write your own scene graph which will handle it for you

Heres the code I used to test what happens, Comment out both the glDisable() states in the main loop and you get an example of what you meant:
Strict

GLGraphics 800, 600

Global Texture = LoadPixmap("tex.png")
Global GLTex = GLTexFromPixmap(Texture)
Global Z:Float = 2.0

glEnable(GL_BLEND)
glEnable(GL_TEXTURE_2D)
glBlendFunc(GL_SRC_ALPHA, GL_ONE)

gluPerspective(45.0, Float(800) / Float(600), 1.0, 1000.0)

While Not KeyDown(KEY_ESCAPE)
	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)
	glBindTexture(GL_TEXTURE_2D, GLTex)

	glEnable(GL_ALPHA_TEST)
	glDisable(GL_DEPTH_TEST)
	glPushMatrix()
	glBegin(GL_QUADS)
		glTexCoord2f (0.0, 0.0)
		glVertex3f (-5.0, -5.0, -20.0)
		glTexCoord2f (1.0, 0.0)
		glVertex3f (5.0, -5.0, -20.0)
		glTexCoord2f (1.0, 1.0)
		glVertex3f (5.0, 5.0, -20.0)
		glTexCoord2f (0.0, 1.0)
		glVertex3f (-5.0, 5.0, -20.0)
	glEnd()
	glPopMatrix()
	
	glPushMatrix()
	If KeyDown(KEY_W) Z :- 0.1
	If KeyDown(KEY_S) Z :+ 0.1
	glTranslatef(2.0, 0.0, Z)
	glBegin(GL_QUADS)
		glTexCoord2f (0.0, 0.0)
		glVertex3f (-5.0, -5.0, -20.0)
		glTexCoord2f (1.0, 0.0)
		glVertex3f (5.0, -5.0, -20.0)
		glTexCoord2f (1.0, 1.0)
		glVertex3f (5.0, 5.0, -20.0)
		glTexCoord2f (0.0, 1.0)
		glVertex3f (-5.0, 5.0, -20.0)
	glEnd()
	glPopMatrix()
	glEnable(GL_DEPTH_TEST)
	glDisable(GL_ALPHA_TEST)
	
	GLDrawText("Press ESC To Quit... W / S = Forward / Back", 10, 10)
	Flip
Wend
End



Tom(Posted 2005) [#12]
A scene graph is what I'm using.

I traverse hierarchialy through every entity in the scene multiplying its *local* matrix along the way, if I come across an entity that uses an alpha surface I add it to a list of alpha entitys to be rendered later.

At this point I also record the current GL_MODELVIEW matrix! (that way when I do render them I don't need to multiply all the matrices again to get to the proper modelview matrix. Instead I can just set the saved matrix as the current modelview one.) Works good!

Before that of course you need to sort them, as I said before I'm using a simple camera>entity radius check for now.