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