Setting Up

Back to GLSL Tutorial Index

Discuss this Tutorial in the Forum

Jump To:

C++ Source
Vertex Shader Source
Fragment Shader Source
Download

To start of 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, here is the exact same
thing copied and pasted :-P

GLSL is the OpenGL Shader Language, and does for OpenGL what HLSL does for DirectX.
GLSL allows for you to keep control of the screen on a per texel basis allowing for
per pixel lighting effects, volumetric fog, high dynamic range rendering,
reflections, refractions, blending, blurring, you name it, you can do it. Now GLSL
does not just cover the graphical side, it also covers the object side. You can
manipulate vertices on a per vertex basis using shaders. Along with GLSL, OpenGL
can also use CG which is Nvidia's shading language. Personally I prefer GLSL
because it uses similar syntax to OpenGL and hence making it easier to learn.

CPP File:

//-------------------------------------------------------------------------------
Section 1: Headers, Variables and Reading the Shader Files

First off, we want to be able to read in our Shaders source code files. These
are called the Vertex Shader and Fragment Shader (Geometry Shaders have just
come out, but are not supported by most hardware). These Shader files hold
the source code to what our shader can and cannot do.

To start off, I have included some new header files, they are:
glew.h - This is the OpenGL Extensions Wrangler, which I find more efficient
than glext.
windows.h - so we can allocate memory as required
stdio.h - so we can read our files

After that we need to pragma comment the glew library. This is done with the line:
#pragma comment(lib,"glew32.lib")

With glew, you have to make sure that you call the command to initialize glew, this is
glewInit(); and is called before everything. So your 'main' function will look something
like:

glutCreateWindow ("A basic OpenGL Window");
// *Here it goes--->* glewInit();
// Init();
glutDisplayFunc (display);
// glutIdleFunc (display);

Now that we have all the files setup, we can start writing some code. Here we
start off with 5 initial variables, they consist of two character arrays called
VertexShaderSource and FragmentShaderSource which hold the source code of our
shaders temporarily as we load them in.
Next we have our actual VertexShader and FragmentShader which is generated from
our source code, by OpenGL.
And finally we have the Shader Program. Each shader program has it's own vertex
and fragment shader.

After that, we now need a function that will load in our shaders. I have called
this readShaderFile (how original), and it takes the expression char *FileName
which is the name of the file we are loading.

char *readShaderFile(char *FileName) {

DATA will hold the shader source and return it to our OpenGL setup calls
char *DATA = NULL;
fp will be the shader FILE we are going to open
FILE *fp;
flength will be the length of the file
int flength = 0;
Now we are going to open the shader file
fp = fopen(FileName,"rt");
now we are going to scan through the file until the end
fseek(fp, 0, SEEK_END);
we are then going to store the length of the file
flength = ftell(fp);
now we are skipping back to the start of the file
rewind(fp);
Here we are allocating memory so we can read in our shader file
DATA = (char *)malloc(sizeof(char) * (flength+1));
now we are reading the file into our DATA array
flength = fread(DATA, sizeof(char), flength, fp);
and we are setting the very last character in the array to '\0' which indicates
the end of the code.
DATA[flength] = '\0';
Now we close the file as we don't need it anymore.
fclose(fp);
And finally we return the DATA from inside the shader file for use.
return DATA;
}

//----------------------------------------------------------------------------
Section 2: Initializing the shader and DeInitializing the shader

This function has to be called before we call any other OpenGL calls. So in
our 'main' function, I am adjusting the code to look like:

// glutInitWindowPosition (100, 100);
glutCreateWindow ("A basic OpenGL Window");
// glewInit();

// *Here it is--->* InitShader();

// Init();
glutDisplayFunc (display);
// glutIdleFunc (display);
// glutReshapeFunc (reshape);

Keep in mind, that you have to initialize each individual shader.

Now onto our shader initialization. This is just a simple function
which takes no parameters and I am calling InitShader.
void InitShader (void) {
Here we are calling the vertex and fragment shader extensions from GLEW.
GLEW_ARB_vertex_shader;
GLEW_ARB_fragment_shader;

Now we have to set our VertexShader and FragmentVariables to actual shader
objects. All we have to do is make a couple of calls and OpenGL does the
rest here.
VertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
FragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
Now that we have our Vertex and Fragment Shaders, it is time to load
in some information so our shaders can perform some of their duties. Here
we are just calling our readShaderFile function we created earlier.
VertexShaderSource = readShaderFile("setup.vert");
FragmentShaderSource = readShaderFile("setup.frag");
Now we have to set some temporary variables that will hold our source code
that we just loaded. These will be called VS and FS respectively.
const char * VS = VertexShaderSource;
const char * FS = FragmentShaderSource;
Now we take the source code, and we bind it to each of our shaders.
glShaderSourceARB(VertexShader, 1, &VS,NULL);
glShaderSourceARB(FragmentShader, 1, &FS,NULL);
And because we no longer need our VertexShaderSource and FragmentShaderSource
we can free these variables.
free(VertexShaderSource);free(FragmentShaderSource);
Now that we have our Shaders bound to their source code, we can use OpenGL to
compile the shaders for us.
glCompileShaderARB(VertexShader);
glCompileShaderARB(FragmentShader);
Now comes the time to create our shader program. This is done simply enough.
ShaderProgram = glCreateProgramObjectARB();
And here we are going to attach both our VertexShader and our FragmentShader
to our newly created ShaderProgram. Remember, each shader program has it's
own vertex and fragment shaders.
glAttachObjectARB(ShaderProgram,VertexShader);
glAttachObjectARB(ShaderProgram,FragmentShader);
Finally we then link the program, and we are done. We now have a shader called
ShaderProgram, in which we can use.
glLinkProgramARB(ShaderProgram);
}

