Lesson 13: Font and Texture Options

Texture Options

Texture functions

Remember the good old blending functions? Well, it turns out that there are also functions that you can apply to textures in order to change the way they appear on screen. Pretty cool, if you ask me :)

You can set the texture function, or texture environment, by using glTexEnv().

void glTexEnv{i|fv}(int target, int pname, type param)
Sets the OpenGL texture environment mode. Currently, target must be GL_TEXTURE_ENV. pname can be either GL_TEXTURE_ENV_MODE or GL_TEXTURE_ENV_COLOR.

If pname is GL_TEXTURE_ENV_MODE, param is an int, either GL_DECAL, GL_REPLACE, GL_MODULATE, or GL_BLEND. If pname is GL_TEXTURE_ENV_COLOR, param is an array of four float values, representing red, green, blue, and alpha components.

Okay, so you have the function definition. Now what happens when you play around with it? Let's start with GL_DECAL.

Dealing strictly with RGB or RGBA-mode textures, using GL_DECAL is a lot like slapping a label(the texture) on a soup can(the model). In fact, it is so much like slapping a label on a soup can, you'll find that the analogy happens to be used quite a lot :)

Now, this analogy is used because if you happen to have a texture that has an alpha value of one throughout(i.e., it is totally opaque), then only the colors from the texture will be shown, and nothing else. If the texture had some translucent parts within it, then whatever color you were rendering the underlying model in would "show through." For example, say you were rendering a grey-coloured cube with a yellow texture over it. Now, if your texture were completely opaque, you would see a completely yellow cube with GL_DECAL, and none of the "underlying" grey. However, if your texture had some translucency, some of the grey from the cube would show through. It is very important to note that if the underlying model is rendered translucent(with an alpha of less than 1.0) then the texture will become translucent too.

GL_REPLACE is even simpler(if you found the above simple :)). It is much like GL_DECAL except that it does not let any underlying colours show through it. However, there is one other important difference - if the texture only has R, G, and B components, and no A, then the alpha value of the texture will be taken from the underlying model. But, if the texture has R, G, B, and A components, then the alpha will be taken from the texture only - if your texture map has alpha values "built-in" to it, then it does not matter how you render the underlying model, because nothing will be taken from it. So you have to watch with this texture mode.

The default, and indeed the most useful texture mode, is GL_MODULATE. What it does is blend each part of the texture with the current GL colour, much like the standard alpha blending modes. I think this is better explained with an example. Say you were rendering a completely textured scene of an outdoors landcape in the daytime. Then you decide to do so again in the nighttime, but how do you get every surface to look darker? The easiest way of doing it is to set the texture environment mode to GL_MODULATE, and then somewhere in your rendering code(at the beginning, hopefully), you add this line:
glColor4f(0.2, 0.2, 0.4, 1.0); // A nice dark nighttime blue
This would blend all subsequent textures with this dark blue colour, making everything look nice and dark.

An example of the GL_MODULATE texture function.
On the left, my 8th tutorial; on the right, the same tutorial rendered with GL_MODULATE and the "nighttime blue" colour.

GL_MODULATE is a very useful mode indeed. Imagine the effects you can make with it... if any of you have ever played Final Fantasy VII, recall the part how if you destroy a normal field monster it would gradually fade from being solid to a sort of translucent red then disappear. You could achieve such an effect by blending your textured monster with a more and more translucent red colour.

Remember that when you set the color, all textured polygons are blended with that colour, so if you want your beautiful textures to look normal again, you have to set the GL colour to pure white(1.0, 1.0, 1.0, 1.0).

GL_BLEND is the last of the texture environment modes, and it is kind of complicated. Before using it, you need to set the GL_TEX_ENV_COLOR via glTexEnv(), explained above. Once you have done that, you can use the GL_BLEND mode.

The GL_BLEND mode uses the texture colour as a sort of set of alpha levels, but instead of having one alpha level for the whole surface, you have one alpha level for each color component(R, G, and B). I think I will be able to better explain this with an example. Say that the colour used to render the underlying model was a sort of dull green(0.5, 1.0, 0.5). Also, let us suppose that the colour passed through glTexEnv was a dull, dark red(0.6, 0.3, 0.3). And, say that the texture itself was just one colour, a bright, dull blue(0.8, 0.8, 1.0). GL_BLEND uses the following formula:

final component = env component(tex component) + model component(1 - tex component)

So, the final colour for the examples above would be (0.58, 0.44, 0.3). GL_BLEND works with alpha values in the same way that GL_MODULATE does. If you don't know what's going on, try and work out the above calculations.

