Alpha Blending

Hello,

Is there any way to draw textures with alpha on the screen without the whole picture to be semi-transparent?
I’ve tried NeHe’s tutorial about blending but I can draw images with the black color as transparent, but if there is a polygon behind the textured polygon it will show through the opaque parts of the image, no matter what the color is (if the alpha value between 0.0 or 1.0).

Does anyone know what the problem is?

Thanks in advance

Let me try to understand what’s going on:

  • you have a transparent texture close to the ‘camera’
  • you have a polygon behind the texture
  • but the polygon is showing through the texture, even when the texture’s alpha value is 1?

Assumming I’ve understood you right, first thing I’d check is to make sure that the texture is actually in-front of the polygon.
Next, I’d check to see what order you’re drawing the objects - normally when doing blending, objects are drawn farthest-first closest-last. If it’s the other way around and you don’t have depth-testing enabled, I think something wierd can happen.
Lastly, I’d check the blending function to make sure that it doesn’t alter the alpha values of the texture.

If checking the above don’t work, is it possible to provide your code for us to look at?

Well, The code I use to load textures is found here http://www.cs.plu.edu/~dwolff/talks/jogl-ccsc/src/f_Textures/TextureLoader.java

Notice that the Texture class can bind the texture to the gl context given. and it recognizes automatically which texture has alpha and which don’t. The image I am testing has an alpha channel and is in PNG format.

The code used in the init() method is

public void init(GLAutoDrawable drawable) {

		// Getting the GL object
		GL gl = drawable.getGL();
		
		// Setting a GLU object
		this.glu = new GLU();
		
		// Setting smooth shading
		gl.glShadeModel(GL.GL_SMOOTH);
		
		// Setting depth buffer and function
		gl.glClearDepth(1.0D);
		gl.glDepthFunc(GL.GL_LEQUAL);
		
		// Enabling 2D textures
		gl.glEnable(GL.GL_TEXTURE_2D);
		
		// Showing only the front face
		//gl.glEnable(GL.GL_DEPTH_TEST);
		
		// Setting nicest perspective
		gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
		
		// Setting the backgroud color to be black
		gl.glClearColor(0.0F, 0.0F, 0.0F, 0.5F);
		
		// Setting the ambient light parameters
		gl.glLightfv(GL.GL_LIGHT1, GL.GL_AMBIENT, lightAmbient, 0);
		 
		// Setting the diffuse light parameters
		gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, lightDiffuse, 0);
		gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, lightPosition, 0);
		
		gl.glEnable(GL.GL_LIGHT1);
		gl.glEnable(GL.GL_LIGHTING);
		
		/*
		 * Alpha blending
		 */
		
		// Setting alpha
		gl.glColor4f(1.0f, 1.0f, 0.0f, 0.0f);
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);

		gl.glEnable(GL.GL_BLEND);
		
		try {
			
			this.image = TextureManager.LoadTexture("ship_full.png").toGL(gl, this.glu, true);
		}
		catch (IOException ioe) {
			
			ioe.printStackTrace();
		}

Notice that depth testing is commented, as it makes weird effect such as - the alpha is black on some polygons, except for some faces which the black is transparent for them and they show through the black. I’ve tried different blending functions and turned depth testing on and off, but with no luck.

the display method() is

public void display(GLAutoDrawable drawable) {
	
		GL gl = drawable.getGL();

		// Clearing the screen
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
	
		/*
		 * Drawing a Cube
		 */

		// Resestting the modelview matrix
		gl.glLoadIdentity();

		// Move right 3.0 units
		gl.glTranslatef(0.0F, 0.0F, -6.0F);
		gl.glRotatef(this.rotRecty, 0.0f, 1.0f, 0.0f);
		gl.glRotatef(this.rotRectx, 1.0f, 0.0f, 0.0f);

		// Setting the color
		gl.glColor4f(1.0F, 1.0F, 0.0F, 1.0f);
	
		gl.glBindTexture(GL.GL_TEXTURE_2D, this.image);
		
		// Drawing the rectangle
		gl.glBegin(GL.GL_QUADS);
		{
			// Front Face
			gl.glNormal3f(0.0f, 0.0f, 1.0f);
			gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
			gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
			gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f( 1.0f,  1.0f,  1.0f);	// Top Right Of The Texture and Quad
			gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-1.0f,  1.0f,  1.0f);	// Top Left Of The Texture and Quad
			
			// Back Face
			gl.glNormal3f(0.0f, 0.0f, -1.0f);
			gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f, -1.0f);	// Bottom Right Of The Texture and Quad
			gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Right Of The Texture and Quad
			gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Left Of The Texture and Quad
			gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f( 1.0f, -1.0f, -1.0f);	// Bottom Left Of The Texture and Quad
			
			// Top Face
			gl.glNormal3f(0.0f, 1.0f, 0.0f);
			gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Left Of The Texture and Quad
			gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-1.0f,  1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
			gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f( 1.0f,  1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
			gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Right Of The Texture and Quad
			
			// Bottom Face
			gl.glNormal3f(0.0f, -1.0f, 0.0f);
			gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(-1.0f, -1.0f, -1.0f);	// Top Right Of The Texture and Quad
			gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f( 1.0f, -1.0f, -1.0f);	// Top Left Of The Texture and Quad
			gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
			gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
			
			// Right face
			gl.glNormal3f(1.0f, 0.0f, 0.0f);
			gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f( 1.0f, -1.0f, -1.0f);	// Bottom Right Of The Texture and Quad
			gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Right Of The Texture and Quad
			gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f( 1.0f,  1.0f,  1.0f);	// Top Left Of The Texture and Quad
			gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
			
			// Left Face
			gl.glNormal3f(-1.0f, 0.0f, 0.0f);
			gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f, -1.0f);	// Bottom Left Of The Texture and Quad
			gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
			gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(-1.0f,  1.0f,  1.0f);	// Top Right Of The Texture and Quad
			gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Left Of The Texture and Quad
		}
		gl.glEnd();
	
		this.rotRectx = (this.rotRectx + this.rectXmodifier) % 360;
		this.rotRecty = (this.rotRecty + this.rectYModifier) % 360;
	}

