Loading, Compiling, Linking, and Using GLSL Programs
A Simple Vertex Shader
This vertex shader scales all vertices in x and y direction. This doesn't really make much sense, but it is a good example to get started with. All vertices of our primitive (or object or scene) will go through this program. gl_Vertex is the current vertex which is being processed by this program. The vertex has the same (untransformed) values like you specify in glVertex3f(x,y,z) in your C++ program. The gl_ModelViewProjectionMatrix is the concatenated modelview and projection matrix. I assume you know what the modelview and projection matrices are, otherwise you can look it up in the OpenGL SDK.
A Simple Fragment Shader
A fragment is basically a pixel before it is rasterized. If you render a fully visible triangle then all pixels between the three vertices must be drawn. Before those pixels are drawn they go through the fragment processor and for each possible pixel the fragment program is exectuted.
In this simple example every fragment is set to green. gl_FragColor holds the output color.
Now we have two simple shaders, a vertex and a fragment shader. If the shaders are located in text files they must be loaded into memory first. This part has nothing to do with OpenGL, it is a simple ASCII file loader. If you write your shaders in Unicode (for the comments), you have to write your own loader. The actual program in memory should be in ASCII. You could also embed your shaders into your C++ code using a static char array. In other words: it doesn't matter how you get your shaders into memory. I recommend using ASCII files. This way you can change your shader code without recompiling your application. The source here simply loads an ASCII Shader File:
First you have to create an OpenGL "Shader Object" specifying what kind of shader it is (e.g. Vertex Shader, Geometry Shader, or Fragment Shader) . A shader object can be created using the OpenGL function glCreateShader with the arguments GL_VERTEX_SHADER or GL_FRAGMENT_SHADER (or GL_GEOMETRY_SHADER_EXT).
Now it is time to compile! This can be done using the OpenGL function glCompileShader:
Now the shaders are probably compiled. But what if you made some typo when entering the shader source code ? To check if the compile was successful or not you can use glGetObjectParameteriv with "GL_COMPILE_STATUS" argument.
If compilation failed, the exact cause can be checked using glGetShaderInfoLog. This usually returns a log message with the error description. The contents of the returned log is depends on the implementation/driver.
A vertex shader and fragment shader (and geometry shader) must be put together to a unit before it is possible to link. This unit is called "Program Object". The Program Object is created using glCreateProgram.
The OpenGL function glAttachShader can attach a Shader Objects to a Program Object.
(When using geometry shaders, you have to specify input primitive Type, output primitive type and maximal number of vertices before linking. But for now forget about geometry shaders.)
To check if linking was successful, glGetObjectParameteriv can be used again, similar to compiling.
Once you have a linked Program Object, it is very easy to use that shader with the OpenGL function glUseProgram with the program object as argument. To stop using the program you can call glUseProgram(0).
As you see it is pretty easy to load, compile, link and use shaders. On the other side it is pretty complicated if you use many different shaders in your code. I created some C++ classes which simplify the whole process.
the class cwc::glShaderManager loads, compiles and links a GLSL program (from file or memory). It returns a cwc::glShader, which holds/represents the "Program Object". There are many other useful things in that class. It is the "libglsl" I created a while back.
In future tutorials, this way is used to load/compile/link shaders to reduce code overhead.
GLSL_Loading.zip (Visual Studio 8 Project)