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

8. Bump Mapping in GLSL

Posted on : 24-06-2010 | By : Swiftless | In : GLSL, Tutorials

Tags: , , , , , , , ,

4

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 by taking an image which stores surface normals on a per-pixel basis. This means that we can apply standard lighting techniques that require a normal value on a per-pixel basis instead of a per-vertex or per-surface basis. This gives our applications a greatly added sense of realism.

How does bump mapping work in OpenGL with GLSL though? GLSL texturing works on a per-texel basis, where a texel can be thought of as a scaled pixel, either larger or smaller, and we get access to a texel at any fragment on an objects surface when we are texturing in GLSL. We already covered texturing in GLSL in a previous tutorial, so I won’t go over it again.

When you look at a normal map, it is generally a blue and purple color. To get your head around normal mapping, all you need to understand is that each pixel in this image file, can be mapped to a normal value. Once you can understand that, normal mapping is extremely straight forward.

Don’t get this tutorial on bump mapping confused with tangent-space bump mapping though. There aren’t many tutorials around that explain the different, but regular bump mapping only cares about the normal maps relation to lights. Tangent space bump mapping takes it a step further and takes into account the objects surfaces in relation-to the light as well as the normal maps relation.

Coding

Enough theory, how can we get it into OpenGL and GLSL. The first thing we will want to do is load in our normal map, and apply it to our object in OpenGL. This requires multi-texturing in OpenGL, which I don’t believe I have covered in GLSL, so I will include that in this tutorial. This code is going to be based on the GLSL texturing tutorial, but you may notice some additions to make it cross-platform between OSX and Windows correctly.

Main.cpp

We are only going to need one new variable for the main.cpp file which will be used to store our normal map texture. This is stored and used just like a normal texture. I am going to call this normal_texture, and define it with our texture variable at the top of our file.

GLuint normal_texture; // Our normal map texture

Then go down to your init method, and remove the current line that creates a texture, and replace it with the following two lines, which will create both of our new

texture = LoadTexture("colour_map.raw", 256, 256);
normal_texture = LoadTexture("normal_map.raw", 256, 256);

Inside of our init method you may also notice the following code, this is for cross-platform compatibility and simply checks if we are on OSX or not. If not, we initialize glew.

#if ( (defined(__MACH__)) && (defined(__APPLE__)) )
#else
glewInit();
#endif

The rest of our code for the main source code file, goes into our cube method, which I have now modified to draw a square instead.

You should already have code to set the first texture, and we are going to adjust this slightly by adding the following line after we set our active texture:

glEnable(GL_TEXTURE_2D);

Our code to set up our first texture should now look like:

glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
int texture_location = glGetUniformLocation(shader.id(), "color_texture");
glUniform1i(texture_location, 0);
glBindTexture(GL_TEXTURE_2D, texture);

Now we need to set up our second texture. This is done by activating the next texture, GL_TEXTURE1 and modifying the other calls to point to our normal map texture. When we do this, we need to make sure that our glUniform1i for our normal map location in the shader, has the value 1. When multi-texturing in GLSL, each texture used will have an incremented value in the shader. Our final code will look like:

glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
int normal_location = glGetUniformLocation(shader.id(), "normal_texture");
glUniform1i(normal_location, 1);
glBindTexture(GL_TEXTURE_2D, normal_texture);

I won’t explain the code for drawing the square, as I have done this in previous tutorials, so I will jump to after our square. We will need to do some cleaning up, as we don’t want our texture states to mess up when we render other objects later on. All I am doing to clean up, is setting the active texture, binding it to nothing, and then disabling texturing on this texture. Note that I am doing this in reverse to how I have set the textures. You can do this in any order, but I like this as I find it easier to follow (count up on initialization, and then count down on de-initialization).

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);

Shader.vert

Our vertex shader requires absolutely no extra calls, so just use the one provided in the previous texturing tutorial.

Shader.frag

Shader.frag is going to contain all of our bump mapping calculations. To get this going, we need to add a new uniform variable, which will load in our normal mapping texture:

uniform sampler2D normal_texture;

Now that we have access to our normal map, lets load it in. For a normal, we only require a 3 value vector, or vec3 in GLSL, and we are going to normalize this to make sure the final normal vector is of unit length. In other words, it has a length of 1.0. But in this line take note of the (* 2.0 – 1.0). When we read in a texture value, we get a number between 0.0 and 1.0 as color cannot have a negative value. This then multiplies the value by 2.0, giving us values between 0.0 and 2.0, and then subtracts 1.0 so we get our final values in the range of -1.0 to 1.0. We have to do this manipulation as a normal vector is 3D and can be pointing in any direction. It’s a nifty little trick which allows us to convert a normal map texture file to a normal map.

// Extract the normal from the normal map
vec3 normal = normalize(texture2D(normal_texture, gl_TexCoord[0].st).rgb * 2.0 - 1.0);

Since we now have the normal value for the current pixel, I am going to set up a quick light, located at the top right of the scene, and also a little towards us. Usually you would base this on actual lights in your scene, and to do so, take a look at my GLSL lighting tutorial.

// Determine where the light is positioned (this can be set however you like)
vec3 light_pos = normalize(vec3(1.0, 1.0, 1.5));

Finally we can get to some familiar code. We are going to calculate the diffuse value for the current pixel, based on our normal extracted from the normal map. Then we are going to multiply the diffuse value against our color texture, and assign the output to our gl_FragColor variable.

// Calculate the lighting diffuse value
float diffuse = max(dot(normal, light_pos), 0.0);

vec3 color = diffuse * texture2D(color_texture, gl_TexCoord[0].st).rgb;

// Set the output color of our current pixel
gl_FragColor = vec4(color, 1.0);

And now you should have a perfectly working bump mapping shader. If you have any questions, feel free to email me at swiftless@gmail.com or leave a comment below.

Downloads

Colour Map Texture
Normal Map Texture

Tutorial Code

Main.cpp

#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;
GLuint normal_texture; // Our normal map texture

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

#if ( (defined(__MACH__)) && (defined(__APPLE__)) )
#else
glewInit();
#endif

