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.

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.
115.
116.
    #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

//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);
    
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    
    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

    GLfloat mShininess[] = {50};
    
    GLfloat DiffuseMaterial[] = {1.0, 0.0, 0.0};
    GLfloat AmbientMaterial[] = {0.0, 0.0, 0.0};
    GLfloat SpecularMaterial[] = {1.0, 1.0, 1.0};
    
    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);
}

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};
    
    glLightfv (GL_LIGHT0, GL_SPECULAR, SpecularLight);
    glLightfv (GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
    glLightfv (GL_LIGHT0, GL_AMBIENT, AmbientLight);
    
    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:

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.

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
    varying vec3 vertex_light_position;
varying vec3 vertex_light_half_vector;
varying vec3 vertex_normal;

void main() {            
    // Calculate the normal value for this vertex, in world coordinates (
multiply by gl_NormalMatrix)

    vertex_normal = normalize(gl_NormalMatrix * gl_Normal);
    // Calculate the light position for this vertex
    vertex_light_position = normalize(gl_LightSource[0].position.xyz);

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

    // 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:

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:
ambient_color
diffuse_color
specular_color

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

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.

    varying vec3 vertex_light_position;
varying vec3 vertex_light_half_vector;
varying vec3 vertex_normal;

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

    // Calculate the diffuse term
    vec4 diffuse_color = gl_FrontMaterial.diffuse * gl_LightSource[0]
.diffuse;

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

    // 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.
    float diffuse_value = max(dot(vertex_normal, vertex_light_position), 0.0);

    // Set the output color of our current pixel
    gl_FragColor = ambient_color + diffuse_color * diffuse_value +
specular_color;
}

 

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

Download shader.vert Source Code for this Tutorial

Download shader.frag Source Code for this Tutorial

  • March 25, 2010
  • 6