OpenGL projective texture mapping

Started by Sharprender, August 24, 2014, 20:49:30

Previous topic - Next topic

Sharprender

I've tried to load a etxture from *.png file and bind it to a quad in 2D.

But if I change the quad to trapezoid, it looks like this


import java.io.IOException;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Color;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

public class TextureExample {

	/** The texture that will hold the image details */
	private Texture texture;

	private float delta = 0f;
	private boolean linear = false;

	/**
	 * Start the example
	 */
	public void start() {
		initGL(800, 600);
		init();

		while (true) {
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
			render();
			input();

			Display.update();
			Display.setTitle("delta= " + delta);
			Display.sync(100);

			if (Display.isCloseRequested()) {
				Display.destroy();
				System.exit(0);
			}
		}
	}

	/**
	 * Initialise the GL display
	 * 
	 * @param width
	 *            The width of the display
	 * @param height
	 *            The height of the display
	 */
	private void initGL(int width, int height) {
		try {
			Display.setDisplayMode(new DisplayMode(width, height));
			Display.create();
			Display.setVSyncEnabled(true);
		} catch (LWJGLException e) {
			e.printStackTrace();
			System.exit(0);
		}

		GL11.glEnable(GL11.GL_TEXTURE_2D);

		GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

		// enable alpha blending
		GL11.glEnable(GL11.GL_BLEND);
		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

		GL11.glViewport(0, 0, width, height);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);

		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		GL11.glOrtho(0, width, height, 0, 1, -1);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
	}

	/**
	 * Initialise resources
	 */
	public void init() {

		try {
			// load texture from PNG file
			texture = TextureLoader.getTexture("PNG", ResourceLoader
					.getResourceAsStream("models/squared.png"));

			System.out.println("Texture loaded: " + texture);
			System.out.println(">> Image width: " + texture.getImageWidth());
			System.out.println(">> Image height: " + texture.getImageHeight());
			System.out
					.println(">> Texture width: " + texture.getTextureWidth());
			System.out.println(">> Texture height: "
					+ texture.getTextureHeight());
			System.out.println(">> Texture ID: " + texture.getTextureID());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * draw a quad with the image on it
	 */
	public void render() {
		Color.white.bind();
		if (linear) {
			texture.setTextureFilter(GL11.GL_LINEAR);
		} else {
			texture.setTextureFilter(GL11.GL_NEAREST);
		}
		texture.bind();

		GL11.glBegin(GL11.GL_QUADS);
		GL11.glTexCoord2f(0, 0);
		GL11.glVertex2f(100, 100);
		GL11.glTexCoord2f(1, 0);
		// GL11.glVertex2f(100 + texture.getTextureWidth(), 100);
		GL11.glVertex2f(700, 100);
		GL11.glTexCoord2f(1, 1);
		// GL11.glVertex2f(100 + texture.getTextureWidth(), 100 +
		// texture.getTextureHeight());
		GL11.glVertex2f(500 + delta, 500);
		GL11.glTexCoord2f(0, 1);
		// GL11.glVertex2f(100, 100 + texture.getTextureHeight());
		GL11.glVertex2f(300 - delta, 500);
		GL11.glEnd();
	}

	private void input() {
		if (Keyboard.isKeyDown(Keyboard.KEY_UP)) {
			if (delta < 300) {
				delta += 1f;
			}
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_DOWN)) {
			if (delta > -100) {
				delta -= 1f;
			}
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
			linear = true;
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_RETURN)) {
			linear = false;
		}
	}

	/**
	 * Main Class
	 */
	public static void main(String[] argv) {
		TextureExample textureExample = new TextureExample();
		textureExample.start();
	}
}


How can I use an projective texture mapping?

Kai

Hi Sharprender,

for projective texturing to work, you need to give the GL some depth information of your scene.
Currently, your quad just "looks like" it is directed into the scene when in fact each pixel of it has the same depth since you are rendering a 2D quad and do not do any "projection" per se (where projection is 3D -> 2D).

What you need to do is first setup a perspective projection matrix (you are using an orthographic projection) and then specify your scene with 3-component vectors.

EDIT:

Let me give a real-world example of what it is that you are currently doing and what it is that you are expecting of the GL to accomplish:

Imagine having a trapezoidal piece of, let's say, aluminum. You have no rectangular shape where if you lay it on a table and look straight from atop it looks like a rectangle. No, you have a trapezoid. That is what you currently saying to the GL when using glVertex2f.

Now imagine further that you have a rectangular picture/photograph approximately of the size of that trapezoid. This is your texture. As you lay the photo on top of the trapezoid laying on the table the photo of course overlaps on the lower edges of your aluminum piece, for the picture being a rectangle whereas your piece of aluminum is a trapezoid.

What you expect of GL to do now, is to somehow "fold" the photo at the lower corners so that it would align with the trapezoid. Of course, the photo would get crinkles and not look right.
Those crinkles are what you are seeing on your image. The GL is first separating your aluminum trapeziod into two triangles (this is how the GL renders quads) and performs bilinear interpolation of your picture onto those triangles separately. This is why you see your texture being wrongly projected on the top/right side to the right and on the bottom/left side to the left. There you also see how the GL is creating the triangles from your trapezoid. The split line of both triangles go from the top/left corner to the bottom/right corner.

Now, let's work on solving this.
You first need to specify to the GL that you actually have a rectangular piece of aluminum. You can still use glVertex2f for that. And then tell the GL that you are "looking" not from atop but from a point slightly away from your piece of alumium. You can use GLU with gluPerspective and gluLookAt for that.

And why does this solve things?
Projective texturing is done looking at the W component of the vertices of your piece of alumium after those have been projected onto your screen using the perspective projection. This is the way the GL knows about relatively "how far" away from your camera/eye your objects actually are in your 3D scene. And this information allows the GL to do correct projective texturing using not only the X- and Y-component of the vertices for bilinear interpolation but also the W-component.

Since this topic has long been unanswered, I just decided to clear things up a bit now. :-)