Featured Posts

8. Bump Mapping in GLSL8. Bump Mapping in GLSL Introduction Bump mapping is essential in todays computer games, and computer graphics in general. Would you like to know the best thing about it? It is extremely simple to implement. Bump mapping works...

Read more

Swiftless GLSL Shader DeveloperSwiftless GLSL Shader Developer Swiftless GLSL Shader Developer   Version 0.1a Currently Swiftless GLSL Shader Developer is in it's first public release, and is currently in alpha status, meaning it is not complete and may contain...

Read more

Wordpress Optimization Wordpress Website Optimizations Introduction Wordpress itself is a fairly wonderful tool. Since switching to it, I find it is a lot quicker to make changes to my website and it is also quicker to get...

Read more

36. OpenGL Framebuffers36. OpenGL Framebuffers Introduction Frame buffers are one of those mythical things that we have all heard of, but many beginner OpenGL developers avoid because there is not much information about them, and they can be confusing...

Read more

1. Terrain Class1. Terrain Class Terrain is one of those things that so far, hasn't been perfectly recreated in computer graphics. But it is almost there! Looking over a beautiful landscape can be one of the most amazing feelings in the...

Read more

  • Prev
  • Next

7. Texturing in GLSL

Posted on : 25-03-2010 | By : Swiftless | In : GLSL

Tags: , , , ,

6

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 :D

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

VN:F [1.9.3_1094]
Please rate so I know where to improve the site. 1 means needs a lot of improvement, 10 means perfect. If you leave a low rating, please state why. I don't want people just coming to bash the site.
Rating: 9.5/10 (4 votes cast)
VN:F [1.9.3_1094]
Rating: +1 (from 1 vote)
7. Texturing in GLSL, 9.5 out of 10 based on 4 ratings

Related posts:

  1. 8. Bump Mapping in GLSL
  2. 16. OpenGL Texturing
  3. 3. GLSL Coloring
  4. 5. GLSL Per Pixel Lighting
  5. 4. GLSL Lighting

Comments (6)

…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”);

and

uniform sampler2D color_texture;

dont seem to line up, is this intentional?

VA:F [1.9.3_1094]
Rating: 0.0/5 (0 votes cast)
VA:F [1.9.3_1094]
Rating: 0 (from 0 votes)

Hey Fonix,

That’s a typo, just rename either of the variables to match up with the other.

Cheers,
Swiftless

VN:F [1.9.3_1094]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

Hey Bloodust,

I’m surprised you got all the way to tutorial 7 before that :P

I’ve mentioned that in the comments for the previous tutorials. The reason I don’t have it, is because I made these tutorials on OSX and on OSX that call isn’t needed.

Cheers,
Swiftless

VN:F [1.9.3_1094]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

Great but missing glew initialization and without it my shader couldnt be created because
“Unhandled exception at 0×00000000″

add this to main before init()

if(glewInit() != GLEW_OK)
return 0;
or just
glewInit();

VA:F [1.9.3_1094]
Rating: 0.0/5 (0 votes cast)
VA:F [1.9.3_1094]
Rating: 0 (from 0 votes)

@Swiftless:
actually my game using shader from yours tuts looks like this:
http://www.gamedev.pl/projects.php?x=view&id=1198

VA:F [1.9.3_1094]
Rating: 0.0/5 (0 votes cast)
VA:F [1.9.3_1094]
Rating: +1 (from 1 vote)

Great tutorial!

I forgot the line
gl_TexCoord[0] = gl_MultiTexCoord0;
in my vertex shader until i saw your tutorial, then it was suddenly working.
it can feel so great to see a simple 2d texture quad on the screen sometimes when you’re doing graphics programming.

VA:F [1.9.3_1094]
Rating: 0.0/5 (0 votes cast)
VA:F [1.9.3_1094]
Rating: 0 (from 0 votes)

Write a comment

Improve the web with Nofollow Reciprocity.