shader.init("shader.vert", "shader.frag");

texture = LoadTexture("colour_map.raw", 256, 256);
normal_texture = LoadTexture("normal_map.raw", 256, 256);
}

void cube (void) {
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
int texture_location = glGetUniformLocation(shader.id(), "color_texture");
glUniform1i(texture_location, 0);
glBindTexture(GL_TEXTURE_2D, texture);

glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
int normal_location = glGetUniformLocation(shader.id(), "normal_texture");
glUniform1i(normal_location, 1);
glBindTexture(GL_TEXTURE_2D, normal_texture);

glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 0.0f);

glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, 0.0f);

glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 0.0f);

glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 0.0f);
glEnd();

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

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, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

shader.bind();
cube();
shader.unbind();

glutSwapBuffers();
}

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");

init();

glutDisplayFunc(display);
glutIdleFunc(display);

glutReshapeFunc(reshape);

glutMainLoop();

return 0;
}

Shader.vert

void main() {
gl_TexCoord[0] = gl_MultiTexCoord0;

// Set the position of the current vertex
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

Shader.frag

uniform sampler2D color_texture;
uniform sampler2D normal_texture;

void main() {

// Extract the normal from the normal map
vec3 normal = normalize(texture2D(normal_texture, gl_TexCoord[0].st).rgb * 2.0 - 1.0);

// Determine where the light is positioned (this can be set however you like)
vec3 light_pos = normalize(vec3(1.0, 1.0, 1.5));

// Calculate the lighting diffuse value
float diffuse = max(dot(normal, light_pos), 0.0);

vec3 color = diffuse * texture2D(color_texture, gl_TexCoord[0].st).rgb;

// Set the output color of our current pixel
gl_FragColor = vec4(color, 1.0);
}
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: 8.0/10 (3 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 2 votes)

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)

6. GLSL Materials

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

Tags: , , , , ,

3

Jump To:

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

Materials have already been explain for use in standard OpenGL in the OpenGL tutorials section fo the website, this tutorial will focus on how to use them inside of a GLSL shader program.

Main.CPP File:

All the code that has been added to this file, has come from the lighting tutorials fromt he OpenGL section of the site, with some minor modifications.

I have also removed the line glColor4f(1.0, 0.0, 0.0, 1.0); so that you don’t get confused with the material and color values.

Feel free to jump to the shaders below.

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.
  #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;

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

//light position variables
GLfloat lx = 0.0;
GLfloat ly = 1.0;
GLfloat lz = 1.0;
GLfloat lw = 0.0;

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

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

    GLfloat mShininess[] = {50};
    
    GLfloat DiffuseMaterial[] = {1.0, 0.0, 0.0};
    GLfloat AmbientMaterial[] = {0.0, 0.0, 0.0};
    GLfloat SpecularMaterial[] = {1.0, 1.0, 1.0};
    
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, DiffuseMaterial);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, AmbientMaterial);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, SpecularMaterial);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mShininess);
    
    glutSolidTeapot(2.0);
}

void setLighting(void) {
    GLfloat DiffuseLight[] = {1.0, 1.0, 1.0};
    GLfloat AmbientLight[] = {0.2, 0.2, 0.2};
    GLfloat SpecularLight[] = {1.0, 1.0, 1.0};
    
    glLightfv (GL_LIGHT0, GL_SPECULAR, SpecularLight);
    glLightfv (GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
    glLightfv (GL_LIGHT0, GL_AMBIENT, AmbientLight);
    
    GLfloat LightPosition[] = {lx, ly, lz, lw}; //set the LightPosition to 
the specified values

    
    glLightfv (GL_LIGHT0, GL_POSITION, LightPosition); //change the 
light accordingly

}

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);
    
    setLighting();
    
    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);
    
    glutDisplayFunc(display);
    glutIdleFunc(display);
    
    glutReshapeFunc(reshape);
    
    init();
    
    glutMainLoop();
    
    return 0;
}

Vertex Shader Source:

Our Vertex Shader for this tutorial, is going to only have one variable added to it. This variable will be called:

varying vec3 vertex_light_half_vector;

And it will store the half-vector for our current light.

To get the half-vector from OpenGL, we can use the line:

vertex_light_half_vector = normalize(gl_LightSource[0].halfVector.xyz);

The half vector will be used to calculate the specular term for our lighting.

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
  varying vec3 vertex_light_position;
varying vec3 vertex_light_half_vector;
varying vec3 vertex_normal;

void main() {            
    // Calculate the normal value for this vertex, in world coordinates (
multiply by gl_NormalMatrix)

    vertex_normal = normalize(gl_NormalMatrix * gl_Normal);
    // Calculate the light position for this vertex
    vertex_light_position = normalize(gl_LightSource[0].position.xyz);

    // Calculate the light’s half vector
    vertex_light_half_vector = normalize(gl_LightSource[0].halfVector.xyz);

    // Set the front color to the color passed through with glColor*f
    gl_FrontColor = gl_Color;
    // Set the position of the current vertex 
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

 

Fragment Shader Source:

The Fragment Shader is where all of our fancy lighting magic is going to take place.

First off, we need to include our varying variable:

varying vec3 vertex_light_half_vector;

Now for the magic. We are going to use 3 vec4 variables, called:
ambient_color
diffuse_color
specular_color

These are going to hold the final color values for our lighting.

To compute the ambient term, we nueed to multiply the front material for our object, by the ambient color for our light. And then add this to the global ambient value multiplied by our front materials ambient value. The final equation looks like:

vec4 ambient_color = gl_FrontMaterial.ambient * gl_LightSource[0].ambient + gl_LightModel.ambient * gl_FrontMaterial.ambient;

For the diffuse colour value, we need to multiply our front material by the diffuse value for our light source:

vec4 diffuse_color = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;

All that we have left to work out now, is our specular value. This is slightly trickier, and is where our half_vector comes into play.

We start off the same as the other color values, and multiply our front material specular value, by the specular value of the light source. We then multiply this by the vertex_light_half_vector dotted with the vertex_normal, all to the power of gl_FrontMaterial.shininess. This gives us the intensity for our specular term. Our final equation looks like:

