Multitextures

I’m having trouble trying to use the multitexture extension.

I know my particular implementation supports multitextures with 2 textures.

What I don’t understand is what is the actual point of using multiple textures?

Basically, can I overlay more than one texture on top of each other on the same quad, so that the first texture is applied to each pixel, then the second texture is applied to each pixel of the first texture?

I just can’t seem to get a sensible result, I either get one texture showing up, or I get a blank quad. I’m trying to follow along with someone’s C code, and it seems I have done everything the way they did, from init to rendering, and it only seems to work when I apply just one texture.

Either texture works by itself, so the actual texture data is ok.

The first texture is a gradient is from blue at the bottom to red at the top, using RGB. The second texture is a checkerboard that contains two gray areas in the upper right and bottom left corners, the other two corners are black, using RGBA, with an alpha value of 128.

Any advice?

Here is the code:

Initialisation:

    // ByteBuffer buff = 64 x 64 gradient texture
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(64);
    IntBuffer intBuffer = byteBuffer.asIntBuffer();
    textureIDs = new int[2];
    GL.glGenTextures(intBuffer);
    textureIDs[0] = intBuffer.get(0);
    GL.glBindTexture(GL11.GL_TEXTURE_2D, textureIDs[0]);
    GL.glTexImage2D(
            GL11.GL_TEXTURE_2D,
            0, 3,
            64, 64,
            0,
            GL11.GL_RGB,
            GL11.GL_UNSIGNED_BYTE,
            buff
    );
    GL.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
    GL.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
    GL.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
    GL.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
    GL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);

    // ByteBuffer buff = 64 x 64 checkerboard texture
    GL.glGenTextures(intBuffer);
    textureIDs[1] = intBuffer.get(0);
    GL.glBindTexture(GL11.GL_TEXTURE_2D, textureIDs[1]);
    GL.glTexImage2D(
            GL11.GL_TEXTURE_2D,
            0, 4,
            64, 64,
            0,
            GL11.GL_RGBA,
            GL11.GL_UNSIGNED_BYTE,
            buff
    );
    GL.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
    GL.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
    GL.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
    GL.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
    GL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);

Rendering:

    GL.glActiveTextureARB(GL13.GL_TEXTURE0);
    GL.glEnable(GL11.GL_TEXTURE_2D);
    GL.glMatrixMode(GL11.GL_TEXTURE);
    GL.glLoadIdentity();
    GL.glMatrixMode(GL11.GL_MODELVIEW);

    GL.glActiveTextureARB(GL13.GL_TEXTURE1);
    GL.glEnable(GL11.GL_TEXTURE_2D);
    GL.glMatrixMode(GL11.GL_TEXTURE);
    GL.glLoadIdentity();
    GL.glMatrixMode(GL11.GL_MODELVIEW);
    GL.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
    GL.glEnable(GL11.GL_BLEND);
    
    GL.glBegin(GL11.GL_QUADS);
    GL.glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
    GL.glMultiTexCoord2fARB(GL13.GL_TEXTURE0, 0.0f, 0.0f);
    GL.glMultiTexCoord2fARB(GL13.GL_TEXTURE1, 0.0f, 0.0f);
    GL.glVertex2i(200, 200);
    GL.glMultiTexCoord2fARB(GL13.GL_TEXTURE0, 0.0f, 1.0f);
    GL.glMultiTexCoord2fARB(GL13.GL_TEXTURE1, 0.0f, 1.0f);
    GL.glVertex2i(200, 263);
    GL.glMultiTexCoord2fARB(GL13.GL_TEXTURE0, 1.0f, 1.0f);
    GL.glMultiTexCoord2fARB(GL13.GL_TEXTURE1, 1.0f, 1.0f);
    GL.glVertex2i(263, 263);
    GL.glMultiTexCoord2fARB(GL13.GL_TEXTURE0, 1.0f, 0.0f);
    GL.glMultiTexCoord2fARB(GL13.GL_TEXTURE1, 1.0f, 0.0f);
    GL.glVertex2i(263, 200);
    GL.glEnd();
    
    // Use reverse order here
    GL.glActiveTextureARB(GL13.GL_TEXTURE1);
    GL.glDisable(GL11.GL_TEXTURE_2D);
    GL.glActiveTextureARB(GL13.GL_TEXTURE0);
    GL.glDisable(GL11.GL_TEXTURE_2D);

Don’t remember much of the multitexturing from memory, but I’m pretty sure you need to bind each texture to its unit like this:


GL.glActiveTextureARB(GL13.GL_TEXTURE0); 
GL.glEnable(GL11.GL_TEXTURE_2D); 
GL.glBindTexture(GL11.GL_TEXTURE_2D, textureIDs[0]); // new line
GL.glMatrixMode(GL11.GL_TEXTURE); 
GL.glLoadIdentity(); 
GL.glMatrixMode(GL11.GL_MODELVIEW); 
 
