texture fidelity -- boiled down to 130lines

Started by ouattwtym, February 26, 2011, 13:31:42

Previous topic - Next topic

ouattwtym

/*
 * I've been trying and failing to get an -exact- blit of a 2D sprite and have boiled it down to this:
 * 
 * This example makes and draws a 16x16 texture of rgb:f2/f2/f2
 * resulting in a 16x16 on-screen box of an entirely different colour: rgb:ef/f3/ef
 * which is very strange!
 * 
 * Please help a noob who's pulling his hair out and point out what I'm not seeing :)
 */

import java.nio.ByteBuffer;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

public class TextureExample {
	private static final int SCREEN_HEIGHT = 600;
	private static final int SCREEN_WIDTH = 800;

	private static final int TEXTURE_WIDTH = 16;
	private static final int TEXTURE_HEIGHT = 16;
	private static final int TEXTURE_PIXELS = TEXTURE_WIDTH * TEXTURE_HEIGHT;
	private static final int TEXTURE_CHANNELS = 3;
	private static final int TEXTURE_BUFFER_SIZE = TEXTURE_CHANNELS * TEXTURE_PIXELS;

	private int textureId;

	private void bindTexture() {
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
	}


	private void initLwjglDisplay() {
		try {
			Display.setDisplayMode(new DisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT));
			Display.create();
			Display.setVSyncEnabled(true);
		}
		catch (LWJGLException e) {
			throw new RuntimeException(e);
		}
	}
	private void initOpenGL() {
		GL11.glEnable(GL11.GL_TEXTURE_2D);
		GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

		// GL11.glEnable(GL11.GL_BLEND);
		// GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
		GL11.glDisable(GL11.GL_BLEND);
		GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_DECAL);

		GL11.glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		GL11.glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 1, -1);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glDisable(GL11.GL_DITHER);
	}

	private void initTexture() {
		textureId = GL11.glGenTextures();
		bindTexture();
		ByteBuffer buffer = ByteBuffer.allocateDirect(TEXTURE_BUFFER_SIZE);
		for (int x = 0; x < TEXTURE_PIXELS; x++) {
			for (int c = 0; c < TEXTURE_CHANNELS; c++) {
				buffer.put((byte) 0x0f2);
			}
		}
		buffer.rewind();

		GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0,
				GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);
	}

	/**
	 * Draw a quad with the texture on it.
	 */
	private void render() {
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
		bindTexture();

		float destination = 100f;
		int scale = 1;

		GL11.glBegin(GL11.GL_QUADS);
		GL11.glTexCoord2f(0, 0);
		GL11.glVertex2f(destination, destination);
		GL11.glTexCoord2f(1, 0);
		GL11.glVertex2f(destination + TEXTURE_WIDTH * scale, destination);
		GL11.glTexCoord2f(1, 1);
		GL11.glVertex2f(destination + TEXTURE_WIDTH * scale, destination + TEXTURE_HEIGHT * scale);
		GL11.glTexCoord2f(0, 1);
		GL11.glVertex2f(destination, destination + TEXTURE_HEIGHT * scale);
		GL11.glEnd();
	}

	private void startTheExample() {
		initLwjglDisplay();
		initOpenGL();
		initTexture();

		while (true) {
			render();

			Display.update();
			Display.sync(10);

			if (Display.isCloseRequested()) {
				Display.destroy();
				break;
			}
		}
	}

	public static void main(String[] argv) {
		TextureExample textureExample = new TextureExample();
		textureExample.startTheExample();
	}
}

ouattwtym

I've been trying and failing to get an exact blit of a 2D sprite.

My code was originally based on.
http://lwjgl.org/wiki/index.php?title=Slick-Util_Library_-_Part_1_-_Loading_Images_for_LWJGL

That was not exact although it improved a lot when I added:
      GL11.glDisable(GL11.GL_DITHER);

After that it still wasn't exact. e.g. 248/256 shades of gray came through with a colour change - mostly slightly towards purple(??)

I thought it might be the "Slick" library image-loading that was at fault so I generated the texture in code and removed Slick - one less thing to be suspicious of. Then I reduced it to just a single color (rgb:f2/f2/f2) "texture" (the code you see above) but it drew as rgb:ef/f3/ef   ???

