OpenGL Shading Language Tutorials

Tutorial 8 - Multitexturing


This tutorial shows how to use multitexturing in OGLSL programs. It is a very important topic, because a lot of shaders in future tutorials use multitexturing, for example image processing, texture functions and bump mapping.

A little introduction to OpenGL Multitexturing

This tutorial uses 2 RGB textures "texture1" and "texture2".

First we need to write a little C++ programm to load textures etc.
The draw loop for the two images above looks about like this:

void DrawFrame(void)
{
 glTranslatef(-2.0,-1.0,0.0);
 glBindTexture(GL_TEXTURE_2D, texture1); 
 drawBox(2.0);
 glTranslatef(2.0,0.0,0.0);
 glBindTexture(GL_TEXTURE_2D, texture2);
 drawBox(2.0);
}

Next step is using multitexturing:

setup for multitexturing.

glActiveTextureARB(GL_TEXTURE0_ARB);
glBindTexture(GL_TEXTURE_2D, texture1);
glEnable(GL_TEXTURE_2D);
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE);
 
glActiveTextureARB(GL_TEXTURE1_ARB);
glBindTexture(GL_TEXTURE_2D, texture2); 
glEnable(GL_TEXTURE_2D);
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_INCR);

'sending' both textures to drawBox:
(This example should result same if you disable shaders.)

void DrawFrame(void)
{
 glTranslatef(-1.0,-1.0,0.0);
 
 myShader->begin();

  myShader->sendUniform1i("myTexture1", 0); // Texture Unit 0
myShader->sendUniform1i("myTexture2", 1); // Texture Unit 1 drawBox(2.0); myShader->end(); }

Inside DrawBox you have to define two sets of texture coordinates, one for each Texture unit. It looks like this:

void drawBox(float size)
{
 // Texture Coordinate (0,0) is top left.
 glBegin(GL_QUADS);
  glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0, 1.0);
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0, 1.0);  
  glVertex3f(0.0, 0.0, 0.0);
  
  glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0, 0.0);
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0, 0.0);
  glVertex3f(0.0, size*1.0, 0.0);
  
  glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0, 0.0);
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0, 0.0); 
  glVertex3f(size*1.0, size*1.0, 0.0);
  
  glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0, 1.0);
  glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0, 1.0);
  glVertex3f(size*1.0, 0.0, 0.0);
 glEnd();
           
}

To figure out how many texture units you can use, you can check GL_MAX_TEXTURE_UNITS_ARB with glGetIntegerv.

You probably noted, multitexturing functions end with ARB - multitexturing is an OpenGL extension and function pointers have to be allocated first.

OGLSL Example

In the following shader program I simply add the color values of the two textures and divide it by 2.

Vertex Program Source

void main(void)
{
 gl_TexCoord[0] = gl_MultiTexCoord0;
 gl_TexCoord[1] = gl_MultiTexCoord1;
 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 
}

Fragment Program Source

uniform sampler2D myTexture1;
uniform sampler2D myTexture2;
void main (void)
{
   vec4 texval1 = texture2D(myTexture, vec2(gl_TexCoord[0]));
   vec4 texval2 = texture2D(myTexture2, vec2(gl_TexCoord[1]));
   
   gl_FragColor = 0.5*(texval1 + texval2);
}


Result: adding both Textures

 

Exercises:

1. Change fragment shader so it mirrors 2nd texture (doesn't matter which direction).

2. Add an uniform variable to move 2nd texture up and down. A little (time based) animation would be nice.

3. How would you access neighbour texture coordinates in this example?
(hint: modify C++ code and fragment shaders for this.)


Author: Martin Christen, christen@clockworkcoders.com
(source code is included in Tutorial Version 0.2 and higher)

 

 

© 2003 by Martin Christen. All Rights Reserved.
christen@clockworkcoders.com