// 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. //------------------------------------------------------------------------------- // 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 #include #include #include #include #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); glColor3f(1,0,0); 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; }