The necessary step after the previous post: Spot Lights.
After tweaking around with point lights, it was the time to experiment with another kind of light: the spot light. I also introduced a 3D model from the Stanford University 3D Scanning Repository to make it a little more interesting.
Here is the fragment shader, which includes slight modifications in the lighting calculation function: spotLight. The vertex shader is exactly the same as the previous post.
#version 330
out vec4 fragmentColor;
struct Light
{
vec4 position;
vec4 ambient;
vec4 diffuse;
vec4 specular;
float constant_attenuation;
float linear_attenuation;
float quadratic_attenuation;
vec3 spot_direction;
float spot_cutoff;
float spot_exponent;
};
uniform Lights
{
Light light[8];
} lights;
uniform Material
{
vec4 ambient;
vec4 diffuse;
vec4 specular;
float shininess;
} material;
uniform int num_lights;
smooth in vec3 vPosition;
smooth in vec3 vNormal;
vec4
spotLight (int lightID)
{
float nDotVP; // normal * light direction
float nDotR; // normal * light reflection vector
float pf; // power factor
float spotDot; // cosine of angle between spotlight
float spot_att; // spotlight attenuation factor;
float attenuation; // computed attenuation factor
float d; // distance from surface to light position
vec3 VP; // direction from surface to light position
vec3 reflection; // direction of maximum highlights
// Compute vector from surface to light position
VP = vec3 (lights.light[lightID].position) - vPosition;
// Compute distance between surface and light position
d = length (VP);
// Normalize the vector from surface to light position
VP = normalize (VP);
// Compute attenuation
attenuation = 1.f / (lights.light[lightID].constant_attenuation +
lights.light[lightID].linear_attenuation * d +
lights.light[lightID].quadratic_attenuation * d * d);
// See if point on surface is inside cone of illumination
spotDot = dot (-VP, normalize (lights.light[lightID].spot_direction));
if (spotDot < lights.light[lightID].spot_cutoff)
spot_att = 0.f;
else
spot_att = pow (spotDot, lights.light[lightID].spot_exponent);
// Combine the spot and distance attenuation
attenuation *= spot_att;
reflection = normalize (reflect (-normalize (VP), normalize
(vNormal)));
nDotVP = max (0.f, dot (vNormal, VP));
nDotR = max (0.f, dot (normalize (vNormal), reflection));
if (nDotVP == 0.f)
pf = 0.f;
else
pf = pow (nDotR, material.shininess);
vec4 ambient = material.ambient * lights.light[lightID].ambient * attenuation;
vec4 diffuse = material.diffuse * lights.light[lightID].diffuse * nDotVP * attenuation;
vec4 specular = material.specular * lights.light[lightID].specular * pf * attenuation;
return ambient + diffuse + specular;
}
void main(void)
{
for (int i = 0; i < num_lights; ++i)
fragmentColor += spotLight (i);
}
And finally some screenshots showing the dragon model and the two spotlights over it:





On the client side of OpenGL how do you buffer Array-Of-Structure Uniforms?
On client side I simply use a std::vector<Light> (Light being the struct to be used in the shader). Then I copy all the client side Light data to the GPU buffer.
Hope it helps! See you!
Why don’t you use a “viewDirection” vector when nDotR is computed ?
i.e. nDotR = max (0.f, dot (normalize (viewDirection), reflection));
nDotR stands for “Normal dot Reflection”. Why would you use the viewDirection?
Beacuse specular highlights depend on the viewer (camera) position.
http://en.wikibooks.org/wiki/GLSL_Programming/GLUT/Specular_Highlights
http://www.arcsynthesis.org/gltut/Illumination/Tut11%20Phong%20Model.html
Well, yes. But, since I’m doing the lighting computations in eye (or camera) space I don’t need the viewDirection vector. I already know the camera is at (0, 0, 0).
I think that even if you doing the lighting computation in eye (or camera) space you still need the viewDirection vector, but in your case viewDirection = normalize(-vPosition). Look at the arcsynthesis tutorial linked above. There the lighting computation is also in camera space.
Yeah, I see. I simplified a little. These are only “demos”, not production code
Thank you for pointing this out.