vec4 specular_color = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(max(dot(vertex_normal, vertex_light_half_vector), 0.0) , gl_FrontMaterial.shininess);

Now all that is left, is to put all this together in our gl_FragColor value. To do this, you need to have some idea about how lighting in OpenGL works. First we need the ambient term, this is going to be there no matter what, and isn’t affected by shadow. Then we need to add our diffuse colour, but we need to multiply this by our diffuse_value to get a simulated shadow effect. Finally, we want to add our specular value to the surface of our object.

Giving us the final line:

gl_FragColor = ambient_color + diffuse_color * diffuse_value + specular_color;

And now you should be all set to go ahead with lighting and materials in OpenGL and GLSL. The next step would be to add multiple light support. And depending on your graphics card, you either need to hard code this for each light, or create a loop to calculate this for ever light. I have found that on the 9600 Nvidia graphics card on my Macbook Pro, I cannot use more than 4 lights, because I run out of varying variables available on my shader. But my GTX260 on my Desktop PC can use all 8 of OpenGL’s lights.

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

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.

  varying vec3 vertex_light_position;
varying vec3 vertex_light_half_vector;
varying vec3 vertex_normal;

void main() {
    // Calculate the ambient term
    vec4 ambient_color = gl_FrontMaterial.ambient * gl_LightSource[0]
.ambient + gl_LightModel.ambient * gl_FrontMaterial.ambient;

    // Calculate the diffuse term
    vec4 diffuse_color = gl_FrontMaterial.diffuse * gl_LightSource[0]
.diffuse;

    // Calculate the specular value
    vec4 specular_color = gl_FrontMaterial.specular * gl_LightSource[
0].specular * pow(max(dot(vertex_normal, vertex_light_half_vector), 0.0) , gl_FrontMaterial.shininess);

    // Set the diffuse value (darkness). This is done with a dot product between
 the normal and the light

    // and the maths behind it is explained in the maths section of the site.
    float diffuse_value = max(dot(vertex_normal, vertex_light_position), 0.0);

    // Set the output color of our current pixel
    gl_FragColor = ambient_color + diffuse_color * diffuse_value +
specular_color;
}

 

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: 6.0/10 (3 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

5. GLSL Per Pixel Lighting

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

Tags: , , , , , ,

0

Jump To:

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

This tutorial is going to be quite simple. To achieve the effect of per pixel lighting, we simply need to move one calculation from our vertex shader to our fragment shader.

So lets go to it!

Main.CPP File:

Just for the sake of it, I have switched to a glutSolidTeapot for this tutorial. It will come on handy when testing different effects.

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.
  #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;

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

//diffuse light color variables
GLfloat dlr = 1.0;
GLfloat dlg = 1.0;
GLfloat dlb = 1.0;

//ambient light color variables
GLfloat alr = 0.0;
GLfloat alg = 0.0;
GLfloat alb = 0.0;

//light position variables
GLfloat lx = 0.0;
GLfloat ly = 1.0;
GLfloat lz = 1.0;
GLfloat lw = 0.0;

void init(void) {
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    
    shader.init(“shader.vert”, “shader.frag”);
}

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
    glColor4f(1.0, 0.0, 0.0, 1.0);
    glutSolidTeapot(2);
}

void setLighting(void) {
    GLfloat DiffuseLight[] = {dlr, dlg, dlb}; //set DiffuseLight[] to the 
specified values

    GLfloat AmbientLight[] = {alr, alg, alb}; //set AmbientLight[] to the 
specified values

    
    glLightfv (GL_LIGHT0, GL_DIFFUSE, DiffuseLight); //change the light
 accordingly

    glLightfv (GL_LIGHT0, GL_AMBIENT, AmbientLight); //change the light
 accordingly

    
    GLfloat LightPosition[] = {lx, ly, lz, lw}; //set the LightPosition to 
the specified values

    
    glLightfv (GL_LIGHT0, GL_POSITION, LightPosition); //change the 
light accordingly

}

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);
    
    setLighting();
    
    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 the last tutorial, to achieve lighting, we had one varying variable called diffuse_value. In this tutorial, we are going to remove this varying, and create two others. One of them is going to contain the vertex_light_position, and the other is going to contain the vertex_normal:

varying vec3 vertex_light_position;
varying vec3 vertex_normal;

Now we need to store some values in here. This is collected the exact same way as last time:

    vertex_normal = normalize(gl_NormalMatrix * gl_Normal);

    vertex_light_position = normalize(gl_LightSource[0].position.xyz);

And that’s all their is in the vertex shader. Note that I have removed the diffuse_value variable.

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
  varying vec3 vertex_light_position;
varying vec3 vertex_normal;

void main() {            
    // Calculate the normal value for this vertex, in world coordinates (
multiply by gl_NormalMatrix)

    vertex_normal = normalize(gl_NormalMatrix * gl_Normal);
    // Calculate the light position for this vertex
    vertex_light_position = normalize(gl_LightSource[0].position.xyz);

    // Set the front color to the color passed through with glColor*f
    gl_FrontColor = gl_Color;
    // Set the position of the current vertex 
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

 

Fragment Shader Source:

Since I changed the varying variables in the Vertex Shader, I also need to update them in the fragment shader:

varying vec3 vertex_light_position;
varying vec3 vertex_normal;

Now, if you remember the diffuse_value calculation from the previous tutorial, it has been pretty much copied and pasted into this tutorial, only now it resides inside the fragment shader:

    float diffuse_value = max(dot(vertex_normal, vertex_light_position), 0.0);

And that is all their is to it. We now have per pixel lighting. That wasn’t too hard :)

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

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.

  varying vec3 vertex_light_position;
varying vec3 vertex_normal;

