6. GLSL Materials

GLSL Materials

Materials in OpenGL define how objects interact with light, controlling their visual appearance under various lighting conditions. Using GLSL shaders, we can create realistic lighting effects by combining ambient, diffuse, and specular components. This tutorial implements these concepts and explains how the shaders work in detail.

Main Program

The main program initializes OpenGL, sets up shaders, and applies material properties to the teapot. Below is the full code:

#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 shader;

// Rotation angle for animation
GLfloat angle = 0.0;

// Light position
GLfloat lx = 0.0, ly = 1.0, lz = 1.0, lw = 0.0;

void init(void) {
    glEnable(GL_DEPTH_TEST);  // Enable depth testing
    glEnable(GL_LIGHTING);    // Enable lighting calculations
    glEnable(GL_LIGHT0);      // Enable light source 0

    shader.init("shader.vert", "shader.frag"); // Load shaders
}

void cube(void) {
    glRotatef(angle, 1.0, 0.0, 0.0); // Rotate on X-axis
    glRotatef(angle, 0.0, 1.0, 0.0); // Rotate on Y-axis
    glRotatef(angle, 0.0, 0.0, 1.0); // Rotate on Z-axis

    GLfloat mShininess[] = {50};                  // Shininess
    GLfloat DiffuseMaterial[] = {1.0, 0.0, 0.0}; // Red diffuse reflection
    GLfloat AmbientMaterial[] = {0.2, 0.2, 0.2}; // Subtle ambient reflection
    GLfloat SpecularMaterial[] = {1.0, 1.0, 1.0}; // White specular highlights

    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, DiffuseMaterial);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, AmbientMaterial);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, SpecularMaterial);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mShininess);

    glutSolidTeapot(2.0); // Render teapot
}

void setLighting(void) {
    GLfloat DiffuseLight[] = {1.0, 1.0, 1.0};
    GLfloat AmbientLight[] = {0.2, 0.2, 0.2};
    GLfloat SpecularLight[] = {1.0, 1.0, 1.0};
    GLfloat LightPosition[] = {lx, ly, lz, lw};

    glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
    glLightfv(GL_LIGHT0, GL_AMBIENT, AmbientLight);
    glLightfv(GL_LIGHT0, GL_SPECULAR, SpecularLight);
    glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
}

void display(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the screen
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // Camera position

    setLighting(); // Configure lighting

    shader.bind();  // Activate the shader program
    cube();         // Render the teapot
    shader.unbind(); // Deactivate the shader program

    glutSwapBuffers(); // Swap buffers for smooth rendering
    angle += 0.1f;    // Increment rotation angle
}

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 Materials Example");

    glewInit();
    init();

    glutDisplayFunc(display);
    glutIdleFunc(display);
    glutReshapeFunc(reshape);

    glutMainLoop();
    return 0;
}

Vertex Shader

The vertex shader calculates and passes data needed by the fragment shader, including the transformed normal vector and the half-vector for specular lighting.

varying vec3 vertex_light_half_vector;
varying vec3 vertex_normal;

void main() {
    // Transform the normal vector to world space
    vertex_normal = normalize(gl_NormalMatrix * gl_Normal);

    // Calculate the half-vector for the light
    vertex_light_half_vector = normalize(gl_LightSource[0].halfVector.xyz);

    // Pass vertex color to the fragment shader
    gl_FrontColor = gl_Color;

    // Transform vertex position to clip space
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

Fragment Shader

The fragment shader computes the final lighting for each pixel, combining ambient, diffuse, and specular terms. Here’s a breakdown of its components:

Ambient Term

Ambient lighting provides a base level of light that uniformly illuminates the object.

vec4 ambient_color = gl_FrontMaterial.ambient * gl_LightSource[0].ambient +
                     gl_LightModel.ambient * gl_FrontMaterial.ambient;

Diffuse Term

Diffuse lighting simulates light that scatters equally in all directions from a surface.

vec4 diffuse_color = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
float diffuse_value = max(dot(vertex_normal, vertex_light_position), 0.0);

Specular Term

Specular lighting creates shiny highlights and depends on the angle between the light’s reflection and the viewer’s direction.

vec4 specular_color = gl_FrontMaterial.specular * gl_LightSource[0].specular *
                      pow(max(dot(vertex_normal, vertex_light_half_vector), 0.0), gl_FrontMaterial.shininess);

Final Color

The final fragment color combines the ambient, diffuse, and specular terms:

gl_FragColor = ambient_color + diffuse_color * diffuse_value + specular_color;

Full Fragment Shader

Here is the complete fragment shader, combining all the components:

varying vec3 vertex_light_half_vector;
varying vec3 vertex_normal;

void main() {
    // Ambient term
    vec4 ambient_color = gl_FrontMaterial.ambient * gl_LightSource[0].ambient +
                         gl_LightModel.ambient * gl_FrontMaterial.ambient;

    // Diffuse term
    vec4 diffuse_color = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
    float diffuse_value = max(dot(vertex_normal, vertex_light_position), 0.0);

    // Specular term
    vec4 specular_color = gl_FrontMaterial.specular * gl_LightSource[0].specular *
                          pow(max(dot(vertex_normal, vertex_light_half_vector), 0.0), gl_FrontMaterial.shininess);

    // Combine terms
    gl_FragColor = ambient_color + diffuse_color * diffuse_value + specular_color;
}

Download Links

If you have any questions, feel free to email me at swiftless@gmail.com.

  • March 25, 2010
  • 6