I could go simpler still and forget further about my original 2D sprite mission and forget about textures and regress to just painting an rgb:f2/f2/f2 quad. Surely that will work ...

float colorValue = ((float)0xf2)/((float)0xff);
GL11.glColor4f(colorValue,colorValue,colorValue,0.0f);
// paint the quad


But no  :( that comes out as rgb:ef/f3/ef too! I can see there must be something astonishingly basic that I'm not getting here about how colors work in OpenGL  :-\

Matthias

Hmm - strange. TWL does 1:1 pixel rendering of textures - you might want to check how your textures are displayed when you load them in the Theme editor.

It could also be related to some gamma settings etc.

jediTofu

Perhaps the very outside pixel colors are crossing over or something.

Try a 32x32 image with a 16x16 image inside (pad with black pixels and make black transparent), or just try a 2 pixel border around the image.
cool story, bro

ouattwtym

Thank you for your suggestions but, digging around, the problem seems more about colors than textures. I've solved it -- well, kind of ...  :-\

My "game" runs in a window on my 16bit true-color desktop. The color I want (namely rgb:f2/f2/f2) is a valid 12-bit color and is possible on my 16-bit true-color desktop. I can fire up Photoshop and draw a box of it onto a screenshot of my game right next to the place where my game failed to draw the box of it and only managed to draw rgb:ef/f3/ef instead. :(

However, raising my desktop to 32bit true-color fixes it and my game is suddenly able to do what, before, I could only do in Photoshop. It can suddenly draw a box of rgb:f2/f2/f2 for the first time just as my Java code asks it to.  :)

In summary:

  • In 32bit true-color rgb:f2/f2/f2 is available and when my code requests it it gets it.

  • In 16bit true-color rgb:f2/f2/f2 is available and when my code requests it it gets rgb:ef/f3/ef instead.
    More generally speaking, 248/256 of 12bit grayscales, though all available, do not draw correctly (though they all do in 32bit).

So that is how it works ... but why?  ???

Drawing a quad on a black background its more than you need to show this problem. Just clearing the screen will do ...

public class ColorBox {
	private static final int SCREEN_HEIGHT = 600;
	private static final int SCREEN_WIDTH = 800;

	private void initLwjglDisplay() {
		try {
			Display.setDisplayMode(new DisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT));
			Display.create();
			Display.setVSyncEnabled(true);
		}
		catch (LWJGLException e) {
			throw new RuntimeException(e);
		}
	}
	private void initOpenGL() {
		float colorValue = ((float)0xf2)/((float)0xff);
		GL11.glClearColor(colorValue, colorValue, colorValue, 0.0f);
	}

	private void render() {
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
	}

	private void startTheExample() {
		initLwjglDisplay();
		initOpenGL();
		while (true) {
			render();
			Display.update();
			Display.sync(10);
			if (Display.isCloseRequested()) {
				Display.destroy();
				break;
			}
		}
	}

	public static void main(String[] argv) {
		ColorBox cb= new ColorBox();
		cb.startTheExample();
	}
}



Matthias

Be lucky that it works in 16 bit color mode at all - most 3D hardware can't do 16 bit at all.

ouattwtym

Hang on. I think I've got confused between 12bit and 24bit color somewhere along the way. I started off with 12bit graphics from my Amiga. 0x0RGB. As PNM files those are #R0G0B0 but that doesn't use the whole color range so its common to scale it up to #RRGGBB so that white is #FFFFFF rather than  #F0F0F0. However, all of a sudden we're talking 24bit color. Of course some of these don't come out right on a 16bit display! Photoshop didn't help by dithering to make it seem as if #F2F2F2 is available in a 16bit display when it can't be.

OpenGL tells me the bits 16bits are allocated r=5/g=6/b=5 btw.
int r = GL11.glGetInteger(GL11.GL_RED_BITS);
		int g = GL11.glGetInteger(GL11.GL_GREEN_BITS);
		int b = GL11.glGetInteger(GL11.GL_BLUE_BITS);
		System.out.println("colour bits:");
		System.out.println(r);
		System.out.println(g);
		System.out.println(b);


I'll revisit this tomorrow with my head on straight :D
zzzzzz