void main() {
    // Set the diffuse value (darkness). This is done with a dot product between
 the normal and the light

    // and the maths behind it is explained in the maths section of the site.
    float diffuse_value = max(dot(vertex_normal, vertex_light_position), 0.0);

    // Set the output color of our current pixel
    gl_FragColor = gl_Color * diffuse_value;
}

 

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: 8.3/10 (4 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

4. GLSL Lighting

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

Tags: , , , , , ,

2

Jump To:

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

Ok, so we can create a shader program, and render a shape with a specified color. Lets move into lighting. In this tutorial, I am going to show you how to perform per vertex lighting, which is then easily modified to achieve per pixel lighting in the next tutorial.

Lets get moving :)

Main.CPP File:

There are going to be quite a few additions to this code, but you might recognise most of it from the lighting tutorials.

I am creating a directional light, that points in the direction [0,1,1]. It has a diffuse color of white, and an ambient color of black.

I have placed the calls to setup my lighting inside of a new method, which is called during our display method, *after* we set the camera position. I have a tip on the OpenGL Tips page, that explains why we do this.

Another minor change, is that I am now using a glutSolidSphere, I have kept it to a low polygon count, only 10 stacks and 10 slices. This is to show how per vertex will differ to per pixel lighting later on.

But enough of that, lets move onto our shaders. You should already know how to setup lighting in a standard OpenGL application.

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.
  #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;

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

//diffuse light color variables
GLfloat dlr = 1.0;
GLfloat dlg = 1.0;
GLfloat dlb = 1.0;

//ambient light color variables
GLfloat alr = 0.0;
GLfloat alg = 0.0;
GLfloat alb = 0.0;

//light position variables
GLfloat lx = 0.0;
GLfloat ly = 1.0;
GLfloat lz = 1.0;
GLfloat lw = 0.0;

void init(void) {
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    
    shader.init(“shader.vert”, “shader.frag”);
}

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
    glColor4f(1.0, 0.0, 0.0, 1.0);
    glutSolidSphere(2, 10, 10);
}

void setLighting(void) {
    GLfloat DiffuseLight[] = {dlr, dlg, dlb}; //set DiffuseLight[] to the 
specified values

    GLfloat AmbientLight[] = {alr, alg, alb}; //set AmbientLight[] to the 
specified values

    
    glLightfv (GL_LIGHT0, GL_DIFFUSE, DiffuseLight); //change the light
 accordingly

    glLightfv (GL_LIGHT0, GL_AMBIENT, AmbientLight); //change the light
 accordingly

    
    GLfloat LightPosition[] = {lx, ly, lz, lw}; //set the LightPosition to 
the specified values

    
    glLightfv (GL_LIGHT0, GL_POSITION, LightPosition); //change the 
light accordingly

}

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);
    
    setLighting();
    
    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);
    
    glutDisplayFunc(display);
    glutIdleFunc(display);
    
    glutReshapeFunc(reshape);
    
    init();
    
    glutMainLoop();
    
    return 0;
}

Vertex Shader Source:

For this tutorial, I am going show you how to perform per
vertex lighting using shaders. Yes, shaders support per pixel
but this is just a starting point.

First I am going to teach you about varying variables. These
are variables that can be shared between the vertex and fragment
shaders. These variables are set inside the vertex shader, and
then read from the fragment shader. In this case we are going
to share a float type variable from the Vertex shader to the
Fragment shader. This variable is going to be called diffuse_value
and will hold a number that will tell us how lit our vertex
should be.

Because we are using per vertex lighting, the GLSL shader will
automatically interpolate the color between the vertices. Just
like OpenGL does.

Now first off we need to know the surface normal of the current
vertex. Because I am using a GLUT sphere, these are already
calculated and sent off with the glNormal3f command. Any numbers
sent from OpenGL through glNormal*; can be read with
the variable gl_Normal. In later tutorials, I will be using
self calculated normals, using my own normal generation code.

Now as for our normal, I am storing it in the variable vec3 vertex_normal.
But first, I have to multiply the normal (gl_Normal) by
the gl_NormalMatrix. This places the normal into world coordinates so that
we can use it correctly. (Later on I will show you how to use tangent space
normals). We also then have to normalize this multiplication so
that all of the normal vectors are between (and including) -1 and 1. Our final call looks like:

vec3 vertex_normal = normalize(gl_NormalMatrix * gl_Normal);

Next on, we are going to work with our light. I am storing this
in a variable called vec3 vertex_light_position. In this, I am calling for the
position of glLight0. This is gathered by the call:

vec3 vertex_light_position = gl_LightSource[0].position.xyz;

This gives us the position of glLight0, and to get the position of
any other light, we just change the number 0 to the light number we
are after. We do not need to multiply this expression by the
gl_NormalMatrix, it is already in world coordinates, as it has been multiplied by the ModelView Matrix before we set it’s position, but we do have to normalize it still.

Now that we have our vertex_normal and the position of our vertex_light_position.
We can now calculate the diffuse_value of our lighting at the
given vertex. To do this we need to take the dot product of both
the vertex_normal and the vertex_light_position together. If you have any idea on
calculating normals, then you should have a fair idea of what the
dot product does. It works as such:
dot(a,b) = (x1 * x2) + (y1 * y2) + (z1 * z3)
If this happens to be equal to 0, then the vectors a and b are
perpendicular (the light meets the surface at a 90 degree angle).
We use the call max(), to make sure we do not get a negative value.
So our diffuse_value equation becomes:

diffuse_value = max(dot(vertex_normal, vertex_light_position), 0.0);

And from that, we have now calculated the intensity of the light
at the given vertex. That started to turn into a bit of a maths
lesson, but it even cleared up some stuff for me without even
thinking about it. GO MATHS :)

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

  varying float diffuse_value;

void main() {            
    // Calculate the normal value for this vertex, in world coordinates (
multiply by gl_NormalMatrix)

    vec3 vertex_normal = normalize(gl_NormalMatrix * gl_Normal);
    // Calculate the light position for this vertex
    vec3 vertex_light_position = gl_LightSource[0].position.xyz;
    // Set the diffuse value (darkness). This is done with a dot product between
 the normal and the light

    // and the maths behind it is explained in the maths section of the site.
    diffuse_value = max(dot(vertex_normal, vertex_light_position), 0.0);

    // Set the front color to the color passed through with glColor*f
    gl_FrontColor = gl_Color;
    // Set the position of the current vertex 
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

 

