Point lights have a position and the light due to each point is considered in body and surface components. The body component does not depend on the viewer because it represents light that goes into the object a small ways, bounces around unpredictably, and comes out again just as likely to be going towards the viewer as any other point. The intensity of the body reflection depends only on the surface normal and the direction from the surface to the light source. When these vectors are paralell the reflection is at its highest and when they are orthogonal it drops to zero. In the Phong model we approximate this relationship with the cosine function which we can get from taking the dot product of the normal and the unit vector in the direction of the light source. The product of this scalar, the body color of the object, and the color of the point light gives us the body component.
Reflection_body = Color_body * Color_light * (surfaceToLight . surfaceNormal)The surface component corresponds to the portion of the light that will bounce off the surface of the object and go towards the viewer. If the object is completely matte this will be zero but many real objects, especially plastics and metals, show small highlights. Because many objects have a clear layer above their pigment and we're modeling the light that bounces off this layer we need to have a separate color to prepresent the surface reflection color. For plastics this will likely be white while for metals it will be the color of the metal. Having a separate color also allows us to vary the intensity of the reflection by decreasing the color values.
To calculate the surface reflection we first find the vector half way between the vector from the surface to the viewer and the vector from the surface to the light source. The closer this vector is to the surface normal the brighter the reflection should be. We use the cosine function again to approximate this, but this time we raise the result to a power that represents how tight the highlights should be. This should depend on the surface, and nominally models roughness, but doesn't do a good job of making surfaces look more rough or smooth.
Reflection_surface = Color_surface * Color_light * ( normalize(surfaceToLight+surfaceToViewer) . surfaceNormal)^roughness
Spot lights are a simple modification of point lights to give them a direction and a dispersion angle. The color value is multiplied by the negative dot product of the direction from the surface to the light and the light's direction, raised to a power. This power represents the tightness of the spot.
Color = Color * (-light.direction . surfaceToLight)^tightnessThe image and movie below show a spotlight moving across a phong shaded cube.
Below are two images of a cube with an offest point light just in front of it, one rendered with Gouraud shading and one with Phong, showing how Gouraud shading can be quite inaccurate. The linked movies show the light moving from left to right across the cube.