Multitexturing with fixed pipeline

Started by lwjgl2015, October 15, 2015, 12:05:51

Previous topic - Next topic

lwjgl2015

I adapted the "Quad textured" example for the fixed pipeline but when I run it I don't see anything and it is not clear to me what I am doing wrong. This is a pretty slavish adaptation, so it should not take long to look at it: it is only the rendering part in the loop (the render method below) that differs; the rest is as in the original. Please don't give me grief over the use of the fixed pipeline: I am working on an existing code base and I don't have a mandate to perform radical surgery. Thanks!

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.util.glu.GLU;

import de.matthiasmann.twl.utils.PNGDecoder;
import de.matthiasmann.twl.utils.PNGDecoder.Format;

public class LwjglSquare
{
    private static final int WIDTH = 250;
    private static final int HEIGHT = 250;
    private static final int TEXTURE_UNIT = GL13.GL_TEXTURE0;

    private static int vboVId, vboIId, texId;

    public static void main( String[] argv ) throws Exception
    {
        Display.setDisplayMode( new DisplayMode( WIDTH, HEIGHT ) );
        Display.setTitle( LwglSquareScene.class.getSimpleName() );
        Display.create();
        
        GL11.glClearColor( 0.4f, 0.6f, 0.9f, 0f );
        GL11.glViewport( 0, 0, WIDTH, HEIGHT );
        
        GL11.glEnable( GL11.GL_TEXTURE_2D );

        setup();

        while ( !Display.isCloseRequested() )
        {
            render();
            Display.update();
        }

        destroy();
    }

    private static void setup() throws IOException
    {
        FloatBuffer vertexBuf = BufferUtils.createFloatBuffer( 16 );
        vertexBuf.put( new float[] { -0.5f, 0.5f, 0, 0, -0.5f, -0.5f, 0, 1, 0.5f, -0.5f, 1, 1, 0.5f, 0.5f, 1, 0 } );
        vertexBuf.flip();

        ByteBuffer indexBuf = BufferUtils.createByteBuffer( 6 );
        indexBuf.put( new byte[] { 0, 1, 2, 2, 3, 0 } );
        indexBuf.flip();

        vboVId = GL15.glGenBuffers();
        GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, vboVId );
        GL15.glBufferData( GL15.GL_ARRAY_BUFFER, vertexBuf, GL15.GL_STATIC_DRAW );
        GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, 0 );

        vboIId = GL15.glGenBuffers();
        GL15.glBindBuffer( GL15.GL_ELEMENT_ARRAY_BUFFER, vboIId );
        GL15.glBufferData( GL15.GL_ELEMENT_ARRAY_BUFFER, indexBuf, GL15.GL_STATIC_DRAW );
        GL15.glBindBuffer( GL15.GL_ELEMENT_ARRAY_BUFFER, 0 );

        texId = loadPngTexture( "square.png", TEXTURE_UNIT );
        
        exitOnGLError( "setup" );
    }
    
    private static void render()
    {
        GL11.glMatrixMode( GL11.GL_PROJECTION );
        GL11.glLoadIdentity();
        GL11.glOrtho( 0, WIDTH, 0, HEIGHT, 1, -1 );
        GL11.glMatrixMode( GL11.GL_MODELVIEW );

        GL11.glClear( GL11.GL_COLOR_BUFFER_BIT );
        
        GL13.glClientActiveTexture( TEXTURE_UNIT );
        GL11.glBindTexture( GL11.GL_TEXTURE_2D, texId );

        GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, vboVId );
        GL11.glVertexPointer  ( 2, GL11.GL_FLOAT, 16, 0 );
        GL11.glTexCoordPointer( 2, GL11.GL_FLOAT, 16, 8 );

        GL15.glBindBuffer( GL15.GL_ELEMENT_ARRAY_BUFFER, vboIId );

        GL11.glEnableClientState( GL11.GL_VERTEX_ARRAY );
        GL11.glEnableClientState( GL11.GL_TEXTURE_COORD_ARRAY );

        GL11.glDrawElements( GL11.GL_TRIANGLES, 6, GL11.GL_UNSIGNED_BYTE, 0 );

        GL11.glDisableClientState( GL11.GL_VERTEX_ARRAY );
        GL11.glDisableClientState( GL11.GL_TEXTURE_COORD_ARRAY );

        GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, 0 );
        GL15.glBindBuffer( GL15.GL_ELEMENT_ARRAY_BUFFER, 0 );
        GL11.glBindTexture( GL11.GL_TEXTURE_2D, 0 );
        
        exitOnGLError( "render" );
    }

    private static void destroy()
    {
        GL11.glDeleteTextures( texId );
        GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, 0 );
        GL15.glDeleteBuffers( vboVId );
        GL15.glBindBuffer( GL15.GL_ELEMENT_ARRAY_BUFFER, 0 );
        GL15.glDeleteBuffers( vboIId );
        exitOnGLError( "destroy" );
        Display.destroy();
    }
    
    @SuppressWarnings( "resource" )
    private static int loadPngTexture( String resName, int textureUnit ) throws IOException
    {
        URL url = LwglSquareScene.class.getResource( resName );
        InputStream in = url.openStream();
        PNGDecoder decoder = new PNGDecoder( in );
        int width  = decoder.getWidth();
        int height = decoder.getHeight();
        ByteBuffer buf = ByteBuffer.allocateDirect( 4 * width * height );
        decoder.decode( buf, 4 * width, Format.RGBA );
        in.close();
        buf.flip();

        int texId = GL11.glGenTextures();
        GL13.glClientActiveTexture( textureUnit );
        GL11.glBindTexture( GL11.GL_TEXTURE_2D, texId );
        GL11.glPixelStorei( GL11.GL_UNPACK_ALIGNMENT, 1 );
        GL11.glTexImage2D( GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buf );
        GL30.glGenerateMipmap( GL11.GL_TEXTURE_2D );
        GL11.glTexParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE );
        GL11.glTexParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE );
        GL11.glTexParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST );
        GL11.glTexParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR );
        exitOnGLError( "loadPngTexture" );
        return texId;
    }

    private static void exitOnGLError( String errorMessage )
    {
        int errorValue = GL11.glGetError();
        if ( errorValue != GL11.GL_NO_ERROR )
        {
            String errorString = GLU.gluErrorString( errorValue );
            System.err.println( "ERROR - " + errorMessage + ": " + errorString );
            if ( Display.isCreated() )
            {
                Display.destroy();
            }
            System.exit( -1 );
        }
    }
}