GL.glActiveTextureARB(GL13.GL_TEXTURE1); 
GL.glEnable(GL11.GL_TEXTURE_2D); 
GL.glBindTexture(GL11.GL_TEXTURE_2D, textureIDs[1]); // new line
GL.glMatrixMode(GL11.GL_TEXTURE); 
GL.glLoadIdentity(); 
GL.glMatrixMode(GL11.GL_MODELVIEW); 
GL.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 
GL.glEnable(GL11.GL_BLEND);

Incidentally, the blend function is applied after multitexturing is done (to blend the result onto the framebuffer). Your code structure suggests you’re trying to use the blend func to combine the two - you want to set the texture environment for that.

I tried using GL_BLEND for the texture environment of the second texture, since that does make more sense. I still get a blank quad though.

You do not want to have blending enabled. Don’t exactly know what is wrong with your code. But here is a complete working multitexture example to look at. It uses LWJGL 0.95 in case it don’t compile:


package sh.tests;

import java.nio.*;
import org.lwjgl.opengl.*;

public class MultiTexture {
      public static int createTextureObject(ByteBuffer scratch, int width, int height) {
            scratch.rewind();
            IntBuffer intBuf = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
            GL11.glGenTextures(intBuf);
            GL11.glBindTexture(GL11.GL_TEXTURE_2D, intBuf.get(0));
            GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, scratch);
            GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); // gets white quad without this
            return intBuf.get(0);
      }
      
      public static void main(String args[]) throws Exception {
            try {
                  Display.setDisplayMode(new DisplayMode(640, 480));
                  Display.create();
                  GL11.glClearColor(0.0f, 0.0f, 1.0f, 0.0f);

                  // load some texture
                  int width = 256;
                  int height = 256;
                  ByteBuffer scratch = ByteBuffer.allocateDirect(4 * width * height);
                  for (int y=0; y<height; y++) {
                        for (int x=0; x<width; x++) {
                              scratch.put((byte)(x|y)).put((byte)(x&y)).put((byte)(x^y)).put((byte)0xff);
                        }
                  }
                  int lightmapId = createTextureObject(scratch, 256, 256);
                  
                  scratch.rewind();
                  for (int y=0; y<height; y++) {
                        for (int x=0; x<width; x++) {
                              int length = (int) Math.min(255, Math.sqrt((x-width/2)*(x-width/2)+(y-height/2)*(y-height/2)));
                              scratch.put((byte)length).put((byte)length).put((byte)length).put((byte)0xff);
                        }
                  }
                  int textureId = createTextureObject(scratch, 256, 256);
                  
                  while (!Display.isCloseRequested()) {
                        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
                        
                        // we want it to be white
                        GL11.glColor4f(1, 1, 1, 1);
                        
                        // first texture unit
                        GL13.glActiveTexture(GL13.GL_TEXTURE0);
                        GL11.glEnable(GL11.GL_TEXTURE_2D);
                        GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId);
                        GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
                  
                        // second texture unit
                        GL13.glActiveTexture(GL13.GL_TEXTURE1);
                        GL11.glEnable(GL11.GL_TEXTURE_2D);
                        GL11.glBindTexture(GL11.GL_TEXTURE_2D, lightmapId);
                        GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
                                                      
                        int size = 256;
                        GL11.glBegin(GL11.GL_QUADS); 
                        //GL11.glColor4f(0.0f, 0.0f, 0.0f, 1.0f); NO black 
                        GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.0f, 0.0f); 
                        GL13.glMultiTexCoord2f(GL13.GL_TEXTURE1, 0.0f, 0.0f); 
                        GL11.glVertex2i(200, 200); 
                        GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.0f, 1.0f); 
                        GL13.glMultiTexCoord2f(GL13.GL_TEXTURE1, 0.0f, 1.0f); 
                        GL11.glVertex2i(200, 200+size); 
                        GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0, 1.0f, 1.0f); 
                        GL13.glMultiTexCoord2f(GL13.GL_TEXTURE1, 1.0f, 1.0f); 
                        GL11.glVertex2i(200+size, 200+size); 
                        GL13.glMultiTexCoord2f(GL13.GL_TEXTURE0, 1.0f, 0.0f); 
                        GL13.glMultiTexCoord2f(GL13.GL_TEXTURE1, 1.0f, 0.0f); 
                        GL11.glVertex2i(200+size, 200); 
                        GL11.glEnd();            
                        
                        Display.update();
                        Thread.yield();
                  }
            } catch (Exception e) {
                  e.printStackTrace();
            } finally {
                  Display.destroy();      
            }
      }
}

Figured it out.

My problem was twofold:

a) You have to bind the texture both during initialisation, when you set the parameters, AND when rendering. This makes perfect sense - during initialisation, the binding specifies which texture object the parameters are stored in. During rendering, the binding specifies which texture you want to use.

b) I needed the following lines for the second texture:

    GL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
    GL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL11.GL_MODULATE);
    GL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_MODULATE);

The combine operation is neccessary on the second texture, or else it won’t combine with the first one.

That got it to work.