Tutorial Index

Tutorial 21 - Spotlights and Other Issues


Introduction

This tutorial will expand on the subject of osg::Light, and discuss some issues that relate to using this functionality.

Concepts

Spotlights
Caveats

Spotlights

Spotlights, or directed lights, are a crucial lighting concept to simulate such items as a flashlight, or a headlight. The light flows from a certain point outward as a cone, heading in one direction only. The OSG::Light class has built in functionality to produce spotlights, building upon the knowledge we already know about lighting. There are 3 main components to constructing a spotlight: direction, cutoff angle, and the attenuation exponent.

The direction of the spotlight is a 3D vector that defines where the rays of the spotlight will travel. This can be specified via the following function:

osg::Light* light = new osg::Light();
...
light->setDirection(osg::Vec3d(0.0, 0.0, 0.0);

The parameters of the osg::Vec3d correspond to each of the 3 axis. The values of the axis should be between 0.0 and 1.0. To shine a spotlight in the positive x direction, for example, we would need to use the values of (1.0, 0.0, 0.0).

The next component of the spotlight is the angle of cutoff. This angle defines the area of the cone of light. This may be set via:

osg::Light* light = new osg::Light();
...
light->setSpotCutoff(angle_in_floating_point);

The angle is measured from the center of the cone of the spotlight to the outer edge. 0.0 would give us a single, while 90.0 degrees will give us a hemisphere of light. Note that 0.0 is the min, and 90.0 is the max value used for this function. A special value of 180.0 degrees is used to cut the spotlight off, producing a regular light. Thus all normal lights in your scene have a cutoff angle of 180.0. The following diagram shows the cutoff angle of a spotlight. Note that the lines do not match up because of some odd quirk with Dia's export to png function.

The last component of the spotlight is the optional cutoff exponent. This value defines the intensity of the light towards the edges of the cone. As objects move from the center of the spotlight to the edges, we have the option of attenuating the intensity of the light on the surface of the objects. This may be performed via the following.

osg::Light* light = new osg::Light();
...
light->setSpotExponent(value_in_floating_point);

Values for this function may be 0.0 for no attenuation, up to any arbitrary floating point value. Careful, though, as the attenuation quickly takes hold with larger values. Experiment with this function to see what different values will produce.

Caveats

As stated in the previous tutorial, most implementations of OpenGL permit the maximum use of 8 lights at any one time. See any problems? Take your average modern game, and you'll quickly notice that in an average scene, more than 8 lights are used. Even games implemented in OpenGL. So how do the developers get around this limitation?

One method is to light the scene in increments. Light one portion of the scene, save the data, move the light to the next desired area, perform the lighting algorithm, and so on. This is done for each desired 'light' in the scene. So in the end we have re-used a single light to illuminate the entire world. This is performed each frame. As you can guess, this method can be quite computationally expensive. As well, we currently know now way to abstract this concept of light reuse to OSG.

Another trick to lighting is the use of lightmaps. A lightmap is simply a way to precalculate the lighting of texture in the case of static lights. Take a torch on a wall for example. The torch may flicker and waiver, but the light typically stays constant. We can take advantage of this fact by precomputing the light values on the textures near the torch, modifying the original texture color values to mimic being lit by this torch. By using this pre-calculated texture on the wall, we eliminate the need to perform actual lighting calculations. So, a light map is precalculated light algorithms applied to a texture map. id software's Quake 3 uses lightmaps extensively. Unfortunately, lightmaps cannot directly account for dynamic lights, lights that move.

Shadows? OpenGL does not directly have the ability to produce shadows from the lighting functionality, but shaders can be used to write algorithms to produce shadows.

Remember how the lighting algorithm works for OpenGL: the lighting of objects is actually the coloring of the individual vertices. The actual light value of a surface is an function of the values of the vertices that compose the surface. So, a low polygon count model will have less detailed lighting than a high count polygon model? Why? Because lower polygons implies less vertices. A sphere composed of 64 surfaces will look a lot less pretty than one composed of 4096 faces. So if your objects do not have satisfactory lighting, you may try to increase the amount of polygons for the model.

And that Zero light? Well, OSG automatically uses the first OpenGL light (Zero) as the global light. If you do not reinitialize this light, or modify it's properties, you will always have the global light turned on. In the tutorials we have total darkness, except for the lights we specifically create. This is because one of those lights we're using is the Zero light. If we did not re-use this light, we would have the global light applied to the scene.

Tutorial Code

Tutorial Code

References

osg::Light

Conclusion

So now we have a better idea what we can and cannot do with the lighting functionality of OSG. It provides a basic way to apply lighting to the scene, but with the current limitations in place, the ability to create a multi-light scene is severely restricted.

The next tutorial we'll take a look at billboards, which are objects that auto-rotate to face the camera. Billboards are typically used to render foliage and other sprite-based resources.