Multitexturing and TexEnv

Hi there!

I have read OpenGL specs and looked at a lot of examples bu I can’t yet understand! It’s no where well documented.

What does GL_SOURCE1_RGB_ARB mean? Does it mean that I am using the Texture Unit 1? Or does it mean I am using the last binded texture?

For example take a look at this code from an example. I know what it does (it creates a nice blend using 3 tiles with a coverage texture using .RGB and .A as the two blending factors and also adds a lightmap). However, I cannot understand yet which part of the code does what. Please correct my interpretations in /* */ . The comments in // are not mine but the authors:

// Texture 3: previous * L
  glActiveTextureARB(GL_TEXTURE3_ARB);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, L);

  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

/* Here we set active texture unit 3 (lightmap I guess) and set it to modulate, that is texture * vcertex colors. */
  
// Texture 2: lerp(C0.alpha, previous, T3)
  glActiveTextureARB(GL_TEXTURE2_ARB);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, T3);

/* Texture unit 2 is active*/
  glMatrixMode(GL_TEXTURE);
  glScalef(0.2, 0.2, 0);
  glMatrixMode(GL_MODELVIEW);

  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
  glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB);

/*set up interpolate function*/

  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
/* How does the GPU know which one is GL_SOURCE0 ???*/
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);

  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
/* How does the GPU know which one is GL_SOURCE1 ???*/

  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PRIMARY_COLOR_ARB);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA);
/* How does the GPU know which one is GL_SOURCE2 ???*/
  // Texture 1: lerp(C0.rgb, T1, T2)
  glActiveTextureARB(GL_TEXTURE1_ARB);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, T1);

  glMatrixMode(GL_TEXTURE);
  glScalef(0.2, 0.2, 0);
  glMatrixMode(GL_MODELVIEW);

  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
  glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB);

  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);

  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);

  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PRIMARY_COLOR_ARB);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_COLOR);

  // Texture 0: T1
  glActiveTextureARB(GL_TEXTURE0_ARB);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, T2);

  glMatrixMode(GL_TEXTURE);
  glScalef(0.2, 0.2, 0);
  glMatrixMode(GL_MODELVIEW);

  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

/* Why does he put replace!? won't it replace all the previously calculated texture?*/

Guys… I am kind of lost with this. I know what the functions do (interpolate between source 0 and source 1 using source 2 as interpolation) but I don’t yet understand how the GPU is supposed to know which one is Source 0,1,2. Help on understanding how glTexEnv works and how to set it up would be deeply apprecited!

Thanks so much in advance!
Cheers
Rod

OK, let’s take each step one at a time.

  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

A TexEnv (each bound texture has an entirely separate one) performs a function. This function is known as the GL_TEXTURE_ENV_MODE. In this case, the mode is set to GL_MODULATE.

What this mode does is it takes the previous color (which, since this is the first TexEnv, there is no previous color, so it is simply the primary color) and multiplies it with the texture color to produce the output color.

Simple enough.

  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
  glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB);

Much more complex.

You see, GL_COMBINE_ARB is a strange function. It completely re-adjusts the way a TexEnv stage works.

The first thing it does is split the function into two: one for the RGB portion of the color and one for the Alpha portion. These can now have separate, independent functions, which is what the second function does. It sets the RGB function to GL_INTERPOLATE.

The GL_INTERPOLATE combine function performs a linear combination between Argument 0 and Argument 1, using Argument 2 as the interpolation value.

Your question, basically, is what are Arguments 0, 1, and 2.

That’s what these functions do:

  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);

  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);

  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PRIMARY_COLOR_ARB);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA);

GL_SOURCEn_RGB_ARB and GL_OPERANDn_RGB_ARB specify what the argument named n is and where it comes from. The GL_SOURCEn_RGB_ARB command tells the system what color value it should draw upon to generate argument n. The GL_OPERANDn_RGB_ARB command tells the system what part of the source value should be used to generate argument n.

In the case of the first pair of functions, it says:

Argument0 = PreviousTexEnvColor.RGB

It may not make sense why this needs to be said, but it could have been:

Argument0 = 1 - PreviousTexEnvColor.RGB

The possible sources are:

GL_PRIMARY_COLOR (the primary color set and interpolated for the privative)
GL_TEXTURE (the color obtained from sampling the texture for this TexEnv stage)
GL_TEXTUREn (the color obtained from sampling the texture for the nth TexEnv stage)
GL_CONSTANT (a constant color that you can set)
GL_PREVIOUS (the color output from the last TexEnv stage)

The possible oprands (or transformations, as I prefer to think of them) are pretty much what you’d expect:

GL_SRC_COLOR
GL_ONE_MINUS_SRC_COLOR
GL_SRC_ALPHA
GL_ONE_MINUS_SRC_ALPHA

If you’re setting the arguments for the alpha portion of a GL_COMBINE_ARB TexEnv stage, then you can only use GL_SRC_ALPHA and GL_ONE_MINUS_SRC_ALPHA.

The OpenGL “SDK” (which it very much is not) has extensive (though somewhat dry) documentation on the subject.

Korval thank you so much for your answer!!! :slight_smile:

A few questions to see if I am getting this right:

A TexEnv (each bound texture has an entirely separate one) performs a function
You mean that every Texture Object (texture id) has a different TexEnv?
Or is that every texture channel (unit) has a different TexEnv? For example if I use glActiveTextureARB(GL_TEXTURE2_ARB) and then use glTexEnv(), then all the textures in Channel 2 will change to that TexEnv?

GL_PRIMARY_COLOR (the primary color set and interpolated for the privative)
You mean this uses the Color4f() commands for each vertice to as the interpolator? How can I get the last output of the converted fragment? (Does this command do that? )