lwjgl2015

PS: I put multitexturing in the title because later I will need to use a different texture unit but for now I am sticking to GL_TEXTURE0.

Kai

The problem is with your projection matrix.
You are using
GL11.glOrtho( 0, WIDTH, 0, HEIGHT, 1, -1 );
so your viewport ranges from 0..WIDTH and 0..HEIGHT. But your quad is very small then, smaller than a pixel, because its vertices only range from -0.5..+0.5, and they are also situated at the bottom left corner of the viewport.

So just remove the call to glOrtho, and you will see the quad at the viewport center.

lwjgl2015

Thanks! Clearly I need to bone up on transformations. I thought that with the viewport set as it was this ortho call was correct.

However, as hinted above, that was not my real issue. My problem is that if you replace GL_TEXTURE0 with any other unit you get a solid red quad, so the texture is there but somehow the texture coordinates got lost and are all (0.0). After re-reading the OpenGL Programming Guide 6th Edition, pp. 444-446, especially Example 9-10, I removed the call to glActiveTexture() from loadPNGTexture() and in render() I did
       GL13.glActiveTexture( TEXTURE_UNIT );
       GL11.glEnable( GL11.GL_TEXTURE_2D );
       GL11.glBindTexture( GL11.GL_TEXTURE_2D, texId );

Then I get a different picture (red fading to black instead of solid red) but still not the right one. Do you know the correct incantations?

Kai

Okay, doing multitexturing with the fixed-function pipeline also requires using glClientActiveTexture().
This sets the active client texture unit which will affect calls such as  glEnableClientState(GL_TEXTURE_COORD_ARRAY) and also glTexCoordPointer().
Also note that glEnable(GL_TEXTURE_2D) only enables the currently active texture unit selected via glActiveTexture().

So, for each texture unit you want to use, do this:
glActiveTexture(texUnit);
glEnable(GL_TEXTURE_2D); // enable texturing for this texture unit
glBindTexture(GL_TEXTURE_2D, ...);
glClientActiveTexture(texUnit);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(...);


You may also want to use glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, ...) to specify how the individual textures will be combined in the final output (replace, modulate, ...).
The call glTexEnvi() affects the texture unit selected via glActiveTexture().

lwjgl2015

So this works indeed. You are awesome! I spent quite some time trying to figure this out.