4. GLSL Lighting
GLSL Lighting
Lighting is an essential part of creating realistic 3D scenes. With GLSL shaders, you can achieve more precise control over lighting, including custom effects. This tutorial introduces per-vertex lighting using GLSL, which mimics OpenGL’s fixed-function pipeline but with greater flexibility.
In per-vertex lighting, the lighting calculations are done at each vertex and interpolated across the surface of the geometry. This tutorial sets up a directional light source and calculates the diffuse lighting intensity at each vertex.
Main Program
Here is the main program (`main.cpp`) that sets up the OpenGL context, initializes the shaders, and renders a rotating sphere with per-vertex lighting.
#if (defined(__MACH__) && defined(__APPLE__)) #include <cstdlib> #include <OpenGL/gl.h> #include <GLUT/glut.h> #include <OpenGL/glext.h> #else #include <cstdlib> #include <GL/glew.h> #include <GL/gl.h> #include <GL/glut.h> #include <GL/glext.h> #endif #include "shader.h" // Shader instance Shader shader; // Rotation angle GLfloat angle = 0.0; // Diffuse light color variables GLfloat dlr = 1.0, dlg = 1.0, dlb = 1.0; // Ambient light color variables GLfloat alr = 0.0, alg = 0.0, alb = 0.0; // Light position variables GLfloat lx = 0.0, ly = 1.0, lz = 1.0, lw = 0.0; void init(void) { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); shader.init("shader.vert", "shader.frag"); } void sphere(void) { glRotatef(angle, 1.0, 0.0, 0.0); glRotatef(angle, 0.0, 1.0, 0.0); glRotatef(angle, 0.0, 0.0, 1.0); glutSolidSphere(2, 10, 10); } void setLighting(void) { GLfloat DiffuseLight[] = {dlr, dlg, dlb}; GLfloat AmbientLight[] = {alr, alg, alb}; GLfloat LightPosition[] = {lx, ly, lz, lw}; glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight); glLightfv(GL_LIGHT0, GL_AMBIENT, AmbientLight); glLightfv(GL_LIGHT0, GL_POSITION, LightPosition); } 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(); sphere(); shader.unbind(); glutSwapBuffers(); angle += 0.01f; } void reshape(int w, int h) { glViewport(0, 0, w, 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); glutInitWindowSize(500, 500); glutCreateWindow("GLSL Lighting Example"); glewInit(); init(); glutDisplayFunc(display); glutIdleFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0; }
Vertex Shader
The vertex shader calculates the lighting intensity at each vertex based on the diffuse lighting model. This intensity is passed to the fragment shader using a `varying` variable.
varying float diffuse_value; void main() { // Calculate the normal in world coordinates vec3 vertex_normal = normalize(gl_NormalMatrix * gl_Normal); // Get the light position vec3 vertex_light_position = gl_LightSource[0].position.xyz; // Calculate diffuse lighting using the dot product diffuse_value = max(dot(vertex_normal, vertex_light_position), 0.0); // Pass the vertex color to the fragment shader gl_FrontColor = gl_Color; // Transform the vertex position gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; }
Fragment Shader
The fragment shader uses the interpolated `diffuse_value` to calculate the final pixel color, creating the appearance of lighting across the surface.
varying float diffuse_value; void main() { // Multiply the vertex color by the diffuse lighting value gl_FragColor = gl_Color * diffuse_value; }
Understanding the Code
1. **Vertex Shader:**
– The dot product between the vertex normal and light direction calculates the intensity of light on the surface.
– `normalize` ensures vectors are unit length, critical for accurate lighting.
2. **Fragment Shader:**
– The interpolated `diffuse_value` creates smooth transitions between lit and shadowed areas.
3. **Lighting Setup:**
– `setLighting` initializes the diffuse and ambient light colors and sets the light position.
Output Behavior
When you run this program, you should see a sphere with a gradient lighting effect, brighter where the light hits directly and darker where it falls off. The lighting changes dynamically as the sphere rotates.
Download Links
If you have any questions, feel free to email me at swiftless@gmail.com.
Thank you very much for your nice tutorials, but why are most of them formatted with a centered text-alignment? This makes them really unreadable, especially the code sections!
Best regards!
Hey, great tutorials on your site 🙂
Just one suggestion, it would be great if you provided screenshots of what the final output will look like in some of these tutorials.
Keep up the good work
Oh i just noticed you mention images in the tutorial but they’re not showing 🙂
Hey Damo,
In a lot of the older tutorials (GLSL included) I originally had an image per tutorial but they have gone walkabout over the years through several site transitions.
If I ever get the chance to come back and put a lot of effort into the site, I would love to put updated images back.
Thanks,
Swiftless
Hi,
The dot product shouldn’t be between the vertex normal and the vector from vertex coords and light pos?
Isn’t the light pos in the dot product doing the light relative to the origin position 0,0,0 ?
This didn’t work for me. I ended up having to mix some of your code with most of http://nehe.gamedev.net/data/articles/article.asp?article=21
Hey,
Could you give an example of what you had to change? I haven’t had any other reported problems?
Cheers,
Swiftless