Lights!
Andor Salga | 18 January, 2009 | 18:47
This blog is about lights and the new light classes we’ll be adding to the library for the next release. I’ll get right to it: OpenGL implementations provide access to light states which can add realism to a scene. Like most other rendering API’s, OpenGL approximates the results of lights, since calculating the real effects would require too many calculations (at least for our purposes). Lighting is important for adding realism to a scene just as real light is important for depth perception. Two eyes give us depth perception, however, if you close one eye, everything is still 3D! One major reason for this is because of light.
For the Unenlightened
For anyone who has never played with lights in OpenGL, it can cause quite a bit of confusion. Keep the following points in mind as you work with the lights:
Lights cannot be seen. They do not have a physical representation like a floating light bulb or a point. Nor do they have a ‘beam’ which can be seen. Placing a model into the scene to indicate where light is coming from is of course possible, but the API won’t do it for you.
Lights cannot cast shadows. The technique of adding shadows to a scene is surprisingly a totally separate topic and can get computationally expensive. If shadows are desired, it is left up to the user to create them.
Lights need to light something. If there aren’t any objects in the scene, there won’t be anything for the light to light up. So make sure you have a model (which has normals) and make sure the model is close enough to the light to get lit. This is especially true if you decide to adjust the attenuation of a light.
Lights go through objects! If you place a light directly in front of object A, and object B is behind object A, B will still get lit. It may seem silly, but OpenGL will not automatically figure out that light should not affect B.
The only way a light is ’seen’ is through its effects. If you haven’t already run the demo, now would be a good time. Even with these strange quirks, lights are definitely worth adding to your scene.
Separating the Spectrum
OpenGL architects decided to separate lights into three components so they are easier to define and work with. These components are ambient, diffuse and specular. Each component has an associated color, which allows us to create interesting effects not possible in real life. This includes using a single light to illuminate an object with a red diffuse component and green highlights.
I won’t cover everything there is to know about lights since someone already did that, but I will cover some basics so one can get a basic grasp of the concepts.
Ambient Light
Ambient light does not have a direction or position, but seems to come from everywhere. If only using ambient light, all objects in the scene would be lit evenly. Assigning an ambient component to a light can seem strange since lights typically tend to attenuate. The functionality is simply provided because OpenGL supports it. Generally, users should use scene.setAmbientLight(array) to place ambient light in the scene.
Upon first glance it may seem that some parts of the duck are lit brighter than others, such as its wing. This is merely the work of the artist who created the texture to create the illusion of a non-flat surface. Focus your attention on the duck’s bill or most of its body and you will see it is a solid color. Compare this with the screenshot with the diffuse lighting.
Diffuse Light
Diffuse light is what most people associate with what light is. Diffuse gives shape to the object, making it appear 3D. Diffuse lighting is dependent on the light’s position relative to the object. If you were looking at the object directly and the light was behind it, you would likely not see the effects.
Specular Light
Specular lighting is used to create shiny highlights. Highlights are typically seen on objects such as glass, metal or plastic and are usually white. Although in order to use specular highlights one must change an object’s material (this I’ll save for another blog). Unlike ambient and diffuse light, specular light takes not only the objects position into account, but also the viewer’s position. When running the demo, notice that the shiny highlights seem to always ‘point’ to you. This mimics the real world where shiny objects have highlights which seem to follow your eyes.
New Light Classes
Initially we provided simple wrapper functions around the OpenGL light state changing commands. However the interface was, among other things, awkward to work with. The new classes will (hopefully) be much more intuitive. With the new classes defined, the light manager is no longer needed. We have also added name members, providing the option to assign names and then later request the scene for a light by name. So, here are the new classes:
Light
This is an ‘abstract class’ which exists only to serve as a base for PositionalLight and DirectionalLight. It is not meant to be instantiated, therefore if anyone knows how to prevent instantiation, I would be glad to hear it. Otherwise I’m left with two options. Option 1, tell users not to instantiate it, or 2: remove it and duplicate the code for DirectionalLight and PositionalLight.
PositionalLight
Inherits from Light and adds a position accessors and attenuation factor accessros. This means the light actually has a position in space and can be attenuated. Depending on the position and attenuation, the light may or may not affect objects in your scene.
DirectionalLight
Inherits from Light. It does not have a position, only a direction. Directional light does not attenuate which is why it inherits from Light and not PositionalLight. It will light one s’ide’ of all the objects in your scene. Think of the light as being infinitely far away, having parallel rays.
SpotLight
Inherits from PositionalLight. Along with having a position, a spotlight also has members such as cutoff and direction. Cutoff is a value which refers to the angle where the spotlight’s cone ’stops’. This value is specified from the middle of the cone of light to the ‘end’. Therefore if a spotlight with a total of 90 degrees is desired, spot.setCutoff(45) is called. As with OpenGL, the cutoff can range from 0 to 90 inclusive or have a value of 180, in which case it is essentially a positional light. The direction of the spotlight is simply where it is pointing and is specified with a vector.
Light CRUD
Creating a light
//Create a positional light with a red diffuse component.
var light = new c3dl.PositionalLight();
light.setName('posLight');
light.setPosition([30,0,0]);
light.setDiffuse([1,0,0,1]);
// scene needs to have the light and by default lights are off.
light.setOn(true);
scn.addLight(light);
Reading and Updating a light
// change the diffuse color of a light on keypress.
var light = scene.getLight('posLight');
// swap the diffuse components on keypress.
if(light.getDiffuse()[0] == 1)
{
light.setDiffuse([0,0,1,1]);
}
else
{
light.setDiffuse([1,0,0,1]);
}
Delete the light
// posLight will no longer contribute to the scene.
scene.removeLight('posLight');
Trade offs
Since we will no longer be providing a direct wrapper, some flexibility is lost. For example, when using lights with OpenGL directly, you change light states with gl.light(…). You can start off with a point light and if you want, you can change a state variable and make it a spot light. Using our new classes, the only way this can be done is if the members are copied over from a positional light to a spot light. Since the library is intended for users generally not familiar with 3d concepts, this trade off is justified with separate lights
Lights Out
Lights can be used to increase the realism of a scene, but they can also adversely affect the realism if used incorrectly. For example, if a scene contained a skybox textured with buildings, we would not want lights illuminating the skybox. Otherwise it destroy the skybox illusion. Therefore before rendering a skybox, all lights should be turned off. In the C3DL, this is done by automatically. Before rendering the SkyModel, all lights are turned off. The same idea applies to particle systems. Particle textures are prelit and should not need additional lighting.
Next Steps
Adding materials to objects must be done in order to define which colors are absorbed and reflected.
All the lighting calculations must be done ourselves for the 2.0 context. So I’ll somehow have to squeeze this into my todo list.
ES Issues
It took a while to get specular lighting to work. I set the material state to FRONT, but there was no change. Took a bit of investigation and I found out to use materials, they have to be assigned to both FONT_AND_BACK facing polygons. This seems to be an OpenGLES specific issue.