Can anyone see the problem here?

Thanks in advance.

Can you get blending working on something simpler? (saves me dealing with massive glBegin/End blocks too :P) For example, let’s just draw 2 things: 1 transparent texture close to the camera, and then 1 opaque square behind it. (sort of hybrid psuedo-code follows)

Assuming you’re working with a 640x480 sized canvas…


public void init(GLAutoDrawable drawable) {

	// Setup the viewing area
	gl.glViewport(0, 0, 640, 480);
	gl.glMatrixMode(GL_PROJECTION);
	gl.glLoadIdentity();
	gl.glOrtho(-320, 320, -240, 240, 0, 20);
	
	gl.glMatrixMode(GL_MODELVIEW);
	gl.glLoadIdentity();

	// Set-up OpenGL features, blending
	gl.glClearColor(0, 0, 0, 1);
	gl.glEnable(GL_TEXTURE_2D);
	gl.glEnable(GL_DEPTH_TEST);

	gl.glEnable(GL_BLEND);
	gl.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

	[load your texture here as well]
}

So now we have our blending function, with depth testing enabled to have things below/above one another, and our texture.
Next, write the display() method to draw our transparent texture and solid square underneath it


public void display(GLAutoDrawable drawable) {

	// Draw the solid square 
	gl.glBegin(GL_QUADS);
	{
		gl.glColor4f(1, 0, 0, 1);
		gl.glVertex3f(0, 0, -10);
		gl.glVertex3f(0, 10, -10);
		gl.glVertex3f(10, 10, -10);
		gl.glVertex3f(10, 0, -10);
	}
	gl.glEnd();

	// Draw the semi-transparent texture over the square
	gl.glBindTexture(GL_TEXTURE_2D, textureid);
	gl.glBegin(GL_QUADS);
	{
		gl.glColor4f(1, 1, 1, 0.5f);
		gl.glTexCoord2f(0, 0);	gl.glVertex2f(0, texheight);
		gl.glTexCoord2f(0, 1);	gl.glVertex2f(0, 0);
		gl.glTexCoord2f(1, 1);	gl.glVertex2f(texwidth, 0);
		gl.glTexCoord2f(1, 0);	gl.glVertex2f(texwidth, texheight);
	}
	gl.glEnd();
}

If you can get a small example to work, getting it to work with larger ones like your cube one should be a bit easier.

The blend function GL_ONE_MINUS_SRC_ALPHA gives nice results but only with the depth testing disabled, which makes the display very weird as further polygons show above the closer ones. but with depth testing enabled there are other wierd results, such as black pixels where the texture should be transparent, but some polygons show through the black pixels, and some don’t, and not all of the textures behave this way although they are the same texture on the card.

I am not trying to show a semi-transparent texture, but to show an opaque picture with the translucent and transparent areas as they sould apper, so it looks like a 2D sprite in 3D space.

Could there be a problem in the texture loading code? you can see it in the URL above.

Thanks for the help.

You need to read up on how the depth test works.

What you probably need to do is:

  1. Draw all your solid (non-blended) geometry first with both depth testing and depth writing on
  2. Enable blending.
  3. Disable depth writing ( glDepthMask(false) ) but leave depth testing enabled.
  4. Draw all your transparent geometry, sorted from furthest away from the camera first to closest to the camera last.

Well, I have never use depthMask so I don’t know what it is…
And how do I sort the polygons? how do I know which is the furthest from the camera?

To turn on/off depth writing: glDepthMask(true) or glDepthMask(false).
As for sorting polygons, you should know which is furthest from the camera, because you’re the one that put them there! :stuck_out_tongue:

It’s true, I put them there, but I rotate and move them so I don’t know what polygon is in front of the other all the time, so is there any method or tutorial to do this correctly?

about the texture which has pixels which are transparent to some polygons and some don’t, here’s a screenshot

http://img129.imageshack.us/img129/9524/cubeso4.jpg

http://img217.imageshack.us/img217/969/cubemarkedof0.jpg

Notice that there is a black line obscuring the top polygon in circle A, and the same rectangle appers to be transparent to the back polygon. This behaviour is wierd and it’s happening with depth testing on and the blend function is GL_ONE_MINUS_SRC_ALPHA.

Thanks for the help

You’ve still got depth writing on. I suggest you get a copy of the red book and read about depth testing.

I know I have depth testing on, I’ve written it :P.
But without the depth testing back polygons appear on top of the front ones.

But I will read in the red book about depth testing.

Theres a cheap trick you can use if you can’t be bothered sorting an individual object: draw it twice, first with front face culling, then with back face culling. It’ll be accurate for convex shapes and a good approximation for concave ones.

You mean enabling front face culling, then draw scene, and then back face culling and redrawing the whole thing again without depth testing?
I think you may have something here… :wink:

What I am planning is quite simple at the moment so it should not bother the card…

Thanks