Textures with alpha channel

Hi folks,

I have a big problem with textures, alpha-channel and the blend function. If I use simple GL_QUADS with the same color as the background, different alpha values for the box should result in the same color (and “invisibility” of the box). That’s fine, but if I now use textures with an alpha channel, the behaviour is odd.

I use gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA) and gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE) to replace the color of the rectangle with an texture created from an BufferedImage with type BufferedImage.TYPE_INT_ARGB. But only for an alpha value of 0.0 and 1.0 the result is correct. As you can see in the attached image, you can see three of five rectangles, where you shouldn’t none of them.

What makes me believe that this is a bug, is the fact, that a quick implementation with C++ works as expected (both with and without texturing). Does anyone has a clue, where the problem could be?

Also strange is, that the texturing of the small demo program is broken on a third windows machine.

import java.awt.Color;
import java.awt.image.BufferedImage;

import javax.media.opengl.DebugGL;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import javax.swing.JFrame;

import com.sun.opengl.util.FPSAnimator;
import com.sun.opengl.util.texture.Texture;
import com.sun.opengl.util.texture.TextureIO;

public class TextureTest implements GLEventListener {
	private JFrame m_frame;
	private int m_height = 128;
	private int m_width = 128;
	private Texture m_t1, m_t2, m_t3, m_t4, m_t5;
	private int m_list1, m_list2, m_list3, m_list4, m_list5;
	private boolean m_useTexture = !true;
	
	TextureTest() {
		m_frame = new JFrame("TextureTest");
		m_frame.setSize(800, 600);
		m_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		GLCapabilities glCaps = new GLCapabilities();
		glCaps.setRedBits(8);
		glCaps.setBlueBits(8);
		glCaps.setGreenBits(8);
		glCaps.setAlphaBits(8);
		glCaps.setStencilBits(8);
		glCaps.setDoubleBuffered(true);
		
		GLCanvas canvas = new GLCanvas(glCaps);
		canvas.addGLEventListener(this);
		m_frame.add(canvas);
		m_frame.setVisible(true);
		
		m_height = canvas.getHeight();
		m_width = canvas.getWidth();
		
		FPSAnimator animator = new FPSAnimator(canvas, 30);
		animator.setRunAsFastAsPossible(false);
		animator.start();
	}
	
	public void display(GLAutoDrawable _drawable) {
		GL gl = _drawable.getGL();
		gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		
		if (m_useTexture) {
			gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
			gl.glEnable(GL.GL_BLEND);
			gl.glEnable(GL.GL_TEXTURE_2D);
		}

		gl.glTranslatef(-0.8f, 0.0f, 0.0f);
		gl.glCallList(m_list1);
		gl.glLoadIdentity();

		gl.glTranslatef(-0.4f, 0.0f, 0.0f);
		gl.glCallList(m_list2);
		gl.glLoadIdentity();
		
		gl.glCallList(m_list3);

		gl.glTranslatef(0.4f, 0.0f, 0.0f);
		gl.glCallList(m_list4);
		gl.glLoadIdentity();

		gl.glTranslatef(0.8f, 0.0f, 0.0f);
		gl.glCallList(m_list5);
		gl.glLoadIdentity();

		if (m_useTexture) {
			gl.glDisable(GL.GL_TEXTURE_2D);
			gl.glDisable(GL.GL_BLEND);
		}
	}

	public void displayChanged(GLAutoDrawable _drawable, boolean _modeChanged, boolean _deviceChanged) {}

	public void init(GLAutoDrawable _drawable) {
		_drawable.setGL(new DebugGL(_drawable.getGL()));
		System.out.println("Chosen GL capabilities: " + _drawable.getChosenGLCapabilities());
		System.out.println("GL_VENDOR: " + _drawable.getGL().glGetString(GL.GL_VENDOR));
		System.out.println("GL_RENDERER: " + _drawable.getGL().glGetString(GL.GL_RENDERER));
		System.out.println("GL_VERSION: " + _drawable.getGL().glGetString(GL.GL_VERSION));
		
		GL gl = _drawable.getGL();
		
		// create textures
		m_t1 = createTexture(gl, m_width, m_height, 0.5f, 0.0f);
		m_t2 = createTexture(gl, m_width, m_height, 0.5f, 0.25f);
		m_t3 = createTexture(gl, m_width, m_height, 0.5f, 0.5f);
		m_t4 = createTexture(gl, m_width, m_height, 0.5f, 0.75f);
		m_t5 = createTexture(gl, m_width, m_height, 0.5f, 1.0f);
		
		m_list1 = createTexturedBox(gl, 0.4, 0.4, 0.5f, 0.0f, m_t1);
		m_list2 = createTexturedBox(gl, 0.4, 0.4, 0.5f, 0.25f, m_t2);
		m_list3 = createTexturedBox(gl, 0.4, 0.4, 0.5f, 0.5f, m_t3);
		m_list4 = createTexturedBox(gl, 0.4, 0.4, 0.5f, 0.75f, m_t4);
		m_list5 = createTexturedBox(gl, 0.4, 0.4, 0.5f, 1.0f, m_t5);
	}

