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.
|