7. Texturing in GLSL

Jump To:

main.cpp Source
shader.vert Source
shader.frag Source
Download

Woo, congrats for making it this far. By now, you should know how to use basic shaders for lighting an object. So the next logical step would be to add some textures to our shape.

But first, because we are introducing a new type of shader variable, calld a Uniform variable, let me first explain it. A Uniform variable is a variable that we write to in our OpenGL application, and can then send through to a shader program. The Uniform variable can be an integer, a float, a vector, and even a matrix.

Because a texture is stored by OpenGL using an integer for identification, we will be sending through an integer variable to our shader by using Uniforms.

Main.CPP File:

The main.cpp is going to have some fairly minor changes. All of the texturing code itself is taken from previous tutorials, so I am going to jump to the stuff we haven’t seen before.

If you want to just jump to the cube() method (which by now draws a teapot because I didn’t rename the method, naughty me), you will notice 4 lines of code are used to setup our textures.

The first line is a call you should have seen from the multitexturing tutorial. We are going to tell OpenGL (and therefore GLSL0), that we are working with our texture unit 0.
    glActiveTexture(GL_TEXTURE0);

After deciding which texture we are going to use, we then need a variable that tells us the location of our texture variable in our shader. This is done with a call to glGetUniformLocation, and takes the paramters for the shader id, and the name of the variable inside the shader for our texture.
    int texture_location = glGetUniformLocation(shader.id(), “texture_color”);

Now that we know where our variable is inside the shader, we can then go ahead and set it. Because OpenGL uses numbers to identify the textures, we make a call to glUniform1i, which means we have a variable going into the shader (uniform), it is one value long (as opposed to a vec2 or vec3), and the value we are sending it is the first texture for our shader. The numbering for the textures in the shader, go from 0…n.
    glUniform1i(texture_location, 0);

And finally, we do what we always do when we are texturing, and we bind our texture that we want to use.
    glBindTexture(GL_TEXTURE_2D, texture);

This is fairly simple, but when dealing with multiple texture inputs to a shader, remember that you need to use the glActiveTexture calls in order, and then when you have finished with them, set the active textures in reverse order and disable texturing on each of the texture units to cleanup.

 

Feel free now to jump down to our shaders, and checkout the changes.

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
    #if ( (defined(__MACH__)) && (defined(__APPLE__)) )   
#include <stdlib.h>
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#include <OpenGL/glext.h>
#else
#include <stdlib.h>
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include <GL/glext.h>
#endif

#include “shader.h”

Shader shader;

GLuint texture;

GLfloat angle = 0.0; //set the angle of rotation

GLuint LoadTexture( const char * filename, int width, int height )
{
    GLuint texture;
    unsigned char * data;
    FILE * file;
    
    //The following code will read in our RAW file
    file = fopen( filename, “rb” );
    if ( file == NULL ) return 0;
    data = (unsigned char *)malloc( width * height * 3 );
    fread( data, width * height * 3, 1, file );
    fclose( file );
    
    glGenTextures( 1, &texture ); //generate the texture with the loaded 
data

    glBindTexture( GL_TEXTURE_2D, texture ); //bind the texture to it’s 
array

    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, 
GL_MODULATE ); /
/set texture environment parameters

    
    //And if you go and use extensions, you can use Anisotropic filtering 
textures which are of an

    //even better quality, but this will do for now.
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 
GL_LINEAR);
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, 
GL_LINEAR);
    
    //Here we are setting the parameter to repeat the texture instead of 
clamping the texture

    //to the edge of our shape. 
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
    
    //Generate the texture
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, 
GL_UNSIGNED_BYTE, data);
    free( data ); //free the texture
    
    return texture; //return whether it was successfull
}

void FreeTexture( GLuint texture )
{
    glDeleteTextures( 1, &texture );
}

void init(void) {
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    
    shader.init(“shader.vert”, “shader.frag”);
    texture = LoadTexture(“texture.raw”, 256, 256);
}