	public void reshape(GLAutoDrawable _drawable, int _x, int _y, int _width, int _height) {
		GL gl = _drawable.getGL();
		double h = (double)_width / (double)_height;
		double viewWidth = 2 * h;
		double viewLeft = -(viewWidth / 2.0);
		double viewRight = viewWidth / 2.0;
		gl.glViewport(0, 0, _width, _height);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();

		GLU glu = new GLU();
		glu.gluOrtho2D(viewLeft, viewRight, -1.0, 1.0);
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
	}
	
	private Texture createTexture(GL _gl, int _width, int _height, float _intensity, float _alpha) {
		Color color = new Color(_intensity, _intensity, _intensity, _alpha);
		BufferedImage image = new BufferedImage(_width, _height, BufferedImage.TYPE_INT_ARGB);
		for (int x = 0; x < _width; x++) {
			for (int y = 0; y < _height; y++) {
				image.setRGB(x, y, color.getRGB());
			}
		}
		
		Texture tmpTexture = TextureIO.newTexture(image, false);
		tmpTexture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
		tmpTexture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
		return tmpTexture;
	}
	
	private int createTexturedBox(GL _gl, double _width, double _height, float _intensity, float _alpha, Texture _texture) {
		int list = _gl.glGenLists(1);
		_gl.glNewList(list, GL.GL_COMPILE);

		_texture.bind();
		_gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE);

		_gl.glColor4f(_intensity, _intensity, _intensity, _alpha);
		_gl.glBegin(GL.GL_QUADS);
		_gl.glTexCoord2d(0.0, 0.0);
		_gl.glVertex2d(-_width/2.0, -_height/2.0);

		_gl.glTexCoord2d(0.0, 1.0);
		_gl.glVertex2d(-_width/2.0, _height/2.0);

		_gl.glTexCoord2d(1.0, 1.0);
		_gl.glVertex2d(_width/2.0, _height/2.0);

		_gl.glTexCoord2d(1.0, 0.0);
		_gl.glVertex2d(_width/2.0, -_height/2.0);

		_gl.glEnd();
		_gl.glEndList();
		return list;
	}

	public static void main(String[] unused) {
		new TextureTest();
	}
}

I tried your demo and it worked fine for me. What kind of computer and graphics card do you have?

Did tried both variants (with and without) textures? I had the boolean flag m_useTexture on false by mistake. Could try it with textures please?

Oops, I retested it and it did have the same problem you did. I then tinkered with it and got it to work with this modification to createTexture(…)

ByteBuffer tex = BufferUtil.newByteBuffer(_width * _height * 4);
		for (int i = 0; i < tex.capacity(); i += 4) {
			tex.put(i, (byte)(_intensity * 255));
			tex.put(i + 1, (byte)(_intensity * 255));
			tex.put(i + 2, (byte)(_intensity * 255));
			tex.put(i + 3, (byte)(_alpha * 255));
		}
		TextureData data = new TextureData(GL.GL_RGBA, _width, _height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false, false, false, tex, null);;
		
		Texture tmpTexture = TextureIO.newTexture(data);

instead of the BufferedImage filling. This is considerably faster and gives the correct result of a solid gray background (it also works if you decrease the intensity and you’ll get a gradient). I don’t know why your code originally didn’t work, it could be a bug in the BufferedImage texture generation, or it could be the getRGB() of the color returned an incorrect result (I doubt this last one, but to be sure, try testing with a different color, with and without blending).

Thanks a lot, I’ll give it a try after some sleep.

I had a version without the Color.getRGB(). Instead I used integer and bit-shifted them correctly. So the perhaps the texture creation with a BufferedImage.

We’ll see tomorrow, and thank you again.

Cool, this did the trick. Thank you, I didn’t believe in a solution anymore, since I’m on this topic for weeks. What took me so long, was to figure out, where exactly my problem is and to put it in some demo code.

Thank you!