Fragment Shader Source:

Because we are using the varying float Diffuse in
our vertex shader, if we wish to read it in here, we
need to set the same variable, so I have done this
first:

varying float diffuse_value;

After that, because I am building on the last tutorial, we will multiply
our colour value, by our new diffuse_value, so our final call to
gl_FragColor will look like:

gl_FragColor = gl_Color * diffuse_value;

Once you get this running, it should show a sphere that gets darker down the bottom, or if you add a camera, it will be dark behind as well. Which you can see in the image at the top of the page.

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

1.
2.
3.
4.
5.
6.
7.
  varying float diffuse_value;

void main() {
    // Set the output color of our current pixel
    gl_FragColor = gl_Color * diffuse_value;
}

 

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.0/10 (3 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

3. GLSL Coloring

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

Tags: , , , ,

0

Jump To:

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

From now on, I won’t be listing the source code for the shader.h and shader.cpp files, as these will not be changing. We have everything we need in place to play with our shaders.

What we are trying to do in this tutorial is simulate our fixed function pipeline in GLSL. In standard OpenGL, we are used to coloring objects with their respective glColor3f or glColor4f methods.

This tutorial will allow us to use the values assigned as the color, inside our shader.

Main.CPP File:

Now this code, isn’t going to change at all, but the key line to focus on is:

    glColor4f(1.0, 0.0, 0.0, 1.0);

Changing this line will change the output colour of the fragment shader.

And I have also changed from using a wire cube, just to show the color of the shape more clearly.

    glutSolidCube(2);

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.

  #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;

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

void init(void) {
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    
    shader.init(“shader.vert”, “shader.frag”);
}

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
    glColor4f(1.0, 0.0, 0.0, 1.0);
    glutSolidCube(2);
}

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.1f;
}

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);
    //set up
the double buffering

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH;

    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:

Our change here, is going to be the introduction of a new line. This line will look like:

    gl_FrontColor = gl_Color;     

And what this means, is that we want the set the front face color for our objects, to that of the color entered in by the glColor3f and glColor4f functions.

If we have a shape with some back facing shapes, then we can also set gl_BackColor to the same value, and this will give us the same color for both front and back facing shapes. Or you could even change this value, so that the front of a shape has one color, and the back of a shape has another color.

