Texture grief

Howdy chaps,

I’m possibly opening myself up to some “LMGTFY” replies here, but I’m having some major trouble understanding the upload of 2d textures in LWJGL/OpenGL. I’ve been focusing on it all afternoon, but don’t seem to be getting anywhere.

Assuming I’ve got an int[] filled with image data in the form of ARGB, how can I:

a) Prepare the data for OpenGL. Can I simply cram the data into an IntBuffer? (It appears I’ll have to change ARGB to RGBA, but that should be trivial). I see one form of glTexImage2D() takes an IntBuffer, so I’m assuming that’s all good?

b) Describe the data to OpenGL. Given I’m using the aforementioned RGBA IntBuffer, what should my call to glTexImage2D() look like? I’m struggling to understand the parameters, in particular internalformat, format and type. I’ve looked at the description in the OpenGL reference, but can’t make head nor tail of it. Is anyone able to provide a more in-depth description of the parameters and their use?

Just for reference, the arguments to glTexImage2D are (with the params in bold giving me grief):
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, java.nio.IntBuffer pixels)

Much appreciated,
nerb.

LMGTFM:

  • So internalformat is how OpenGL should represent the texture data internally. All I want to do is display a sprite directly, as is, so I’m guessing GL_RGBA should suit my needs?
  • format is the format of my pixel data. If I’ve got RGBA packed into an int, do I want to use GL_RGBA again?
  • type is the type of data I’m uploading. Now this one confuses me a little. The data is packed into standard Java signed 32bit ints… So what type should I select? GL_INT?

See here:

And some info here on buffers:

  • Usually just stick with RGBA for internalFormat.
  • “format” depends on your buffer. If your integers are ARGB, then you need to use GL_ARGB (assuming it’s supported).
  • This would make sense. But all of demos I’ve seen either use unsigned byte, or unsigned int as data type. Odd since Java doesn’t have any unsigned types. So it may just be that ByteBuffer/IntBuffer need GL_UNSIGNED_BYTE and GL_UNSIGNED_INT, respectively. You can use also use GL_UNSIGNED_INT_8_8_8_8. Maybe somebody with more expertise on endianness and NIO buffers can weigh in on that.

If you’re trying to grab data from a BufferedImage, see here:

Thanks very much for the reply davedes. The tutorial seemed to clear things up for me (or at least I thought…). It appears GL_UNSIGNED_INT_8_8_8_8 is the one to use if RGBA data is packed into an int. GL_UNSIGNED_INT seemed to cause OpenGL to expect an int for each channel.

All said and done, I’m still not problem-free. I’ve extracted my image data, pushed it off to OpenGL, however when I go to use the texture in my shaders I get nothing more than a black screen.

I’ve ripped up a quick example and dumped it in the pastebin, if anyone would care to have a look: http://pastebin.java-gaming.org/b069d24847b. I’ve created a giant, ugly init() method, just so that you can easily follow my workflow. Obviously the imgPath variable will need to be changed to point to your own image too. If the example was working correctly, it should’ve covered the screen in the chosen texture. (Nothing fancy, just a square filling the screen, no transformations or anything).

I’m not quite sure what I’m doing wrong. I’m guessing I haven’t set some required state, or am doing something silly in my shaders?

Cheers muchly.

PS. Not sure if it is of any relevance, but my texture was a 128x128 32bit png. The RGBA data appeared as expected in the pixel ByteBuffer. I also checked my texture coordinates, and they are behaving as expected too.

I have trouble loading texture to opengl as well. I took this code from some website and modified for my own needs. You need to have a class folder in your project for this to work.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import javax.swing.ImageIcon;

import org.lwjgl.opengl.GL11;

public class SpriteSheet {

	private static List<SpriteSheet> sheets = new ArrayList<SpriteSheet>();
	private static ColorModel glAlphaColorModel = new ComponentColorModel(
			ColorSpace.getInstance(ColorSpace.CS_sRGB),
            new int[] {8,8,8,8},
            true,
            false,
            ComponentColorModel.TRANSLUCENT,
            DataBuffer.TYPE_BYTE);
	private static int target = GL11.GL_TEXTURE_2D;
	

	public static SpriteSheet sheet = new SpriteSheet("/image.png");
	
	public int id = 0;
	public int width, height;
	public String name;
	
	public SpriteSheet(String name) {
		
		this.name = name;
		
		try {
		
			BufferedImage image = loadBufferedImage(name);
			ByteBuffer imageByteBuffer = convertImageData(image);
			
			id = GL11.glGenTextures();
			
			bind();
			
			GL11.glTexParameteri(target, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
			GL11.glTexParameteri(target, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
			
			this.width = image.getWidth();
			this.height = image.getHeight();
			
			GL11.glTexImage2D(
					target,
					0,
					GL11.GL_RGBA,
					image.getWidth(),
					image.getHeight(),
					0,
					GL11.GL_RGBA,
					GL11.GL_UNSIGNED_BYTE,
					imageByteBuffer);
			
			
			sheets.add(this);
		
		}catch(IOException e) {
			e.printStackTrace();
		}
	}
	
	public SpriteSheet(int id, int width, int height) {
		this.id = id;
		this.width = width;
		this.height = height;
		this.name = "none";
	}
	
	@SuppressWarnings("rawtypes")
	private ByteBuffer convertImageData(BufferedImage image){
		
		ByteBuffer textureByteBuffer = null;
		WritableRaster raster = null;
		BufferedImage bufImage = null;
		
		raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, image.getWidth(), image.getHeight(), 4, null);
		bufImage = new BufferedImage(glAlphaColorModel, raster, false, new Hashtable());
		
		Graphics g = bufImage.getGraphics();
		g.drawImage(image, 0, 0, null);
		
		byte[] imageBytes = ((DataBufferByte) bufImage.getRaster().getDataBuffer()).getData();
		
		textureByteBuffer = ByteBuffer.allocateDirect(imageBytes.length);
		textureByteBuffer.order(ByteOrder.nativeOrder());
		textureByteBuffer.put(imageBytes, 0, imageBytes.length);
		textureByteBuffer.flip();
		
		return textureByteBuffer;
	}
	
	private BufferedImage loadBufferedImage(String name) throws IOException {
		
		URL url = SpriteSheet.class.getResource(name);
		
		Image img = new ImageIcon(url).getImage();
		
		BufferedImage image = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
		 
		Graphics g = image.getGraphics();
		
		g.drawImage(img, 0, 0, image.getWidth(), image.getHeight(), null);
		
		g.dispose();
		
		int[] pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
		
		Color color = new Color(0, 0, 0, 0);
		for(int i=0;i<pixels.length;i++){
			if(pixels[i] == -65281) image.setRGB(i % image.getWidth(), i / image.getWidth(), color.getRGB()); // Replace certain colors with alpha
		}
		
		return image;
		
	}
	
	
	public void bind() {
		GL11.glBindTexture(target, id);
	}
	
}

When you want to create a Spritesheet, you type in path from your class folder to the file. Just study this you will understand.

Arrrgh… I think I’ve found my problem! I hadn’t defined the min & mag filters (glTexParameteri() and all that). I thought they would default to something reasonable if I didn’t explicitly state it, but obviously not.

Thanks to all that looked at it (and cheers for your reply nonetheless trollwarrior).