Let there be light. And let it be phong shaded.
Returning to my basic 3D cube from previous posts, I wanted to stop it looking quite so much like Jason's cube-of-many-colours and just have one colour that could then be changed depending on the status of what it represented. The problem here is that just setting the colour for each face to the same results in what is essentially shadow puppetry. There's no variation in the colours due to the environment and so they are all solid.
How do we get around this as easily as possible? Use the basic lighting supplied by OpenGL. Note that I'm not trying to do photo-realistic shading of a cube here, all I want is for the colour of the cube's faces to vary depending on how much light they are seeing. What this amounts to is finding angle between the light coming in and the 'normal' of the face - i.e. the direction perpendicular to the plane that the face is on. To do more complicated lighting you'd want to use shaders but that's serious overkill for what I'm looking for.
Anyway, there are several things that need to be done to get this working as intended - enable lighting in the OpenGL, position the light, set the normals and set the materials. The first part basically boils down to the following code:
GLfloat white_light[]= { 1.0f, 1.0f, 1.0f, 1.0f }; // set values for a white light
glLightfv(GL_LIGHT1, GL_DIFFUSE, white_light); // set the DIFFUSE colour of LIGHT1 to white
glLightfv(GL_LIGHT2, GL_DIFFUSE, white_light); // set the DIFFUSE colour of LIGHT2 to white
glEnable(GL_LIGHT1); // enable the lights
glEnable(GL_LIGHT2);
glEnable(GL_LIGHTING); // enable lighting in general
All that's happening here is I'm setting the values for the two lights I'm going to use and turning them on. You generally have access to 8 lights (I think) depending the implementation. I'm setting the DIFFUSE (light reflected everywhere) value to white so the surfaces it hits will just reflect the colour that I set the material to. I'm not bothering with any SPECULAR (light reflected like a mirror) or AMBIENT (general light) as it's not needed for this at present (and I *think* Ambient is set by default).
Next we need to position the light:
// set the light position
GLfloat light_pos1[]= { 1.0f, 1.0f, 3.0f, 0.0f };
glLightfv(GL_LIGHT1, GL_POSITION, light_pos1);
GLfloat light_pos2[]= { 1.0f, -1.0f, -3.0f, 0.0f };
glLightfv(GL_LIGHT2, GL_POSITION, light_pos2);
We need to be careful about where this code it. It needs to be after all 'camera' translations/rotations but before drawing any objects. If you put it in the wrong place, you'll get weird effects like the light changing when you move the camera. Note that I'm also using two lights as if the face is pointing away from the light, you get no diffuse light at all.
Next on the list is the normal vectors. For a cube this is fairly trivial to work out but for more complicated geometry, these would usually be loaded with the object or calculated when on loading. For my cube, something similar to the following is needed:
glNormal3d(0, 0, 1);
glVertex3f( 0.5, -0.5, 0.5 );
glVertex3f( 0.5, 0.5, 0.5 );
glVertex3f( -0.5, 0.5, 0.5 );
glVertex3f( -0.5, -0.5, 0.5 );
Obviously the important bit is the glNormal3d command which describes the vector of the normal for this face. Similar calls are made for all of the cube faces.
And finally, the last bit setting the material of the cube. When using lighting, you can no longer just use glColor and must use glMaterial instead. This allows you to set the various aspects of how the object reacts to light, namely how the AMBIENT, DIFFUSE and SPECULAR light is reflected back. As I said above, I really care most about the DIFFUSE light in this case which will just colour the faces depending on their direction relative to the light:
GLfloat red[] = {0.8f, .2f, .2f, 1.f};
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
Note that this should be placed before calling the display lists/glVertex comands. I'm also setting the AMBIENT a little as well as there is some of this by default.
With all these elements in place, you should get cubes that are a single colour but are shaded appropriately given the angle of the light! Next problem to overcome - overpainting!
References:
http://www.cse.msu.edu/~cse872/tutorial3.html
http://nehe.gamedev.net/tutorial/texture_filters_lighting__keyboard_control/15002/
Code:
https://github.com/doc-sparks/Interface/tree/v0.5
(Note that the tag includes the changes for overpainting as well which basically means some init code has been switched to the paintEvent function)