void cube (void) {
    glRotatef(angle, 1.0, 0.0, 0.0); //rotate on the x axis
    glRotatef(angle, 0.0, 1.0, 0.0); //rotate on the y axis
    glRotatef(angle, 0.0, 0.0, 1.0); //rotate on the z axis

    glActiveTexture(GL_TEXTURE0);
    int texture_location = glGetUniformLocation(shader.id(), “texture_color”);
    glUniform1i(texture_location, 0);
    glBindTexture(GL_TEXTURE_2D, texture);
    
    glutSolidTeapot(2.0);
}

void display (void) {
    glClearColor (0.0,0.0,0.0,1.0);
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();  
    gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
    
    shader.bind();
    cube();
    shader.unbind();
    
    glutSwapBuffers();
    angle += 0.01f;
}

void reshape (int w, int h) {
    glViewport (0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
    glMatrixMode (GL_MODELVIEW);
}

int main (int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); //set up
 the double buffering

    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(“A basic OpenGL Window);
    
    glewInit();

    glutDisplayFunc(display);
    glutIdleFunc(display);
    
    glutReshapeFunc(reshape);
    
    init();
    
    glutMainLoop();
    
    return 0;
}

Vertex Shader Source:

In this tutorial, we are going to go back to our first shader that we ever made. So this shader will not be using lighting. Ontop of our base shader, all we need is one extra line in our vertex shader.

This line will write to the variable gl_TexCoord[0], the texture coordinates for the current vertex. The variable gl_TexCoord[0] is simply a varying variable between the vertex shader and the fragment shader.

This line is also where the calls to glActiveTexture start to make sense.

When we feed our texture coordinates through to OpenGL, GLSL stores them in the appropriate gl_MultiTexCoord(n) call. This allows us to set multiple uv coordinates per vertex, which is useful when multitexturing.

In the end, our one line needed in the vertex shader to enable texturing, is:
gl_TexCoord[0] = gl_MultiTexCoord0;

1.
2.
3.
4.
5.
6.
7.
    void main() {            
    gl_TexCoord[0] = gl_MultiTexCoord0;
    
    // Set the position of the current vertex 
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

 

Fragment Shader Source:

Our Fragment shader is also going to introduce one new line of code, but we are also going to modify another.

The line we want to add, will be used to setup the input location for our texture variable, and will give us a variable we can use to access the texture.

uniform sampler2D color_texture;

You may be wondering why the variable is of the type sampler2D, I honestly have no idea of the naming, I have never looked into it. But all you have to remember is that a sampler2D is code for a 2D texture. You can also get sampler1D’s and sampler3D’s, along with samplers for shadows and projections. But these are a little more advanced, and if all goes well, I’ll have some tutorials for them later.

Now as you know, we need to set the gl_FragColor from our fragment shader, so that we get something on the screen.

We are going to modify our original line to look like:
 gl_FragColor = texture2D(color_texture, gl_TexCoord[0].st);

This line means that we have a 2D texture that we are reading in, and this texture is associated with the color_texture variable, and finally we are calling the u,v coordinates for the current fragment.

When we are calling gl_TexCoord[0].st, it is good to remember that this variable is actually a vec4 variable with the values s, t, p and q. But we only want to use the first two values, s and t. Theoretically you can replace st with xy or even rg, to pull out the first two values, as it is only a vec4 variable. But switching the naming aroung like this can be confusing.

It is also good to keep in mind, that texture2D returns a vec4 variable, with the values for (red, green, blue and alpha). Which is perfect because gl_FragColor requires a vec4.

And thats all there is to texturing in GLSL. It is quite simple, I have just gone to the effort to over explain it for everyone 😀

If you have any questions, please email me at swiftless@gmail.com

1.
2.
3.
4.
5.
6.
7.
    uniform sampler2D color_texture;

void main() {
    // Set the output color of our current pixel
    gl_FragColor = texture2D(color_texture, gl_TexCoord[0].st);
}

 

Download:

Download shader.h Source Code for this Tutorial

Download shader.cpp Source Code for this Tutorial

Download main.cpp Source Code for this Tutorial

Download shader.vert Source Code for this Tutorial

Download shader.frag Source Code for this Tutorial

  • March 25, 2010
  • 19