1.
2.
3.
4.
5.
  void main() {     
    // Set the front color to the color passed through with glColor*f 
    gl_FrontColor = gl_Color;        
    // Set the position of the current vertex 
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

 

Fragment Shader Source:

The only difference here, is the replacement of our vec4(1.0) color value, with the value gl_Color.

*Note that the gl_Color read in by the vertex shader, is different to that of the gl_Color value read in by the fragment shader. The one in the fragment shader, refers to the gl_Color value we get from the vertex shader, whilst the gl_Color value in our vertex shader is read in from our application.*

And that is all there is to it. It is often handy to set the colours, lighting values, etc in regular OpenGL mode, so that if our shader fails on a certain system, we will still get a similar result. It just won’t look as pretty.

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

1.
2.
3.
4.
  void main() {
    // Set the output color of our current pixel
    gl_FragColor = gl_Color;
}

 

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: 7.0/10 (2 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

2. GLSL Validation

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

Tags: , , , ,

0

Jump To:

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

When developing with C++, it is often handy when the compiler tells you where a problem is. With GLSL validation, we can find out where exactly we have an error within our shader program.

This tutorial is going to show you how to validate a GLSL program. If your shaders fails validation, the output will be written to the console, and will hopefully help you find the problem.

Let get to it..

Shader.H File:

This tutorial is going to start off nice and simple, no changes will be made to this file :)

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.

  

#ifndef __SHADER_H
#define __SHADER_H

#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 <string>

class Shader {
public:
    Shader();
    Shader(const char *vsFile, const char *fsFile);
    ~Shader();
    
    void init(const char *vsFile, const char *fsFile);
    
    void bind();
    void unbind();
    
    unsigned int id();
    
private:
    unsigned int shader_id;
    unsigned int shader_vp;
    unsigned int shader_fp;
};

#endif

Shader.CPP File:

Shader.cpp is where we are going to make *all* of our changes. To get validation working, we are going to add two new methods. One for validating the seperate vertex and fragment shaders, and one for validating the final shader program.

We are going to call these methods validateShader and validateProgram. validateShader will take two parameters, the first being the shader we want the check, and the second being the filename of the file associated with the shader.

static void validateShader(GLuint shader, const char* file = 0)

We are going to need three variables for this. The first is just the size of the output buffer that we want. This can be any value, but 512 sounds nice.

    const unsigned int BUFFER_SIZE = 512;

The second variable will be an array of char’s and will be used to contain the output from our validation. We then make a call to memset to set the char array to all 0′s.

    char buffer[BUFFER_SIZE];
    memset(buffer, 0, BUFFER_SIZE);

The final variable will be called length, and guess what. It will contain the length of the buffer we get back from OpenGL.

    GLsizei length = 0;    

All we have to do now, is read back the shader log from opengl and store it in our buffer.

    glGetShaderInfoLog(shader, BUFFER_SIZE, &length, buffer);

Finally, we just output the information if the length of the returned string is greater than 0. Meaning we have something to output.

    if (length > 0) {
        cerr << “Shader “ << shader << “ (<< (file?file:“”) << “) compile error: “ << buffer << endl;

The next method, validateProgram will take one parameter, and this will be an integer associated with the shader we are validating.

static void validateProgram(GLuint program)

The first few lines of the method are practically identical to that of the previous. We just want to change the output from the shader log:

    const unsigned int BUFFER_SIZE = 512;
    char buffer[BUFFER_SIZE];
    memset(buffer, 0, BUFFER_SIZE);
    GLsizei length = 0;
    
    glGetShaderInfoLog(shader, BUFFER_SIZE, &length, buffer);
    if (length > 0) {

Here, we now want to state that we have a problem linking the shaders, and output the error.

        cerr << “Program “ << program << “ link error: “ << buffer << endl;
    

Now for the new parts, we want to make a call to validate the program, and then using an integer, read back the status of the shader.

    glValidateProgram(program);
    GLint status;
    glGetProgramiv(program, GL_VALIDATE_STATUS, &status);

Once we have read back the status, we check to make sure the status is GL_TRUE, if it is GL_FALSE, then we have an error, and we output which GLSL shader program had the error.

    if (status == GL_FALSE)
        cerr << “Error validating shader “ << program << endl;

 

Then we just have to call the methods we have just created. These are called from within the init() method. I am going to call the validation methods after the respective compile calls for the shaders like so:

    glCompileShader(shader_vp);
    validateShader(shader_vp, vsFile);
    glCompileShader(shader_fp);
    validateShader(shader_fp, fsFile);

And then at the end of the init method, make a call to validateProgram:

validateProgram(shader_id);

That is the end of this tutorial, the rest of the page simply contains the source code for this tutorial.

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

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.
  #include “shader.h”
#include <string.h>
#include <iostream>
#include <stdlib.h>

using namespace std;

static char* textFileRead(const char *fileName) {
    char* text;
    
    if (fileName != NULL) {
        FILE *file = fopen(fileName, “rt”);
        
        if (file != NULL) {
            fseek(file, 0, SEEK_END);
            int count = ftell(file);
            rewind(file);
            
            if (count > 0) {
                text = (char*)malloc(sizeof(char) * (count + 1));
                count = fread(text, sizeof(char), count, file);
                text[count] = ‘\0′;
            }
            fclose(file);
        }
    }
    return text;
}

static void validateShader(GLuint shader, const char* file = 0) {
    const unsigned int BUFFER_SIZE = 512;
    char buffer[BUFFER_SIZE];
    memset(buffer, 0, BUFFER_SIZE);
    GLsizei length = 0;
    
    glGetShaderInfoLog(shader, BUFFER_SIZE, &length, buffer);
    if (length > 0) {
        cerr << “Shader “ << shader << “ (<< (file?file:“”) << “) compile error: 
“ << buffer << endl;
    }
}

static void validateProgram(GLuint program) {
    const unsigned int BUFFER_SIZE = 512;
    char buffer[BUFFER_SIZE];
    memset(buffer, 0, BUFFER_SIZE);
    GLsizei length = 0;
    
    memset(buffer, 0, BUFFER_SIZE);
    glGetProgramInfoLog(program, BUFFER_SIZE, &length, buffer);
    if (length > 0)
        cerr << “Program “ << program << “ link error: “ << buffer << endl;
    
    glValidateProgram(program);
    GLint status;
    glGetProgramiv(program, GL_VALIDATE_STATUS, &status);
    if (status == GL_FALSE)
        cerr << “Error validating shader “ << program << endl;
}

Shader::Shader() {
    
}

Shader::Shader(const char *vsFile, const char *fsFile) {
    init(vsFile, fsFile);
}

void Shader::init(const char *vsFile, const char *fsFile) {
    shader_vp = glCreateShader(GL_VERTEX_SHADER);
    shader_fp = glCreateShader(GL_FRAGMENT_SHADER);
    
    const char* vsText = textFileRead(vsFile);
    const char* fsText = textFileRead(fsFile);    
    
    if (vsText == NULL || fsText == NULL) {
        cerr << “Either vertex shader or fragment shader file not found.“ << endl
;
        return;
    }
    
    glShaderSource(shader_vp, 1, &vsText, 0);
    glShaderSource(shader_fp, 1, &fsText, 0);
    
    glCompileShader(shader_vp);
    validateShader(shader_vp, vsFile);
    glCompileShader(shader_fp);
    validateShader(shader_fp, fsFile);
    
    shader_id = glCreateProgram();
    glAttachShader(shader_id, shader_fp);
    glAttachShader(shader_id, shader_vp);
    glLinkProgram(shader_id);
    validateProgram(shader_id);
}

Shader::~Shader() {
    glDetachShader(shader_id, shader_fp);
    glDetachShader(shader_id, shader_vp);
    
    glDeleteShader(shader_fp);
    glDeleteShader(shader_vp);
    glDeleteProgram(shader_id);
}

unsigned int Shader::id() {
    return shader_id;
}

void Shader::bind() {
    glUseProgram(shader_id);
}

void Shader::unbind() {
    glUseProgram(0);
}

Main.CPP File:

No need to do anything here :D

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.

  #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;

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

void init(void) {
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    
    shader.init(“shader.vert”, “shader.frag”);
}

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
    glColor4f(1.0, 0.0, 0.0, 1.0);
    glutWireCube(2);
}

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.1f;
}

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);
    //set up
the double buffering

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH;

    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:

Woo, no changes here :)

1.
2.
3.
4.
5.
  void main() {            
    // Set the position of the current vertex 
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

 

Fragment Shader Source:

For this tutorial, we are not going to make any changes here either :)

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

1.
2.
3.
4.
  void main() {
    // Set the output color of our current pixel
    gl_FragColor = vec4(1.0);
}

 

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: 0.0/10 (0 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)

1. GLSL Setup

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

Tags: , , , ,

28

GLSL is the OpenGL Shader Language, and is the OpenGL version of HLSL for DirectX. It allows us to manipulate the scene on a per vertex and per fragment basis, giving us total control.

Jump To:

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

To start off the OpenGL Shader Language tutorials, I am going to take our basic
Smooth Rotation OpenGL tutorial and add to it. Now for those of you that didn’t
read about GLSL on the tutorials page, up the top, it will just give you a brief outline on what shaders can do as well as how it compares to DirectX, and I recommend taking a look.

If you don’t know anything about GLSL already, a shader program is made up of either a Vertex Shader and a Fragment Shader, or a Vertex Shader, Geometry Shader and Fragment Shader, which is supported on graphics cards from the Nvidia 8xxx cards onwards.

Here is a quick rundown on the different types of shader programs:

A Vertex Shader allows us to manipulate vertices, and gives us access to vertex normals, our lighting pipeline, object materials, and other functions.

A Fragment Shader allows us to manipulate the outputting pixels that we see on our screen, here we can create our effects such as bloom, lighting, texturing, fog, etc.

