6. GLSL Materials
Jump To:
main.cpp Source
shader.vert Source
shader.frag Source
Download
Materials have already been explain for use in standard OpenGL in the OpenGL tutorials section fo the website, this tutorial will focus on how to use them inside of a GLSL shader program.
Main.CPP File: All the code that has been added to this file, has come from the lighting tutorials fromt he OpenGL section of the site, with some minor modifications. I have also removed the line glColor4f(1.0, 0.0, 0.0, 1.0); so that you don’t get confused with the material and color values. Feel free to jump to the shaders below.
|
||||
Vertex Shader Source: Our Vertex Shader for this tutorial, is going to only have one variable added to it. This variable will be called: varying vec3 vertex_light_half_vector; And it will store the half-vector for our current light. To get the half-vector from OpenGL, we can use the line: vertex_light_half_vector = normalize(gl_LightSource[0].halfVector.xyz); The half vector will be used to calculate the specular term for our lighting.
|
||||
Fragment Shader Source: The Fragment Shader is where all of our fancy lighting magic is going to take place. First off, we need to include our varying variable: varying vec3 vertex_light_half_vector; Now for the magic. We are going to use 3 vec4 variables, called: These are going to hold the final color values for our lighting. To compute the ambient term, we nueed to multiply the front material for our object, by the ambient color for our light. And then add this to the global ambient value multiplied by our front materials ambient value. The final equation looks like: vec4 ambient_color = gl_FrontMaterial.ambient * gl_LightSource[0].ambient + gl_LightModel.ambient * gl_FrontMaterial.ambient; For the diffuse colour value, we need to multiply our front material by the diffuse value for our light source: vec4 diffuse_color = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse; All that we have left to work out now, is our specular value. This is slightly trickier, and is where our half_vector comes into play. We start off the same as the other color values, and multiply our front material specular value, by the specular value of the light source. We then multiply this by the vertex_light_half_vector dotted with the vertex_normal, all to the power of gl_FrontMaterial.shininess. This gives us the intensity for our specular term. Our final equation looks like: vec4 specular_color = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(max(dot(vertex_normal, vertex_light_half_vector), 0.0) , gl_FrontMaterial.shininess); Now all that is left, is to put all this together in our gl_FragColor value. To do this, you need to have some idea about how lighting in OpenGL works. First we need the ambient term, this is going to be there no matter what, and isn’t affected by shadow. Then we need to add our diffuse colour, but we need to multiply this by our diffuse_value to get a simulated shadow effect. Finally, we want to add our specular value to the surface of our object. Giving us the final line: gl_FragColor = ambient_color + diffuse_color * diffuse_value + specular_color; And now you should be all set to go ahead with lighting and materials in OpenGL and GLSL. The next step would be to add multiple light support. And depending on your graphics card, you either need to hard code this for each light, or create a loop to calculate this for ever light. I have found that on the 9600 Nvidia graphics card on my Macbook Pro, I cannot use more than 4 lights, because I run out of varying variables available on my shader. But my GTX260 on my Desktop PC can use all 8 of OpenGL’s lights. If you have any questions, please email me at swiftless@gmail.com
|
||||
Download: Download shader.h Source Code for this Tutorial Download shader.cpp Source Code for this Tutorial Download main.cpp Source Code for this Tutorial |
Thanks for the helpful tutorials. My question is: how to detect lights count to calculate illumination in loop
for (int i = 0; i < numLights; ++i) { // where to get numLights?
…
}
Thanks
Igors
The easiest way is using a uniform and setting it in the application code. Normally you want to have fewer lights enabled on lower performance machines so you can keep frame rate.
This is very helpful, thank you.
I have heard from Lighthouse3D that the normal may be normalized twice: once in the vertex shader and again in the fragment shader. I know I have proven to be somewhat of a newbie (as I am), but when I substitute vertex_normal for vec3 fragment_normal = normalize(vertex_normal); I see a less vertex-ish lighting. Let me know what you think.
Hi Mitch,
You are totally correct with what you have read, and I should update the tutorial to comply, thats a mistake on my part.
It is required to normalize your variables in your fragment shader instead of your vertex shader, and this is explained on Lighthouse3D in detail as to why.
Cheers,
Swiftless
Love your new website, congratulations!