As for deinitializing the shader, it is simple enough.
void DeInitShader (void) {
We just detach our vertex and fragment shaders from our ShaderProgram.
glDetachObjectARB(ShaderProgram,VertexShader);
glDetachObjectARB(ShaderProgram,FragmentShader);
And then we just delete the ShaderProgram object.
glDeleteObjectARB(ShaderProgram);
}
And we just call this function at the end of our 'main' function.

We are now ready to go ahead and use our shader.

//--------------------------------------------------------------------------
Section 3: Using the shader
Using the shader is luckily, the easiest part (for now). In our 'display'
function I have added the line:

glUseProgramObjectARB(ShaderProgram);

Inbetween the rotation and drawing codes as shown below:

glRotatef(angle,1,1,1);
*HERE--->* glUseProgramObjectARB(ShaderProgram);
glutSolidCube(2);

//-------------------------------------------------------------------------
Section 4: The Vertex Shader (See shader file - setup.vert)
Section 5: The Fragment Shader (See shader file - setup.frag)

//-------------------------------------------------------------------------
Section 6:

That was the first part of my shader tutorials. If you have any questions
or comments, feel free to discuss this in the forums, and or email me at
swiftless@gmail.com

#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include <windows.h>
#include <stdio.h>

#pragma comment(lib,"glew32.lib")

char *VertexShaderSource,*FragmentShaderSource;

int VertexShader,FragmentShader;

int ShaderProgram;

GLfloat angle = 0.0;

char *readShaderFile(char *FileName) {
FILE *fp;
char *DATA = NULL;

int flength = 0;

fp = fopen(FileName,"rt");

fseek(fp, 0, SEEK_END);
flength = ftell(fp);
rewind(fp);

DATA = (char *)malloc(sizeof(char) * (flength+1));
flength = fread(DATA, sizeof(char), flength, fp);
DATA[flength] = '\0';

fclose(fp);

return DATA;
}

void display (void) {
glClearColor (0.0,0.0,0.0,1.0);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0,0,-5);
glRotatef(angle,1,1,1);
glUseProgramObjectARB(ShaderProgram);
glutSolidCube(2);
glutSwapBuffers();
angle++;
}

void InitShader (void) {
GLEW_ARB_vertex_shader;
GLEW_ARB_fragment_shader;

VertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
FragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);

VertexShaderSource = readShaderFile("setup.vert");
FragmentShaderSource = readShaderFile("setup.frag");

const char * VS = VertexShaderSource;
const char * FS = FragmentShaderSource;

glShaderSourceARB(VertexShader, 1, &VS,NULL);
glShaderSourceARB(FragmentShader, 1, &FS,NULL);

free(VertexShaderSource);free(FragmentShaderSource);

glCompileShaderARB(VertexShader);
glCompileShaderARB(FragmentShader);

ShaderProgram = glCreateProgramObjectARB();

glAttachObjectARB(ShaderProgram,VertexShader);
glAttachObjectARB(ShaderProgram,FragmentShader);

glLinkProgramARB(ShaderProgram);
}

void DeInitShader (void) {
glDetachObjectARB(ShaderProgram,VertexShader);
glDetachObjectARB(ShaderProgram,FragmentShader);

glDeleteObjectARB(ShaderProgram);
}

void Init (void) {
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
}

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

int main (int argc, char **argv) {
glutInit (&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize (500, 500);
glutInitWindowPosition (100, 100);
glutCreateWindow ("A basic OpenGL Window");
glewInit();
InitShader();
Init();
glutDisplayFunc (display);
glutIdleFunc (display);
glutReshapeFunc (reshape);
glutMainLoop ();
DeInitShader();
return 0;
}

Vertex Shader:

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
gl_ModelViewProjectionMatrix * glVertex;

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 end with the line
gl_Position = *something*;
because the point of the vertex shader is to set the positions of
the vertices.

void main()
{
// Set the position of the current vertex
gl_Position = gl_ModelViewProjectionMatrix * glVertex;
}

Fragment Shader:

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 = *something*;
because the fragment shader is setting the current color
drawn on the screen.

In this fragment shader, gl_FragColor must end up being
the color that we want to display on the screen. In
this case we are setting that to gl_Color. gl_Color
will take the current color set in OpenGL using
glColor3f(r,g,b) or any other glColor call and then
use this color. So the fragment shader will be outputting
the same colour that would be shown, even if we had not
called this shader.

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

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

Download:

Download C++ .CPP Source Code for this Tutorial

Download Vertex Shader Source Code for this Tutorial

Download Fragment Shader Source Code for this Tutorial

     

 

Copyright 2008, Donald Urquhart
Proudly supported by http://www.cdadc.com