A Geometry Shader (which I will introduce in later tutorials), allows us to actually create and remove vertices as we see fit. This is great for dynamic level of detail, allows us to create ‘fuzzy’ polygons, explode shapes, and do a bunch of nifty effects.

 

I’d like to point out now, that this tutorial is aimed for OpenGL 2.0 and up, where GLSL is made a part of OpenGL. All prior versions will need to use ARB extensions.

Shader.H File:

Being our first GLSL tutorial, this might be a little long as there is a bit to setting up your first application. It’s not really complicated, and the final code is quite short, but because there is such a range of things to do, this page is going to be a bit long.

Having used Java quite a bit recently thanks to my University, I am growing a fondness for Object Oriented programming. This tutorial is going to take an Object Oriented style approach at GLSL shaders, and we will end up with a class that we can reuse each time we want to create a shader program.

To start off with our class declaration, open up a new C++ header file called "shader.h". In this we want to start off with a new class, so type something along the lines of:

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.
  #ifndef __SHADER_H
#define __SHADER_H

#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 <string>

class Shader {
public:
    Shader();
    ~Shader();

private:

};
#endif

This will be a nice clean skeleton on which we are going to add everything. You may notice the extra long code for our header files which includes an if statement. This code is explained on the new OpenGL Tips page, in short, it means you can compile on Mac as well.

Now, we want to add a few methods to the public: section of our class. We will give whoever uses this class the ability to create a shader using the constructor, or by using an init method. Both of which will take the file names for the vertex and fragment shader.

So we need to add the lines:

Shader(const char *vsFile, const char *fsFile);
void init(const char *vsFile, const char *fsFile);

Ok, so we now know how to create our shaders. But what about using them? Well all we need are three methods, bind and unbind to enable and disable our shader, and id to get the number associated with our shader, so that we can pass variable through to the shader:

void bind();
void unbind();
unsigned int id();

Nice, now all that is left is to do is to create three private variables. One for our shader program, one for our fragment shader and a final one for our vertex shader. I will be calling them:

unsigned int shader_id;
unsigned int shader_vp;
unsigned int shader_fp;

When we put all of this into shader.h, we get the following file, which is quite simple and does all that we need.

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.

  

#ifndef __SHADER_H
#define __SHADER_H

#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 <string>

class Shader {
public:
    Shader();
    Shader(const char *vsFile, const char *fsFile);
    ~Shader();
    
    void init(const char *vsFile, const char *fsFile);
    
    void bind();
    void unbind();
    
    unsigned int id();
    
private:
    unsigned int shader_id;
    unsigned int shader_vp;
    unsigned int shader_fp;
};

#endif

Shader.CPP File:

Now lets move onto our shader.cpp file. This is where we are going to fill all of our methods outlined above, and introduce a new method.

So lets start from the beginning, with our constructors:

Shader::Shader() {  
}

Shader::Shader(const char *vsFile, const char *fsFile) {
    init(vsFile, fsFile);
}

Phew, now that that’s out of the way.. Lets move onto our init method.

The first two lines we are going to add, are going to tell OpenGL to create a vertex shader and a fragment shader, and assign the id’s of these to our shader_vp and shader_fp variables.

    shader_vp = glCreateShader(GL_VERTEX_SHADER);
    shader_fp = glCreateShader(GL_FRAGMENT_SHADER);
    
Now the following two lines, read in our vertex shader and fragment shader files. These are just standard text documents, with whatever extension you wish to add. The method textFileRead() can be any method that returns the contents of the shader files as a char*. This tutorial includes code for this, but I will not be explaining it, as I assume you have some knowledge of C++.

    const char* vsText = textFileRead(vsFile);
    const char* fsText = textFileRead(fsFile);    
    
Then I just do a little check to see if we were able to load both the files, if we have a problem reading them, we will just skip the method, and OpenGL should go on with everything like normal, just our shader won’t function.

    if (vsText == NULL || fsText == NULL) {
        cerr << “Either vertex shader or fragment shader file not found.“ << endl;
        return;
    }

The next two lines pass our shader file contents to OpenGL to attach it to our shaders.
    
    glShaderSource(shader_vp, 1, &vsText, 0);
    glShaderSource(shader_fp, 1, &fsText, 0);

This next two lines, tells OpenGL to compile our shaders, which we have already bound the source code to.
    
    glCompileShader(shader_vp);
    glCompileShader(shader_fp);
    
And finally, we create our shader_id as a shader program. And then attach the vertex shader and the fragment shader to the shader program, and finally, link the program.

    shader_id = glCreateProgram();
    glAttachShader(shader_id, shader_fp);
    glAttachShader(shader_id, shader_vp);
    glLinkProgram(shader_id);

So now we can read in a shader file and compile it as a shader program. So lets take a look at the methods which allow us to use this:

This first method, id(), will return the shader_id program. This is used for when we bind our shader, or for when we want to pass variables through to our shader.
unsigned int Shader::id() {
    return shader_id;
}

This second method, bind(), will simply attach our shader, and anything drawn afterwards will use this shader, either until this shader is unbound or another shader is enabled.
void Shader::bind() {
    glUseProgram(shader_id);
}

The third of our usage methods, is unbind(), this simply binds the shader 0, which is reserved for OpenGL, and will disable our current shader.
void Shader::unbind() {
    glUseProgram(0);
}

So whats left now? We can create our shader, and we can use our shader. So why don’t we clean up our shader when we are done?

I will be doing this from within the destructor method for our shader, and this should only be called once at the end of our application, otherwise we will have to recreate our shader, which is an expensive process.

Our desctructor is made up of only 5 method calls to OpenGL. The first two, just detach our vertex shader and fragment shader from our shader project, and the final 3 just delete all of the shaders and then deletes the shader program.

    glDetachShader(shader_id, shader_fp);
    glDetachShader(shader_id, shader_vp);
    
    glDeleteShader(shader_fp);
    glDeleteShader(shader_vp);
    glDeleteProgram(shader_id);

