Lesson 4: Texture Mapping

As the name suggests, we will deal with texture mapping in this lesson. Basic knowledge of texturing in OpenGL is required (e.g. binding textures, texture coordinates, texture units, ...). After the elementary shader functionality in GLSL is explained, you are going to write your own multi-texture shader.


 1. Textures in GLSL

GLSL provides numerous functions and variables that enable the programmer to use texturing in their shaders. Below, we are going to explain these for vertex shaders and pixel shaders.


 Texture operations in vertex shaders

As always, vertex shaders deal with coordinates. In this case we concentrate on the texture coordinates of the vertices that are passed from OpenGL to the shader. They can be accessed in a GLSL shader through attributes called gl_MultiTexCoord0 to gl_MultiTexCoord7. One can access the fields, s, t, r, and q, of a texture coordinate with the [] operator or the . operator and the alias names .s, .t, .p, .q. Someone familiar with texture coordinates may wonder why the third component is called .p and not .r. The reason is simple, the identifier .r is already reserved for the first component of a color vector.

After the vertex shader has manipulated a texture coordinate, it has to write the result into the varying variable gl_TexCoord[0-7]. The interpolated texture coordinates can be processed further in a pixel shader.

In most cases, the texture coordinate is fed back into the pipeline unchanged:


	gl_TexCoord[0] = gl_MultiTexCoord0;

Listing #1: Writing the texture coordinate in a vertex shader


 Texture operations in pixel shaders

To be able to access textures in a pixel shader, we have to make them known by declaring uniform variables of the types sampler[1-3]D. They have to be bound to a specific texture unit, using OpenGL calls in the program code.

A pixel shader is only able to color one fragment in a single pass. This means that we have to be able to access individual color values of a texture. The provided GLSL functions are called texture1D(sampler1D, float), texture2D(sampler2D, vec2), and texture3D(sampler3D, vec3).

Normally, the texture is read at the position determined by the interpolated texture coordinates:


[texturize.frag]
	uniform sampler2D tex0;

	void main(void)
	{
		gl_FragColor = texture2D(tex0, gl_TexCoord[0].st);
	}

Listing #2: Reading a texel in a pixel shader


 2. Flag shader

Having programmed relatively simple shaders in the past lessons, we will raise the bar now. This time you are going to write a multi-texturing shader with vertex animation.


 Task

Your task for this lesson is to create a flag effect that uses vertex animation and multi-texturing. This is a chance to use the things you just learned about, putting GLSL texturing in practice. The figure below shows how the result might look like:


Figure #1: “Up ewich ungedeelt“ (“Forever undivided“)


 Environment

In order to produce the wanted effect, you need to use some OpenGL resources that cannot be set up in a shader. Therefore, ShaderSchool has already created and initialized them for you.

Firstly, there is a grid model that is to be animated to move like a flag. For this to happen its verticies have to be manipulated in the vertex shader. In wireframe mode the model looks like this:


Figure #2: The basic grid model

The first two texture units are bound to two textures. The first texture shows a flag and the second one is an alpha channel texture. Both textures, combined with multiplication, produce the tattered look that can be seen in figure #1. There is no need to correct the aspect ratio of the square textures to fit onto a flag, the texture coordinates already account for that.

Figure #3: The textures in texture units 0 and 1

To create an animation, a shader needs access to a value that is constantly changing. For this effect, there exists a uniform variable of type float. It indicates the seconds that have passed since the last shader compile.



	vertex.x += sin(time);

Listing #3: We only tell you this much

 Tips