Main.CPP File: There are going to be quite a few additions to this code, but you might recognise most of it from the lighting tutorials. I am creating a directional light, that points in the direction [0,1,1]. It has a diffuse color of white, and an ambient color of black. I have placed the calls to setup my lighting inside of a new method, which is called during our display method, *after* we set the camera position. I have a tip on the OpenGL Tips page, that explains why we do this. Another minor change, is that I am now using a glutSolidSphere, I have kept it to a low polygon count, only 10 stacks and 10 slices. This is to show how per vertex will differ to per pixel lighting later on. But enough of that, lets move onto our shaders. You should already know how to setup lighting in a standard OpenGL application. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. | | | #if ( (defined(__MACH__)) && (defined(__APPLE__)) ) #include <stdlib.h> #include <OpenGL/gl.h> #include <GLUT/glut.h> #include <OpenGL/glext.h> #else #include <stdlib.h> #include <GL/glew.h> #include <GL/gl.h> #include <GL/glut.h> #include <GL/glext.h> #endif#include “shader.h” Shader shader; GLfloat angle = 0.0; //set the angle of rotation //diffuse light color variables GLfloat dlr = 1.0; GLfloat dlg = 1.0; GLfloat dlb = 1.0; //ambient light color variables GLfloat alr = 0.0; GLfloat alg = 0.0; GLfloat alb = 0.0; //light position variables GLfloat lx = 0.0; GLfloat ly = 1.0; GLfloat lz = 1.0; GLfloat lw = 0.0; void init(void) { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); shader.init(“shader.vert”, “shader.frag”); } void cube (void) { glRotatef(angle, 1.0, 0.0, 0.0); //rotate on the x axis glRotatef(angle, 0.0, 1.0, 0.0); //rotate on the y axis glRotatef(angle, 0.0, 0.0, 1.0); //rotate on the z axis glColor4f(1.0, 0.0, 0.0, 1.0); glutSolidSphere(2, 10, 10); } void setLighting(void) { GLfloat DiffuseLight[] = {dlr, dlg, dlb}; //set DiffuseLight[] to the specified values GLfloat AmbientLight[] = {alr, alg, alb}; //set AmbientLight[] to the specified values
glLightfv (GL_LIGHT0, GL_DIFFUSE, DiffuseLight); //change the light accordingly glLightfv (GL_LIGHT0, GL_AMBIENT, AmbientLight); //change the light accordingly GLfloat LightPosition[] = {lx, ly, lz, lw}; //set the LightPosition to the specified values glLightfv (GL_LIGHT0, GL_POSITION, LightPosition); //change the light accordingly }void display (void) { glClearColor (0.0,0.0,0.0,1.0); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); setLighting(); shader.bind(); cube(); shader.unbind(); glutSwapBuffers(); angle += 0.01f; } void reshape (int w, int h) { glViewport (0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode (GL_PROJECTION); glLoadIdentity (); gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 100.0); glMatrixMode (GL_MODELVIEW); } int main (int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); //set up the double buffering glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(“A basic OpenGL Window“); glutDisplayFunc(display); glutIdleFunc(display); glutReshapeFunc(reshape); init(); glutMainLoop(); return 0; } |
|
Vertex Shader Source: For this tutorial, I am going show you how to perform per vertex lighting using shaders. Yes, shaders support per pixel but this is just a starting point. First I am going to teach you about varying variables. These are variables that can be shared between the vertex and fragment shaders. These variables are set inside the vertex shader, and then read from the fragment shader. In this case we are going to share a float type variable from the Vertex shader to the Fragment shader. This variable is going to be called diffuse_value and will hold a number that will tell us how lit our vertex should be. Because we are using per vertex lighting, the GLSL shader will automatically interpolate the color between the vertices. Just like OpenGL does. Now first off we need to know the surface normal of the current vertex. Because I am using a GLUT sphere, these are already calculated and sent off with the glNormal3f command. Any numbers sent from OpenGL through glNormal*; can be read with the variable gl_Normal. In later tutorials, I will be using self calculated normals, using my own normal generation code. Now as for our normal, I am storing it in the variable vec3 vertex_normal. But first, I have to multiply the normal (gl_Normal) by the gl_NormalMatrix. This places the normal into world coordinates so that we can use it correctly. (Later on I will show you how to use tangent space normals). We also then have to normalize this multiplication so that all of the normal vectors are between (and including) -1 and 1. Our final call looks like: vec3 vertex_normal = normalize(gl_NormalMatrix * gl_Normal); Next on, we are going to work with our light. I am storing this in a variable called vec3 vertex_light_position. In this, I am calling for the position of glLight0. This is gathered by the call: vec3 vertex_light_position = gl_LightSource[0].position.xyz; This gives us the position of glLight0, and to get the position of any other light, we just change the number 0 to the light number we are after. We do not need to multiply this expression by the gl_NormalMatrix, it is already in world coordinates, as it has been multiplied by the ModelView Matrix before we set it’s position, but we do have to normalize it still. Now that we have our vertex_normal and the position of our vertex_light_position. We can now calculate the diffuse_value of our lighting at the given vertex. To do this we need to take the dot product of both the vertex_normal and the vertex_light_position together. If you have any idea on calculating normals, then you should have a fair idea of what the dot product does. It works as such: dot(a,b) = (x1 * x2) + (y1 * y2) + (z1 * z3) If this happens to be equal to 0, then the vectors a and b are perpendicular (the light meets the surface at a 90 degree angle). We use the call max(), to make sure we do not get a negative value. So our diffuse_value equation becomes: diffuse_value = max(dot(vertex_normal, vertex_light_position), 0.0); And from that, we have now calculated the intensity of the light at the given vertex. That started to turn into a bit of a maths lesson, but it even cleared up some stuff for me without even thinking about it. GO MATHS  1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. | | | varying float diffuse_value; void main() { // Calculate the normal value for this vertex, in world coordinates ( multiply by gl_NormalMatrix) vec3 vertex_normal = normalize(gl_NormalMatrix * gl_Normal); // Calculate the light position for this vertex vec3 vertex_light_position = gl_LightSource[0].position.xyz; // Set the diffuse value (darkness). This is done with a dot product between the normal and the light // and the maths behind it is explained in the maths section of the site. diffuse_value = max(dot(vertex_normal, vertex_light_position), 0.0); // Set the front color to the color passed through with glColor*f gl_FrontColor = gl_Color; // Set the position of the current vertex gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; } |
|
Fragment Shader Source: Because we are using the varying float Diffuse in our vertex shader, if we wish to read it in here, we need to set the same variable, so I have done this first: varying float diffuse_value; After that, because I am building on the last tutorial, we will multiply our colour value, by our new diffuse_value, so our final call to gl_FragColor will look like: gl_FragColor = gl_Color * diffuse_value; Once you get this running, it should show a sphere that gets darker down the bottom, or if you add a camera, it will be dark behind as well. Which you can see in the image at the top of the page. If you have any questions, please email me at swiftless@gmail.com 1. 2. 3. 4. 5. 6. 7. | | | varying float diffuse_value; void main() { // Set the output color of our current pixel gl_FragColor = gl_Color * diffuse_value; } |
|