And that’s all there is to creating a shader program. It’s not that tricky, and it all makes sense when sit back and think about what it is doing.

Now lets take a look at the main.cpp file below for this project.

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.
  #include “shader.h”
#include <string.h>
#include <iostream>
#include <stdlib.h>

using namespace std;

static char* textFileRead(const char *fileName) {
    char* text;
    
    if (fileName != NULL) {
        FILE *file = fopen(fileName, “rt”);
        
        if (file != NULL) {
            fseek(file, 0, SEEK_END);
            int count = ftell(file);
            rewind(file);
            
            if (count > 0) {
                text = (char*)malloc(sizeof(char) * (count + 1));
                count = fread(text, sizeof(char), count, file);
                text[count] = ‘\0′;
            }
            fclose(file);
        }
    }
    return text;
}

Shader::Shader() {
    
}

Shader::Shader(const char *vsFile, const char *fsFile) {
    init(vsFile, fsFile);
}

void Shader::init(const char *vsFile, const char *fsFile) {
    shader_vp = glCreateShader(GL_VERTEX_SHADER);
    shader_fp = glCreateShader(GL_FRAGMENT_SHADER);
    
    const char* vsText = textFileRead(vsFile);
    const char* fsText = textFileRead(fsFile);    
    
    if (vsText == NULL || fsText == NULL) {
        cerr << “Either vertex shader or fragment shader file not found.“ << endl;
        return;
    }
    
    glShaderSource(shader_vp, 1, &vsText, 0);
    glShaderSource(shader_fp, 1, &fsText, 0);
    
    glCompileShader(shader_vp);
    glCompileShader(shader_fp);
    
    shader_id = glCreateProgram();
    glAttachShader(shader_id, shader_fp);
    glAttachShader(shader_id, shader_vp);
    glLinkProgram(shader_id);
}

Shader::~Shader() {
    glDetachShader(shader_id, shader_fp);
    glDetachShader(shader_id, shader_vp);
    
    glDeleteShader(shader_fp);
    glDeleteShader(shader_vp);
    glDeleteProgram(shader_id);
}

unsigned int Shader::id() {
    return shader_id;
}

void Shader::bind() {
    glUseProgram(shader_id);
}

void Shader::unbind() {
    glUseProgram(0);
}

Main.CPP File:

As most of the main.cpp file is based on the double buffered window tutorial, I am only going to explain the lines that are related to GLSL and implementing our new class. The first thing we are going to do here, is include our header file:

#include “shader.h”

Now that we have access to our shader class, we need to declare a Shader object. I am going to call it shader for lack of a better word in this tutorial:

Shader shader;

Now before we can do anything with our shaders, we need to call glewInit(), this sets up GLEW so that we can use the extensions required to use shaders. This code will go below our glutCreateWindow call in our main method:

glewInit();

Simple enough so far. Now we want to initialize our shader. Inside our void init(void) method, add the following line:

shader.init(“shader.vert”, “shader.frag”);

For this tutorial, our shader programs are going to be called shader.vert and shader.frag and will be located in the same directory as the the compiled application.

All that is left now is to use the shader. In our display method, we want the shader to be applied to our cube, so before we draw the cube we want to bind our shader, and afterwards we want to unbind it:

shader.bind();
cube();
shader.unbind();

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.

  #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;

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

void init(void) {
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    
    shader.init(“shader.vert”, “shader.frag”);
}

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
    glColor4f(1.0, 0.0, 0.0, 1.0);
    glutWireCube(2);
}

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.1f;
}

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);
    //set up
the double buffering

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH;

    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:

This is pretty much the most basic of vertex shaders. Vertex shaders
allow for us to manipulate the vertices of an object. The major
advantage of this would be if you were moving vertices or a mass
group of vertices, in a particular way. For example, water could
be created with a plane tesselated enough, and the vertex shader
would be faster at computing the positions of the vertices than
if you were doing it in immediate mode.

Anyway, this shader, just like all other GLSL shaders, has a main
function, it is possible to have other functions inside a shader,
but OpenGL will look for the main function and call it.

Inside our main function, I am setting the position of the vertices
to exactly where they would be if I hadn’t used this shader. We are
multiplying the projection matrix, by the current vertex to place
the vertex where it should belong.

The variable gl_Position, is used to set the position of the current
vertex. Another method of setting the vertex to where it should be
without shaders, to set gl_Position to ftransform; ftransform is a
GLSL variable that does the exact same as
glModelViewProjectionMatrix * glVertex;

I don’t use ftransform() personally, and I believe it is even deprecated
when using geometry shaders. (Just don’t quote me on that)

So all this shader will do is place the vertices we are drawing to where
they should have been in the first place.

*Note* All vertex shaders are called for every vertex individually.

Every vertex shader, needs to have the line
gl_Position = *something*;
because the point of the vertex shader is to set the positions of
the vertices.

1.
2.
3.
4.
5.
  void main() {            
    // Set the position of the current vertex 
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

 

Fragment Shader Source:

The fragment shader needs a main function, just as the

vertex shader does. But unlike a vertex shader, this is
called for every pixel on the screen.

All fragment shaders must end with the line
gl_FragColor = vec4(*something*);
because the fragment shader is setting the current color
drawn on the screen.

In the fragment shader, gl_FragColor will end up being
the color that we want to display on the screen. In
this case we are setting that to the color white, so every pixel
belonging to our shape will be white.

GLSL has a few different types, I recommend taking a look at the
GLSL quick reference guide to get an idea of all of them.
gl_FragColor takes the value of vec4() which is a 4 float
vector. This can be written as vec4(red, green, blue, alpha)
or you can even pass through other vec4′s in the constructor,
and even shorthand to vec4(1.0) to make all values 1.0. This will
give us our white color.

And that is all there is to shaders (for now).
If you have followed through this far, well done. Give
yourself a pat on the back :)

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

Revision: 1.1
Added in use of glewInit() for Windows machines.

1.
2.
3.
4.
  void main() {
    // Set the output color of our current pixel
    gl_FragColor = vec4(1.0);
}

 

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.0/10 (6 votes cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)
Improve the web with Nofollow Reciprocity.