When shading with multiple light sources, we need to evaluate the shading equation for each light and sum the results to determine the total light intensity reflected by a surface location. The natural choice is to create uniform arrays to store the position and intensity of each light. We'll use an array of structures so that we can store the values for multiple lights within a single uniform variable.
The following figure shows a "pig" mesh rendered with five light sources of different colors. Note the multiple specular highlights.
Set up your OpenGL program with the vertex position in attribute location zero, and the normal in location one.
To create a shader program that renders using the ADS (Phong) shading model with multiple light sources, use the following steps:
layout (location = 0) in vec3 VertexPosition; layout (location = 1) in vec3 VertexNormal; out vec3 Color; struct LightInfo { vec4 Position; // Light position in eye coords. vec3 Intensity; // Light intensity }; uniform LightInfo lights[5]; // Material parameters uniform vec3 Kd; // Diffuse reflectivity uniform vec3 Ka; // Ambient reflectivity uniform vec3 Ks; // Specular reflectivity uniform float Shininess; // Specular shininess factor uniform mat4 ModelViewMatrix; uniform mat3 NormalMatrix; uniform mat4 MVP; vec3 ads( int lightIndex, vec4 position, vec3 norm ) { vec3 s = normalize( vec3(lights[lightIndex].Position – position) ); vec3 v = normalize(vec3(-position)); vec3 r = reflect( -s, norm ); vec3 I = lights[lightIndex].Intensity; return I * ( Ka + Kd * max( dot(s, norm), 0.0 ) + Ks * pow( max( dot(r,v), 0.0 ), Shininess ) ); } void main() { vec3 eyeNorm = normalize( NormalMatrix * VertexNormal); vec4 eyePosition = ModelViewMatrix * vec4(VertexPosition,1.0); // Evaluate the lighting equation for each light Color = vec3(0.0); for( int i = 0; i < 5; i++ ) Color += ads( i, eyePosition, eyeNorm ); gl_Position = MVP * vec4(VertexPosition,1.0); }
in vec3 Color; layout( location = 0 ) out vec4 FragColor; void main() { FragColor = vec4(Color, 1.0); }
lights
array in the vertex shader. For each light, use something similar to the following code. This example uses the C++ shader program class (prog
is a GLSLProgram
object).prog.setUniform("lights[0].Intensity", vec3(0.0f,0.8f,0.8f) ); prog.setUniform("lights[0].Position", position );
Update the array index as appropriate for each light.
Within the vertex shader, the lighting parameters are stored in the uniform array lights. Each element of the array is a struct of type LightInfo
. This example uses five lights. The light intensity is stored in the Intensity
field, and the position in eye coordinates is stored in the Position
field.
The rest of the uniform variables are essentially the same as in the ADS (ambient, diffuse, and specular) shader presented in Chapter 2, The Basics of GLSL Shaders.
The ads
function is responsible for computing the shading equation for a given light source. The index of the light is provided as the first parameter lightIndex
. The equation is computed based on the values in the lights
array at that index.
In the main
function, a for
loop is used to compute the shading equation for each light, and the results are summed into the shader output variable Color
.
The fragment shader simply applies the interpolated color to the fragment.
18.217.203.172