At any rate, I am not really quite sure why you would want to use the GL_BLEND mode, so if you don't understand, well, don't feel too left out. Now, I don't want to sound lazy, but if you want to see some concrete examples of the above functions, well, write your own :) It would be good practice, and it is fun to experiment with these things on your own.

Texture filtering modes

Ahh, texture filtering. A single function call can be the difference between ugly, blocky textures, and ugly, blurry textures. That's if you have an ugly texture to start off with. If you have a nice texture, then it's the difference between nice, blocky textures and nice, smooth textures. So it's good.

There are many options for texture filtering. You can set if you want textures to be smoother when magnified, or minified; you can set whether or not you want textures to repeat themselves so that you can "tile" a texture several times across one polygon; and more. That's right, more!

And the best part about texture filtering options is that you can apply different options to each texture - when you set a texture filtering option, it applies only to the texture that is currently bound(although you only have to set filtering options once for each texture). Anyway, here's the function:

void glTexParameteri(int target, int pname, type param)
Sets texture parameter for the currently bound texture. There are other forms of glTexParameter(), but those will be discussed later on. target is either GL_TEXTURE_1D or GL_TEXTURE_2D, depending on what kind of texture you are using, one- or two-dimensional. The following table lists the options for pname:
GL_TEXTURE_WRAP_SDetermines what should be done if the horizontal texture coordinates ever go beyond the [0,1] range. GL_CLAMP clamps the texture, and GL_REPEAT tiles it.
GL_TEXTURE_WRAP_TDetermines what should be done if the vertical texture coordinates ever go beyond the [0,1] range. GL_CLAMP clamps the texture, and GL_REPEAT tiles it.
GL_TEXTURE_MAG_FILTERDetermines what kind of stretching should be done if a texture-mapped polygon is rendered larger than the texture mapped to it is(pixel-wise). GL_NEAREST uses simple "blocky" stretching for textures, whereas GL_LINEAR uses linear-interpolated stretching.
GL_TEXTURE_MIN_FILTERDetermines what kind of stretching should be done if a texture-mapped polygon is rendered smaller than the texture mapped to it is(pixel-wise). GL_NEAREST uses simple "blocky" stretching for textures, whereas GL_LINEAR uses linear-interpolated stretching.

And now, for a ludicrous amount of pictoral examples! The four diagrams below represent textured square quads with (0.0, 0.0) as the lower-left texture coordinate pair and (2.0, 2.0) as the upper-right texture coordinate pair, meaning that the upper-right corner extends past the edge of the texture. Remember that S refers to the horizontal(X) wrap and T refers to the vertical(Y) wrap.

The original texture.
The texture used for the following examples.


A texture with S clamping and T clamping. A texture with S repeating and T repeating.
On the left: GL_TEXTURE_WRAP_S = GL_CLAMP; GL_TEXTURE_WRAP_T = GL_CLAMP.
On the right: GL_TEXTURE_WRAP_S = GL_REPEAT; GL_TEXTURE_WRAP_T = GL_REPEAT.

A texture with S clamping and T repeating. A texture with S repeating and T clamping.
On the left: GL_TEXTURE_WRAP_S = GL_CLAMP; GL_TEXTURE_WRAP_T = GL_REPEAT.
On the right: GL_TEXTURE_WRAP_S = GL_REPEAT; GL_TEXTURE_WRAP_T = GL_CLAMP.

Now for the pictoral examples of the magnification/minification settings(don't ask me why there's a whole load of pictures all of a sudden).

A texture scaled with the GL_NEAREST parameter.
Using GL_NEAREST for both the GL_TEXTURE_MAG_FILTER and the GL_TEXTURE_MIN_FILTER. First, the original 32x32 texture; then, the texture scaled to 66%; then, the texture scaled to 133%. Notice how the textures appear "blocky", especially in the smaller texture.


A texture scaled with the GL_NEAREST parameter.
Using GL_LINEAR for both the GL_TEXTURE_MAG_FILTER and the GL_TEXTURE_MIN_FILTER. Notice how the textures appear much smoother thanks to the linear interpolation.


GL_LINEAR is nice, isn't it? Yes, yes it is. Now, as I said, glTexParameter() has other uses, such as for mipmapping, and texture bordering... but maybe I'll talk about those later :D

In the Next Lesson...

Next, we'll learn about some advanced blending options. Impress your friends with a snazzy additive-blended particle engine! That is, if you make one on your own, using additive blending, which will be covered in the next lesson.