GL_TEXTUREn (the color obtained from sampling the texture for the nth TexEnv stage)

So if bind texture “Grass” declaring first glActiveTextureARB(GL_TEXTURE3_ARB);
and then I have the texture “Sand” in
glActiveTextureARB(GL_TEXTURE1_ARB); and I want to use texture “Grass” in my TexEnv: then I would use :

glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE3); --> Correct? (I want to get the texture in Channel glActiveTextureARB(GL_TEXTURE3_ARB) )
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);

…and finally is GL_PREVIOUS_ARB the last defined Texture Environment or actually the actual texture environment number -1? Example: if my current TexEnv is in GL_TEXTURE3_ARB then is the GL_PREVIOUS equal to the TexEnv GL_TEXTURE2_ARB or the last defined (in code order) TexEnv (which could be for example GL_TEXTURE5_ARB)?
)

Thank you so much for everything!

I find this topic is really cucumbersome, also cause old specs are always mixed with the new ones, (although the specs you sent me are the most complete I’ve yet found :wink: ).

Thanks so much for your time and for your info that is really helpful :slight_smile: ,
Cheers
Rod

The texture environment is per texture unit, not per texture image. All environment stages are executed in order, that is, first the one in GL_TEXTURE0, then GL_TEXTURE1, and so on…

You can always access the output of the previous stage using GL_PREVIOUS. That is, when you use GL_PREVIOUS as source in GL_TEXTURE1, you’ll always get the output of the combiner of GL_TEXTURE0. The order in which you setup the stages does not matter.

GL_PRIMARY_COLOR is always the interpolated color from glColor (or from the color array, if you are using vertex arrays).

By the way, do you have some particular reason why you are using combiners? Shaders are much easier and more intuitive to use, once you understand the basics. You just write the calculation as shader code, and don’t have to think about how to set this up in seperate stages…

By the way, do you have some particular reason why you are using combiners?
Backward compatibility I guess. I made my game for SM3.0 hardware but I have testers with GeForce 4Ti, 4MX and even RIVA TNT 2.
Many people still have Radeon 9k / GeForce FX.
Radeon is OK, but GeForce FX is slow with GLSL - it’s better to use combiners whenever possible.
Unless you want your product to require GeForce 6 at least…

Overmind thanks so much for your answer! :slight_smile:
You made everything much clearer now, and I really understand the code. Thanks!

Originally posted by Overmind:
By the way, do you have some particular reason why you are using combiners?
As k_szczech guessed right :wink: , I want it first because of backward compatibility and second because I am yet stuck on the 5500 FX series (I am soon buying a new card :wink: ) and I can’t enjoy GLSL yet (GLSL works, but very slowly).

My main reason for understanding combiners really well (I assume passing this knowledge to GLSL will not be hard) is for creating the following effect: I already managed as to how to create a terrain with 3 combined textures and lightmap; however, now I want the terrain to respond to lighting.

In other words, with my current implementation (single 2048x2048 base texture + detail texture + lightmap) the terrain gets modulated by GL_LIGHTING and I can create the following “sunset” transition effect when I set Ambient to 0.2 and all other light parameters to zero for all objects except the sky and then increment them gradually. Some screenies of the sunsets with the actual implementation, so you get the idea of how the terrain looks with low ambient and the effect I want to achieve with the new terrain implementation:

However, with my new implementation, I am using the vertex colors as the interpolators and Lighting off, so now I can’t make a transition between a lighted and a dark terrain. I cannot turn on lighting because the texture combiners don’t work and I get a single textured terrain “Lighting effects are calculated before the textures are blended, so lighting wouldn’t do us any good and we disable it.” (says the author of the Advanced OpenGL Game Programming’s Tutorial)

Taking into account what I learned from texture combiners I am considering these options:

  1. Replace vertex color interpolators for an additional texture unit which has RGB as interpolator 1 and A as interpolator 2.
    Given that Colors will have correct values I could modulate the whole material of the terrain and use GL_LIGHTING enabled. Disadvantages are that I’ll have to bind another texture unit into GPU and that given that colors are given by vertex, I don’t know if this will work as expected.

  2. Use an additional Texture Environment with
    GL_CONSTANT and a constant color, that varies from white to black according to my own “world light”. (Can this be done? I’ve looked at the specs and it says that the operand for this function can be GL_SRC_COLOR, but I am not quite sure how to actually determine this color)

  3. Any ideas?

This is my actual code for the new implentation:

 	
			// The first texture is placed, ignoring the vertex color.
			glActiveTextureARB(GL_TEXTURE0_ARB);
			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);


			// The second texture and the previous texture are combined based
			// on the vertex color.			
			glActiveTextureARB(GL_TEXTURE1_ARB);
			glEnable(GL_TEXTURE_2D);

			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB);

			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);

			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);

			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PRIMARY_COLOR_ARB);
			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_COLOR);

			glDisable(GL_TEXTURE_2D);


			// The third texture and the previous result are combined based
			// on the vertex alpha value.
			glActiveTextureARB(GL_TEXTURE2_ARB);
			glEnable(GL_TEXTURE_2D);
												  
			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB);

			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);

			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);

			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PRIMARY_COLOR_ARB);
			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA);

			glDisable(GL_TEXTURE_2D);


			// The fourth texture, the lightmap, is modulated with the
			// previous texture.
			glActiveTextureARB(GL_TEXTURE3_ARB);
			glEnable(GL_TEXTURE_2D);

			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

			glDisable(GL_TEXTURE_2D); 

Thanks so much for everything!
Cheers,
Rod

P.S: k_szczech I’ve already managed to make a basic a implementation of GLSL in my engine and now working on shadow mapping, so I’ll update you in the next few weeks in the other post and tell how my shadowmapping is going! :slight_smile: