TextureRenderer == Black Magic

Yes, it’s me again…and yes, it’s the same exact problem I still haven’t been able to solve. I have a concise example showing exactly the problem I’m having and a side-by-side comparison to TextureRenderer that looks exactly as I’d like my texture to appear. I’ve walked through the TextureRenderer, Texture, and TextureData classes line-by-line and still cannot find what it’s doing that makes this massive difference in appearance:

http://captiveimagination.com/download/black_magic.jpg

The first line of text is mine and the second line is TextureRenderer.

Here’s my source code:

package test;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;
import javax.media.opengl.GL;

import com.sun.opengl.util.j2d.TextureRenderer;

public class TestShape2 extends TestBasic {
	private Shape shape1;
	private Shape shape2;
	private TextureRenderer renderer;
	
	public void initialize(GL gl) {
		super.initialize(gl);
		
		try {
			shape1 = new Shape(gl); {
				BufferedImage image = ImageIO.read(getClass().getClassLoader().getResource("resource/crate.png"));
				BufferedImage crate = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB_PRE); {
					Graphics2D g = crate.createGraphics();
					g.setColor(new Color(1.0f, 0.0f, 0.0f, 1.0f));
					g.fillRect(0, 0, 256, 256);
					g.drawImage(image, 0, 0, null);
					g.dispose();
				}
				shape1.updateTexture(gl, crate);
			}
			
			shape2 = new Shape(gl); {
				BufferedImage text = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB_PRE); {
					Graphics2D g = text.createGraphics();
					g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
					Font f = new Font("Arial", Font.BOLD, 24);
					g.setFont(f);
					g.drawString("Testing", 50.0f, 50.0f);
					g.dispose();
				}
				shape2.updateTexture(gl, text);
			}
			
			renderer = new TextureRenderer(256, 256, true, true); {
				Graphics2D g = renderer.createGraphics();
				g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
				Font f = new Font("Arial", Font.BOLD, 24);
				g.setFont(f);
				g.drawString("Testing", 50.0f, 50.0f);
				g.dispose();
				
				renderer.markDirty(0, 0, 256, 256);
			}
		} catch(Throwable t) {
			t.printStackTrace();
		}
	}
	
	public void draw(GL gl) {
		gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
		
		gl.glTranslatef(-50.0f, -50.0f, -500.0f);
		
		shape1.draw(gl, 256.0f, 256.0f);
		
		shape2.draw(gl, 256.0f, 256.0f);
		
		renderer.begin3DRendering(); {
			renderer.draw3DRect(0.0f, -50.0f, 0.0f, 0, 0, 256, 256, 1.0f);
			renderer.end3DRendering();
		}
	}
	
	public static void main(String[] args) throws Exception {
		new TestShape2();
	}
}
package test;

import java.awt.Color;
import java.io.InputStream;
import java.net.URL;

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.Animator;
import com.sun.opengl.util.GLUT;

public abstract class TestBasic implements GLEventListener {
	protected GLU glu;
	protected GLUT glut;
	protected GLCanvas canvas;
	protected Animator animator;
	
	public TestBasic() {
		glu = new GLU();
		glut = new GLUT();
		
		JFrame frame = new JFrame(getClass().getSimpleName()); {
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.setSize(800, 600);
			frame.setBackground(Color.WHITE);

			GLCapabilities caps = new GLCapabilities(); {
			}
			canvas = new GLCanvas(caps); {
				canvas.addGLEventListener(this);
				animator = new Animator(canvas);

				frame.add(canvas);
			}

			frame.setVisible(true);
		}
		
		animator.start();
	}
	
	public void init(GLAutoDrawable drawable) {
		GL gl = new DebugGL(drawable.getGL());
		gl.setSwapInterval(1);												// Vertical sync
		gl.glShadeModel(GL.GL_SMOOTH);										// Enable smooth shading
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);							// Black background
		gl.glClearDepth(1.0f);												// Depth buffer setup
		gl.glEnable(GL.GL_LINE_SMOOTH);
		gl.glEnable(GL.GL_BLEND);
		gl.glEnable(GL.GL_POLYGON_SMOOTH);
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
		gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);			// Really nice perspective calculations
		gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST);
		gl.glHint(GL.GL_POLYGON_SMOOTH_HINT, GL.GL_NICEST);
		gl.glEnable(GL.GL_TEXTURE_2D);
		
		initialize(gl);
	}
	
	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
	}

	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
		GL gl = drawable.getGL();
		if (height <= 0) { // avoid a divide by zero error!
			height = 1;
		}
		float h = (float) width / (float) height;
		gl.glViewport(0, 0, width, height);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(45.0f, h, 1.0, 20000.0);
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
	}
	
	public void display(GLAutoDrawable drawable) {
		GL gl = new DebugGL(drawable.getGL());
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);		// Clear the screen and depth buffer
		gl.glLoadIdentity();
		gl.glTranslatef(0.0f, 0.0f, -5.0f);
		draw(gl);
	}
	
	public void initialize(GL gl) {
	}
	
	public abstract void draw(GL gl);

	public static final String getShaderSource(String resourceName) {
		try {
			URL url = TestBasic.class.getClassLoader().getResource("resource/" + resourceName);
			InputStream is = url.openStream();
			if (is == null) return null;
			StringBuffer buffer = new StringBuffer();
			int i;
			while ((i = is.read()) != -1) {
				buffer.append((char)i);
			}
			return buffer.toString();
		} catch(Throwable t) {
			throw new RuntimeException(t);
		}
	}
}
package test;

