OpenGL Lighting 13. OpenGL Lighting • Overview of Lighting in OpenGL In order for lighting to have an effect in OpenGL, two things are required: A light An object to be lit Lights can be set to any color, and can have a wide variety of parameters which determine where they are located, in what direction they shine, and how they interact with objects in the world. Objects have a variety of parameters which determine how they reflect the light which hits them. The color(s) of an object is determined by the combining the parameters of the light(s) in the world with the object’s parameters. E.R. Bachmann & P.L. McDowell MV 4202 Page 1 of 32 OpenGL Lighting • Types of Light in OpenGL The three types of lights used in OpenGL are defined as follows: Ambient: Bounced light which has been scattered so much that it is impossible to tell the direction to its source (disappears if the light is turned off) Diffuse: Directional light which is brighter on perpendicular surfaces. Its reflection is scattered evenly. Specular: Directional light which tends to reflect in a preferred direction (associated with shininess) Each of these three components has an effect on the lighting of each object in the world – the actual light at a location is the sum of the three types of lights Just as in space, light is not noticable until it hits an object, and then its effect depends upon the object E.R. Bachmann & P.L. McDowell MV 4202 Page 2 of 32 OpenGL Lighting • Components of Lighting in OpenGL o OpenGL approximates light and lighting as if light could be broken into red, green and blue components - Light sources are characterized by the amount of red, green, and blue they emit o Each light emits one or more of the three separate types of light: - Ambient, diffuse and specular - Each of these types has a red, green, and blue component o In addition to the lights that can be placed in a scene, the entire scene can be lit by a global ambient light, which will affect the lighting of every object in the scene, regardless of location or position o The lights in a scene can be attenuated E.R. Bachmann & P.L. McDowell MV 4202 Page 3 of 32 OpenGL Lighting • Material o Each object is composed of a material which has four properties that determine how the lighting model treats the object: - emissive, ambient, diffuse and specular • ambient, diffuse and specular interact with the corresponding types of light and determine what percentage of the respective types of light are reflected • emissive allows an object to be a certain color even if not illuminated by any lights in the world emissive is most often used to simulate lamps and other light sources in a scene E.R. Bachmann & P.L. McDowell MV 4202 Page 4 of 32 OpenGL Lighting • Using Lighting in OpenGL There are four steps required to add lighting to scene: - Define a normal vector for each vertex of all the objects - Define material properties for all of the objects in the scene - Create, position and enable one or more light sources - Create and select a light model • Setting Material Properties An object’s material properties determine how it reflects the various types of light produced by light sources The material properties of an object may also be set to make it appear to emit light Material properties are set using void glMaterial{if}( GLenum face, GLenum pname, TYPE param ); void glMaterial{if}v( GLenum face, GLenum pname, TYPE *param ); where E.R. Bachmann & P.L. McDowell MV 4202 Page 5 of 32 OpenGL Lighting - face may be GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK to indicate which face the property is being specified for - pname identifies the property being set - param specifies the value to which the property is being set - The nonvector version works only for setting the shininess of an object (pname of GL_SHININESS) E.R. Bachmann & P.L. McDowell MV 4202 Page 6 of 32 OpenGL Lighting ♦ Ambient Reflection (GL_AMBIENT) Ambient reflection is most noticeable when an object receives no direct illumination Ambient reflectance isn’t affected by the position of the viewpoint The ambient RGB values of a material determine the proportion of the RGB components of ambient light which are reflected by the material The ambient term is the ambient color of the light scaled by the material property ambient term = ambientlight * ambientmaterial (13.2) The ambient components of a material are set by calling glMaterial with GL_AMBIENT as the pname argument Example 13.12 GLfloat mat_amb[] = { 0.1, 0.5, 0.8, 1.0 }; glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, mat_amb ); E.R. Bachmann & P.L. McDowell MV 4202 Page 7 of 32 OpenGL Lighting ♦ Diffuse Reflection (GL_DIFFUSE) Diffuse reflection is most intense where the light direction is perpendicular to the object’s surface It is affected by the angle of the incident light relative to the normal direction Diffuse reflectance isn’t affected by the position of the viewpoint The diffuse term takes into account the diffuse color of the light, whether the light falls directly on the vertex, and the diffuse color of the material The diffuse term is given by (max{L • n, 0}) * diffuselight * diffusematerial (13.3) where - L is a unit vector pointing from the vertex to the light position - n is the unit normal vector at the vertex The diffuse components of a material are set by calling glMaterial with GL_DIFFUSE as the pname argument Example 13.13 GLfloat mat_dif[] = { 0.1, 0.5, 0.8, 1.0 }; glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_dif ); E.R. Bachmann & P.L. McDowell MV 4202 Page 8 of 32 OpenGL Lighting ♦ Diffuse and Ambient Reflection (GL_AMBIENT_AND _DIFFUSE) The diffuse and ambient reflectance of an object are normally the same color and intensity Using GL_AMBIENT_AND _DIFFUSE as the pname may be used to set them both Example 13.14 GLfloat mat_amb_dif[] = {0.1, 0.5, 0.8, 1.0}; glMaterialfv( GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_amb_dif ); ♦ Specular Reflection Specular reflection produces highlights Depends on both the angle from the viewpoint to the object and the angle from the light source to the object It is brightest along the direct angle of reflection Specular highlights are normally white or light gray in color The specular term is given by (max{s • n, 0})shininess * specularlight * specularmaterial (13.4) where E.R. Bachmann & P.L. McDowell MV 4202 Page 9 of 32 OpenGL Lighting - s is the normalized sum of two unit vectors from ◊ the vertex to the light position (or the light direction) ◊ the vertex to the viewpoint (or (0, 0, 1) for non local view points) - n is the unit vector at the vertex - If s • n = 0, there is no specular term Example 13.15 GLfloat mat_spec[] = {1.0, 1.0, 1.0, 1.0}; glMaterialfv( GL_FRONT, GL_SPECULAR, mat_spec ); The shininess term in 13.4 determines the size and brightness of the highlight - The higher the value the smaller and brighter the highlight - Smaller values will produce a larger less focused (dimmer) highlight The shininess term is in the range [0.0, 128.0] Shininess is set by calling glMaterial with GL_SHININESS as the pname argument Example 13.16 glMaterialf(GL_FRONT, GL_SHININESS, 5.0 ); E.R. Bachmann & P.L. McDowell MV 4202 Page 10 of 32 OpenGL Lighting ♦ Emission (GL_EMISSION) Specifying an RGBA color for GL_EMISSION will make an object appear to be giving off light of that color Specifying an RGBA color for GL_EMISSION does not turn the object into a light source Objects with an emissive property will be visible even if there are no light sources Example 13.17 GLfloat mat_emiss[] = {0.3, 0.2, 0.2, 0.0}; glMaterialfv(GL_FRONT, GL_EMISSION,mat_emiss ); E.R. Bachmann & P.L. McDowell MV 4202 Page 11 of 32 OpenGL Lighting Example 13.18 Setting the material properties. GLfloat no_mat[] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat mat_ambient[] = { 0.7, 0.7, 0.7, 1.0 }; GLfloat mat_ambient_color[] = { 0.8, 0.8, 0.2, 1.0 }; GLfloat mat_diffuse[] = { 0.1, 0.5, 0.8, 1.0}; GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0}; GLfloat no_shininess[] = { 0.0 }; GLfloat low_shininess[] = { 5.0 }; GLfloat high_shininess[] = { 100.0 }; GLfloat mat_emission[] = {0.3, 0.2, 0.2, 0.0}; glClear(GL_COLOR_BUFFER_BIT | G L_DEPTH_BUFFER_BIT); // // // draw sphere in first row, first column diffuse reflection only; no ambient or specular glPushMatrix(); glTranslatef( -3.75, 3.0, 0.0 ); glMaterialfv( GL_FRONT, GL_AMBIENT, no_mat ); glMaterialfv( GL_FRONT, GL_DIFFUSE, mat_diffuse ); glMaterialfv( GL_FRONT, GL_SPECULAR, no_mat ); glMaterialfv( GL_FRONT, GL_SHININESS, no_shininess ); glMaterialfv( GL_FRONT, GL_EMISSION, no_mat ); glutSolidSphere( 1.0, 16, 16 ); glPopMatrix(); . . . E.R. Bachmann & P.L. McDowell MV 4202 Page 12 of 32 OpenGL Lighting • Using glColorMaterial Often it is desirable to use the current color specified using glColor*() as the ambient and diffuse color of the material glColorMaterial allows the ambient, diffuse, or specular properties of a material to be set to the current color Usage: void glColorMaterial( GLenum face, GLenum mode ); where - face specifies which polygon face the current color will be applied Possible values for face: ◊ GL_FRONT ◊ GL_BACK ◊ GL_FRONT_AND_BACK - mode specifies the property to be set to the current color Possible values for mode: ◊ GL_AMBIENT ◊ GL_DIFFUSE E.R. Bachmann & P.L. McDowell MV 4202 Page 13 of 32 OpenGL Lighting ◊ GL_AMBIENT_AND_DIFFUSE ◊ GL_SPECULAR - The current color may only be applied in one mode at a time; i.e., it is not possible to set both the specular and either ambient or diffuse colors to the current color at the same time ♦ Enabling glColorMaterial Before glColorMaterial can be used, it must be enabled with glEnable( GL_COLOR_MATERIAL ); Example 13.19 GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; glEnable( GL_COLOR_MATERIAL ); glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ); // Set the ambient and diffuse properties glColor3f( 0.2, 0.5, 0.8 ); // Set the specular color glMaterialfv( GL_FRONT, GL_SPECULAR, mat_specular ); glColor3f( 1.0, 1.0, 1.0 ); // Draw the object auxSolidSphere( 1.0 ); E.R. Bachmann & P.L. McDowell MV 4202 Page 14 of 32 OpenGL Lighting • Creating, Positioning and Enabling Lights OpenGL allows the inclusion of at least eight separate light sources Each light source is given a predefined name as follows: GL_LIGHT0 GL_LIGHT1 . . . GL_LIGHT7 Each must be individually turned on and off using glEnable and glDisable respectively: glEnable( GL_LIGHT0 ); glDisable( GL_LIGHT0 ); Lighting as a whole is enabled and disabled similarly glEnable( GL_LIGHTING ); E.R. Bachmann & P.L. McDowell MV 4202 Page 15 of 32 OpenGL Lighting If lighting is disabled, the current color is mapped onto the current vertex - No calculations concerning normals, light sources, lighting model and material properties are performed - Each light source will have numerous characteristics or properties: - Color and intensities of its ambient, diffuse and specular components - Position - Direction - Constant, linear and quadratic attenuation factors - Spotlight exponent and cutoff factors The properties of each light are set using: void glLight{if}( GLenum light, GLenum pname, TYPE param ); void glLight{if}v( GLenum light, GLenum pname, TYPE *param ); where E.R. Bachmann & P.L. McDowell MV 4202 Page 16 of 32 OpenGL Lighting - light specifies the name of the light (e.g., GL_LIGHT0) - pname specifies the property being set - param specifies the value to which the property is being set - The nonvector version can only be used to set single-value characteristics Pname may be any of the following to set the respective properties: - GL_AMBIENT Ambient RGBA intensity of the light Example 13.1 GLFloat lt_ambient[] = { 0.0, 0.0, 0.0, 1.0 }; glLightfv( GL_LIGHT0, GL_AMBIENT, lt_ambient ); - GL_DIFFUSE Diffuse RGBA intensity of the light Example 13.2 GLFloat lt_diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; glLightfv( GL_LIGHT0, GL_DIFFUSE, lt_diffuse ); E.R. Bachmann & P.L. McDowell MV 4202 Page 17 of 32 OpenGL Lighting - GL_SPECULAR Specular RGBA intensity of the light Example 13.3 GLFloat lt_spec[] = { 1.0, 1.0, 1.0, 1.0 }; glLightfv( GL_LIGHT0, GL_SPECULAR, lt_spec ); - GL_POSITION Sets either the position or direction of a light based upon the value of the fourth element of param ◊ 1.0 indicates that the first three elements give the xyz position of the light ◊ 0.0 indicates that the first three elements define a vector which points in the direction from which the light is coming Example 13.4 A directional light GLFloat lt_direct[] = { 1.0, 1.0, 1.0, 0.0 }; glLightfv( GL_LIGHT0, GL_POSITION, lt_direct ); Example 13.5 A positional light E.R. Bachmann & P.L. McDowell MV 4202 Page 18 of 32 OpenGL Lighting GLFloat lt_posit[] = { 1.0, 1.0, 1.0, 1.0 }; glLightfv( GL_LIGHT0, GL_POSITION, lt_posit ); - GL_SPOT_DIRECTION xyz direction of a spot light ◊ vector indicates the direction the light is pointing Example 13.6 GLFloat spot_direction[] = { -1.0, -1.0, 0.0 }; glLightfv( GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction ); - GL_SPOT_CUTOFF Defines the spotlight cutoff angle in degrees cutoff angle spotlight Default cutoff angle is 180 degrees E.R. Bachmann & P.L. McDowell MV 4202 Page 19 of 32 OpenGL Lighting Example 13.7 glLightf( GL_LIGHT0, GL_SPOT_CUTOFF, 45.0 ); - GL_SPOT_EXPONENT Controls how concentrated the light is Lower values cause the light to attenuate near the edges of the cone ◊ default exponent is 0 ◊ higher values result in a more focused light source - GL_CONSTANT_ATTENUATION GL_LINEAR_ATTENUATION GL_QUADRATIC_ATTENUATION Set the constants of the attenuation factor used to attenuate positional lights E.R. Bachmann & P.L. McDowell MV 4202 Page 20 of 32 OpenGL Lighting attenuationfactor = 1 k c + kl d + k q d 2 (13.1) - 13.1 is multiplied by the contribution of the associated light source for each vertex - kc, kl, and kq are the constant, linear and quadratic attenuation factors, respectively - default values: kc = 1, kl = 0, and kq = 0 - d is the distance from the light source to the vertex - Using attenuated lighting slows performance Example 13.8 glLightf(GL_LIGHT2,GL_CONSTANT_ATTENTUATION,2.0); glLightf( GL_LIGHT2, GL_LINEAR_ATTENTUATION, 1.0 ); glLightf(GL_LIGHT2, GL_QUADRATIC_ATTENTUATION,0.5); E.R. Bachmann & P.L. McDowell MV 4202 Page 21 of 32 OpenGL Lighting • Selecting a Lighting Model The lighting model is concerned with three factors: - The global ambient light intensity - Local or infinitely distance viewpoint - Differences between the lighting of the front and the back polygon faces The properties of the lighting model are set using glLightModel{if}( GLenum pname, TYPE param ); glLightModel{if}v( GLenum pname, TYPE *param ); where - pname is the property being set - param is the value to which the property is being set - Possibilities for pname are ◊ GL_LIGHT_MODEL_AMBIENT Set ambient RGBA intensity of the entire scene ◊ GL_LIGHT_MODEL_LOCAL_VIEWER How specular reflections are computed ◊ GL_LIGHT_MODEL_TWO_SIDE One-sided or two-sided lighting E.R. Bachmann & P.L. McDowell MV 4202 Page 22 of 32 OpenGL Lighting ♦ Global Ambient Light Global ambient light is not from any particular source - It is present in the scene even if no lights are enabled Example 13.9 Specify the global ambient RGBA intensity glFloat lmodel_ambient[]={0.2, 0.2, 0.2, 1.0}; glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient ); ♦ Local or Infinite Viewpoint The location of the viewpoint affects the calculations for the highlights produced by specular reflectance The intensity of a highlight depends on - the vertex normal - the direction from the vertex to the light source - the direction from the vertex to the viewpoint With an infinite viewpoint, the direction from every vertex remains constant With a local viewpoint, the direction is different for each vertex E.R. Bachmann & P.L. McDowell MV 4202 Page 23 of 32 OpenGL Lighting Example 13.10 Placing the viewpoint at the origin of the viewing (eye) coordinates: glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE ); ♦ Two-sided Lighting By default, OpenGL only performs lighting calculations for the front side of polygons Lighting may be enabled for both polygon faces Example 13.11 Lighting both polygon faces: glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE ); E.R. Bachmann & P.L. McDowell MV 4202 Page 24 of 32 OpenGL Lighting • The OpenGL Lighting Calculation The lit color of a vertex in the RGBA mode is determined by vertexcolor = emissionmaterial + (ambientlight model × ambient material ) 1 × (spotlight effect )i +∑ 2 t =0 kc + k f d + k q d n −1 [ × ambientlight × ambient material ( + max{Li ⋅ n, 0}× diffuselight i × diffuse material ( + max{si ⋅ n, 0} shininess ) × specularlighti × specularmaterial )]) (13.5) • Looking at this calculation, it is • Efficiency Note • Lighting is expensive -- all of the following increase the computational load: E.R. Bachmann & P.L. McDowell MV 4202 Page 25 of 32 OpenGL Lighting - Multiple Lights Attenuation Two-sided Lighting Local Viewpoint • Looking at equation 13.5, it is easy to see why lighting is expensive and why each of these increases the computational load. • Shading in OpenGL OpenGL provides two different shading models ♦ Flat Shading (GL_FLAT) Lines or filled polygons are drawn in a single color The color of one particular vertex determines the color for the entire primitive - For a line segment, the color of the line is the current color when the last vertex is specified - See text Table 4-2 to determine how OpenGL selects the color of a Flat shaded polygon • Smooth or Gouraud Shading (GL_SMOOTH) E.R. Bachmann & P.L. McDowell MV 4202 Page 26 of 32 OpenGL Lighting The color of the interior points of primitives is interpolated between the colors of the vertices which define the primitive The color of each vertex is treated individually To specify a shade model use void glShadeModel( GLenum mode ); where - mode is either GL_SMOOTH (default) or GL_FLAT • Positioning and Directing Lights Light sources are subject to the same matrix transformations as a primitive object E.R. Bachmann & P.L. McDowell MV 4202 Page 27 of 32 OpenGL Lighting - Lights are positioned and directed relative to the current local coordinate system as described by the current modelview matrix - The position and direction of a light can be manipulated by changing the contents of the modelview matrix stack - The positioning of a light does not affect the modelview matrix stack (a translation matrix is not generated) ♦ Stationary Lights To fix the position of a light, set it’s position after the projection matrices have been set and viewing transformations are on the stack - Only the viewing transformations should be on the modelview stack when the light is positioned - Do the viewing transformations, position the light then do the modeling transformations and draw the objects in the scene ♦ Moving Light To move a light relative to an object, position it by translating and rotating it relative to the local coordinate system of the object ♦ Light tied to the Viewpoint E.R. Bachmann & P.L. McDowell MV 4202 Page 28 of 32 OpenGL Lighting To create a light which moves with the viewpoint, set the light position and position it when there are no viewing transformations on the stack - Only the Identity matrix should be on the modelview stack - Both the light and the view point will be affected in the same way - IOW, position the light after the modelview stack has been cleared to the identity matrix, but before any viewing or modeling transformation are completed! • Shadows OpenGL does not directly support shadows “Fake shadows” can be created by projecting three dimensional objects onto a two dimensional plane The following function creates a shadow matrix: E.R. Bachmann & P.L. McDowell MV 4202 Page 29 of 32 OpenGL Lighting It takes as arguments - Three points that lie in the plane onto which the shadow will be projected - The position of the light source casting the shadow - A pointer to the projection matrix which is being returned Two utility functions reduceToUnit and calcNormal are also shown ♦ makeShadowMatrix // Creates a shadow projection matrix out of the plane equation // coefficients and the position of the light. The return value // is stored in destMat[][] void MakeShadowMatrix( GLfloat points[3][3], GLfloat lightPos[4], GLfloat destMat[4][4] ) { GLfloat planeCoeff[4]; GLfloat dot; // Find the plane equation coefficients. // Find the first three coefficients the same way we // find a normal. calcNormal( points, planeCoeff ); // Find the last coefficient by back substitutions planeCoeff[3] = - ((planeCoeff[0]*points[2][0]) + (planeCoeff[1]*points[2][1]) + (planeCoeff[2]*points[2][2])); // Dot product of plane and light position dot = planeCoeff[0] * lightPos[0] + planeCoeff[1] * lightPos[1] + E.R. Bachmann & P.L. McDowell MV 4202 Page 30 of 32 OpenGL Lighting planeCoeff[2] * lightPos[2] + planeCoeff[3] * lightPos[3]; // Now make the projection matrix // First column destMat[0][0] = dot - lightPos[0] * planeCoeff[0]; destMat[1][0] = 0.0f - lightPos[0] * planeCoeff[1]; destMat[2][0] = 0.0f - lightPos[0] * planeCoeff[2]; destMat[3][0] = 0.0f - lightPos[0] * planeCoeff[3]; // Second column destMat[0][1] = 0.0f - lightPos[1] * planeCoeff[0]; destMat[1][1] = dot - lightPos[1] * planeCoeff[1]; destMat[2][1] = 0.0f - lightPos[1] * planeCoeff[2]; destMat[3][1] = 0.0f - lightPos[1] * planeCoeff[3]; // Third Column destMat[0][2] = 0.0f - lightPos[2] * planeCoeff[0]; destMat[1][2] = 0.0f - lightPos[2] * planeCoeff[1]; destMat[2][2] = dot - lightPos[2] * planeCoeff[2]; destMat[3][2] = 0.0f - lightPos[2] * planeCoeff[3]; // Fourth Column destMat[0][3] = 0.0f - lightPos[3] * planeCoeff[0]; destMat[1][3] = 0.0f - lightPos[3] * planeCoeff[1]; destMat[2][3] = 0.0f - lightPos[3] * planeCoeff[2]; destMat[3][3] = dot - lightPos[3] * planeCoeff[3]; } // end makeShadowMatrix ♦ calcNormal // Points p1, p2, & p3 specified in counter clock-wise order void calcNormal( float v[3][3], float out[3] ) { float v1[3],v2[3]; static const int x = 0; static const int y = 1; static const int z = 2; // Calculate two vectors from the three points v1[x] = v[0][x] - v[1][x]; v1[y] = v[0][y] - v[1][y]; v1[z] = v[0][z] - v[1][z]; v2[x] = v[1][x] - v[2][x]; E.R. Bachmann & P.L. McDowell MV 4202 Page 31 of 32 OpenGL Lighting v2[y] = v[1][y] - v[2][y]; v2[z] = v[1][z] - v[2][z]; // Take the cross product of the two vectors to get // the normal vector which will be stored in out out[x] = v1[y]*v2[z] - v1[z]*v2[y]; out[y] = v1[z]*v2[x] - v1[x]*v2[z]; out[z] = v1[x]*v2[y] - v1[y]*v2[x]; // Normalize the vector (shorten length to one) ReduceToUnit(out); } // end calNormal ♦ ReduceToUnit // Reduces a normal vector specified as a set of // three coordinates, to a unit normal vector of length one. void ReduceToUnit( float vector[3] ) { float length; // Calculate the length of the vector length = (float)sqrt( (vector[0]*vector[0]) + (vector[1]*vector[1]) + (vector[2]*vector[2]) ); // Keep the program from blowing up by providing an // acceptable value for vectors that may evaluate // close to zero. if ( length > 0.0f ) { vector[0] /= length; vector[1] /= length; vector[2] /= length; } // end if } // end ReduceToUnit E.R. Bachmann & P.L. McDowell MV 4202 Page 32 of 32