GLSL shaders, an introduction

The use of shaders in 3D graphics is a very effective and flexible way to have stunning results. They give you the opportunity to define custom behavior for the light and its interaction with the surfaces. You can go beyond the standard physics models and create your own special effects. The good thing about shaders is the minimum processing overhead that comes with them. They are programs that run on the GPU and not on the main processor and, if you take into account the power they give you then I think they are worth it.

This article will not teach you how to manipulate light models and the physical properties of materials. This requires a great expertise in that field and I do not consider myself an expert. I will only give you a simple example how to load, compile and use shaders. My aim is to show you how things work from a programmer's point of view. I remember how hard it was for me to strip down the code samples and get the minimum required code to load and use a shader.

Fragment and Vertex shaders

Shaders usually go in pairs. First we have the 'vertex' shader. This is called first on a per vertex basis and gives you the chance to read and store geometry information to be used in light calculations, as well as modify the display geometry. Then the system calls the 'fragment' shader which is used to perform the lighting calculations for each fragment, aka polygon to be displayed.

The shaders are little programs written in GLSL, a simple language that resembles C. Using predefined keywords, or we should call them variables, we can access OpenGL processing pipeline information like the vertex coordinates, normal vectors, light, color, materials etc. This information is all we need to define how the objects will be rendered on the screen and even modify their placement.

Before we use the shaders we have to load and compile them. This involves reading them from disk. I would not recommend having them hardcoded in your code since it is bad for the flexibility of your game. After we load them we pass them to OpenGL for compilation and link. Then they are ready to be used. All we have to do is invoke the appropriate shader for each job and release it when we are done. This means that we can have different shaders for different objects in on scene! Imagine the flexibility and versatility this gives to your game.

Here is how it is done

GLchar *fsSource = ... the fragment shader source code
GLchar *vsSource = ... the vertex shader source code

// First ask OpenGL to create a vertex shader
vs = glCreateShader(GL_VERTEX_SHADER);
// then pass the shader code to OpenGL
glShaderSource(vs, 1, (const GLchar**)&vsSource, NULL);
// and request a compilation
// Check for possible errors
int errc;
if (errc == GL_FALSE) // if compilation failed
	// print the error on the debug screen
	printLog("vertex shader", vs);
	// perform clean up and abandon further processing

// now is the time for the fragment shader
// the sequence of actions is the same as before
fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, (const GLchar**)&fsSource, NULL);
if (errc == GL_FALSE)
	printLog("fragment shader", fs);
	// perform clean up and abandon further processing

// if everything went OK create a shader program
sp = glCreateProgram();
// attach the compiled shaders to it
glAttachShader(sp, vs);
glAttachShader(sp, fs);
// and link then to form a complete shader solution

Now that we have the shader loaded into the graphics card memory we have to use it to see the results

// invoke the shader
... do our drawing stuff here
// and put back for later use

It is a good practice to release the graphics card resources when our program exits

// delete the shaders
// and the shader program

You can find many shaders over the internet that can cover most of your programming needs. The sample program that comes with this article is modified version of the