import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.nio.Buffer;
import java.nio.IntBuffer;

import javax.media.opengl.GL;

public class Shape {
	private int textureId;
	
	public Shape(GL gl) {
		// Generate Texture
		int[] tmp = new int[1];
		gl.glGenTextures(1, tmp, 0);
		textureId = tmp[0];
	}
	
	public final void updateTexture(GL gl, BufferedImage image) {
		SampleModel sm = image.getRaster().getSampleModel();
		int scanlineStride = ((SinglePixelPackedSampleModel)sm).getScanlineStride();
		int internalFormat = GL.GL_RGBA;
		int pixelFormat = GL.GL_BGRA;
		int pixelType = GL.GL_UNSIGNED_INT_8_8_8_8_REV;
		int alignment = 1;
		
		DataBuffer data = image.getRaster().getDataBuffer();
		Buffer buffer = IntBuffer.wrap(((DataBufferInt)data).getData());
		
		gl.glBindTexture(GL.GL_TEXTURE_2D, textureId);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE);
		gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat, image.getWidth(), image.getHeight(), 0, pixelFormat, pixelType, null);
		
		int[] align = new int[1];
		int[] rowLength = new int[1];
		int[] skipRows = new int[1];
		int[] skipPixels = new int[1];
		
		gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, align, 0);
		gl.glGetIntegerv(GL.GL_UNPACK_ROW_LENGTH, rowLength, 0);
		gl.glGetIntegerv(GL.GL_UNPACK_SKIP_ROWS, skipRows, 0);
		gl.glGetIntegerv(GL.GL_UNPACK_SKIP_PIXELS, skipPixels, 0);
		
		gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, alignment);
		gl.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, scanlineStride);
		gl.glPixelStorei(GL.GL_UNPACK_SKIP_ROWS, 0);
		gl.glPixelStorei(GL.GL_UNPACK_SKIP_PIXELS, 0);
		
		gl.glTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, image.getWidth(), image.getHeight(), pixelFormat, pixelType, buffer);
		
		gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]);
		gl.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, rowLength[0]);
		gl.glPixelStorei(GL.GL_UNPACK_SKIP_ROWS, skipRows[0]);
		gl.glPixelStorei(GL.GL_UNPACK_SKIP_PIXELS, skipPixels[0]);
	}
	
	public final void draw(GL gl, float width, float height) {
		gl.glBindTexture(GL.GL_TEXTURE_2D, textureId);
		
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
		
		gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
		
        gl.glBegin(GL.GL_QUADS); {
	        gl.glTexCoord2f(0.0f, 1.0f);
	        gl.glVertex3f(0.0f, 0.0f, 0.0f);
	        
	        gl.glTexCoord2f(1.0f, 1.0f);
	        gl.glVertex3f(width, 0.0f, 0.0f);
	        
	        gl.glTexCoord2f(1.0f, 0.0f);
	        gl.glVertex3f(width, height, 0.0f);
	        
	        gl.glTexCoord2f(0.0f, 0.0f);
	        gl.glVertex3f(0.0f, height, 0.0f);
	        
	        gl.glEnd();
        }
	}
}

Wow, I finally solved it! I really don’t understand why this would make a difference, but I was trying any random change I could make to see if there was a change and I stumbled upon it.

If I call:

[quote]gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA);
[/quote]
Right before I bind the texture it looks exactly as it should. I haven’t been doing this because I set the blend function during initialization and thought that was sufficient. Apparently it is not…can someone explain to me why I need to set this before drawing each time instead of being able to just set it once?

My only guess is that it you’re changing it somewhere else, or it’s being set in one of the JOGL utility classes. The blend mode is part of normal gl state so it won’t get reset. Perhaps one the utilities is pushing and popping state??

Anyway, this is why I don’t touch the utilities, because it’s so hard to know what it’s doing under the hood and it can play havoc with your graphics. At least for you, the havoc resulted in a better texture.

Well, after removing my foot from my mouth yet again I realized that I was calling “gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);” not “gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA);”

It’s just so easy to glaze over code that looks like it’s the same. ::slight_smile: Well, anyway, now I have to multiply my r, g, and b to set the alpha, but it works and it looks great, so I’m happy.

Thanks Riven for your help. :slight_smile:

Though I feel like an idiot for it taking so long and so many posts to figure out the problem, now that I have I hope someone else might avoid the same stupid problem. :wink: