Chapter Overview
| >Point Light >Directional Light >Spot Light >Light Position, Direction and Influence Area >Tutorial - Light it up >Exercises |
This chapter is dedicated to another very important aspect of computer graphics: light sources. Well, most scenes have at least one, but often more, lightsources to simulate a realistic environment. OpenSG features the common OpenGL lightsourses: spot, directional and point lights. The actual usage is easy and should be similar to OpenGL. At the end of this chapter there will be a tutorial in which we will add some light sources to the water mesh from the last chapter.
PointLightPtr pLight = PointLight::create();
beginEditCP(pLight, PointLight::PositionFieldMask |
PointLight::ConstantAttenuationFieldMask |
PointLight::LinearAttenuationFieldMask |
PointLight::QuadraticAttenuationFieldMask |
PointLight::DiffuseFieldMask |
PointLight::AmbientFieldMask |
PointLight::SpecularFieldMask |
PointLight::BeaconFieldMask);
//Attenuation parameters
pLight->setConstantAttenuation(1);
pLight->setLinearAttenuation(0);
pLight->setQuadraticAttenuation(2);
//color information
pLight->setDiffuse(Color4f(1,1,1,1));
pLight->setAmbient(Color4f(0.2,0.2,0.2,1));
pLight->setSpecular(Color4f(1,1,1,1));
//set the beacon
pLight->setBeacon(someNodePtr);
endEditCP (pLight, PointLight::PositionFieldMask |
PointLight::ConstantAttenuationFieldMask |
PointLight::LinearAttenuationFieldMask |
PointLight::QuadraticAttenuationFieldMask |
PointLight::DiffuseFieldMask |
PointLight::AmbientFieldMask |
PointLight::SpecularFieldMask |
PointLight::BeaconFieldMask);
DirectionalLightPtr dLight = DirectionalLight::create();
beginEditCP(dLight);
//color information
dLight->setDiffuse(Color4f(1,1,1,1));
dLight->setAmbient(Color4f(0.2,0.2,0.2,1));
dLight->setSpecular(Color4f(1,1,1,1));
//set the beacon
dLight->setBeacon(someNodePtr);
endEditCP (dLight);
SpotLightPtr sLight = SpotLight::create();
beginEditCP(sLight);
//set how fast light intesity decreases at the border
sLight->setSpotExponent(2);
//set the opening angle
sLight->setSpotCutOff(deg2rad(30));
//color information
sLight->setDiffuse(Color4f(1,1,1,1));
sLight->setAmbient(Color4f(0.2,0.2,0.2,1));
sLight->setSpecular(Color4f(1,1,1,1));
//set the beacon
sLight->setBeacon(someNodePtr);
endEditCP (sLight);
As mentioned above, spot lights are really expensive and should be used with caution!
Light setup
This is a typical setup if you want to move the light source independent of the rest of the scene. But the beacon is not limited in any way, it can be an arbitrary Node in the graph, including nodes below the light source or even the light source itself.
The main motivation for specifying the position/orientation of the lights via a beacon Node and not via for example a Matrix is consistency and automatisms. This approach hides the difference between light sources and other Nodes for purposes of manipulation and animation. Light sources are manipulated exactly like any other node, by changing transformations on a Transform or ComponentTransform node. This also allows passive changes in the sense that a light source can be literally attached to another object and move whenever it moves. All just by setting the beacon.
For the attachment to another object situation the current setup is still somewhat limited. Given that the position and orientation of the light source is defined by constant values in the coordinate system of the beacon, you'd have to add temporary transformations to move the light source inside this coordinate system, for example to move the two headlights of a car to the correct position within the car. To simplify these operations, the light sources actually do have position and direction Fields, as applicable to the give type of light source. But you still need to set a beacon to define the reference coordinate system. If in doubt, just use the light source's Node as a beacon.
Just to mention it: The camera uses the same beacon mechanism to define the position and orientation of the viewer. This unifies moving and animation of all relevant objects in the scenegraph to changing Transformation nodes.
Another comment: the SimpleSceneManager by default provides a directional light set up as a headlight. That is the reason why we didn't have a black sceen in the previous tutorials, although we had not defined a single light source.
Note: Right now the limitation of the area of influence is not implemented yet, a light source will also influence its siblings, not only its children. It is still stringly recommended to follow the above mentioned screen setup, otherwise things will break once the area feature is implemented, and even today unexpected effects can appear due to culling of light sources. So do it right from the beginning and you never have to worry.
LightPtr l = roomlight;
beginEditCP(l, Light::OnFieldMask);
l->setOn(false); // nighty night!
endEditCP(l, Light::OnFieldMask);
Attenuation parameters
The image to the very left has only a very small factor for constant attenuation (0.1). Attenuation of the intensity is not really visible as the plane seems equally lit. The image in the middle has some reasonable values where as the right image has a quadric attenuation value of one resulting in a much too dark image.
You see that some caution is advised, else you might end up with a blank screen. After hours of search for the error you might begin to dislike these parameters... so keep it in mind ;-)
mgr->setHeadlight(false);
If you execute the application you will notice that nothing has really changed. That is because of the material we defined previously. If you look what we have done before: We simply created a SimpleMaterial and assigned it to the geometry without providing any additional information. The material we created in that way is completly unaffected by light sources. It is equally lit from every direction. So first thing we do is to assign a correct material:
in the createScenegraph() function locate the line that says
SimpleMaterialPtr mat = SimpleMaterial::create();
and add the following code right below that line
beginEditCP(mat);
mat->setDiffuse(Color3f(1,0,0));
endEditCP(mat);
When executing we see, as expected, nothing, because our only geometry is rendered black, as there is no light source left to do the job! If you turn the headlight back on, you will see that the water mesh is blue and not red as the material's diffuse color we just set. Why that? If you look a few lines upwards you might remember that we provided every single vertex with a specific color - blue. Remove the block where the GeoColor3f property is created as well as the line that says
geo->setColors(colors);
If you saw nothing but a black screen, you forgot to turn the headlight back on. Anyway, turn the headlight off, as we want to add our own light sources now. We start with the most common a point light.
At first add two new headers
#include <OpenSG/OSGPointLight.h> #include <OpenSG/OSGSpotLight.h>
Locate the following block of code right at the very end of the createScenegraph() function
NodePtr root = Node::create();
beginEditCP(root);
root->setCore(geo);
endEditCP(root);
and replace the entire block with the following code
PointLightPtr pLight = PointLight::create();
NodePtr root = Node::create();
NodePtr water = Node::create();
NodePtr pLightTransformNode = Node::create();
TransformPtr pLightTransform = Transform::create();
NodePtr pLightNode = Node::create();
beginEditCP(pLightNode);
pLightNode->setCore(Group::create());
endEditCP(pLightNode);
Matrix m;
m.setIdentity();
m.setTranslate(50,25,50);
beginEditCP(pLightTransform);
pLightTransform->setMatrix(m);
endEditCP(pLightTransform);
//we add a little sphere that will represent the light source
GeometryPtr sphere = makeSphereGeo(2,2);
SimpleMaterialPtr sm = SimpleMaterial::create();
beginEditCP(sm, SimpleMaterial::DiffuseFieldMask |
SimpleMaterial::LitFieldMask);
{
sm->setLit(false);
sm->setDiffuse(Color3f(1,1,1));
}
endEditCP (sm, SimpleMaterial::DiffuseFieldMask |
SimpleMaterial::LitFieldMask);
beginEditCP(sphere, Geometry::MaterialFieldMask);
{
sphere->setMaterial(sm);
}
endEditCP (sphere, Geometry::MaterialFieldMask);
NodePtr sphereNode = Node::create();
beginEditCP(sphereNode);
sphereNode->setCore(sphere);
endEditCP(sphereNode);
beginEditCP(pLightTransformNode);
pLightTransformNode->setCore(pLightTransform);
pLightTransformNode->addChild(pLightNode);
pLightTransformNode->addChild(sphereNode);
endEditCP(pLightTransformNode);
beginEditCP(pLight);
pLight->setPosition(Pnt3f(0,0,0));
//Attenuation parameters
pLight->setConstantAttenuation(1);
pLight->setLinearAttenuation(0);
pLight->setQuadraticAttenuation(0);
//color information
pLight->setDiffuse(Color4f(1,1,1,1));
pLight->setAmbient(Color4f(0.2,0.2,0.2,1));
pLight->setSpecular(Color4f(1,1,1,1));
//set the beacon
pLight->setBeacon(pLightNode);
endEditCP (pLight);
beginEditCP(water);
water->setCore(geo);
endEditCP(water);
beginEditCP(root);
root->setCore(pLight);
root->addChild(water);
root->addChild(pLightTransformNode);
endEditCP(root);
If you run the application it will crash and the terminal tells you something about a "segmentation fault". I personally do not like this error, because the only thing you are told by this error is, that something really does not work! So what can it be? When you have a look at the display function you see right at the beginning that we are accessing the geometry core of the scene node. That exacly is our problem! We need the geometry pointer to modify the geometry and we assumed that the core of the root node holds the geometry core. Well that was correct, until we changed the design of the scenegraph. Now it is the one and only child of the root node which holds the geometry. Change the line that says
GeometryPtr geo = GeometryPtr::dcast(scene->getCore());
to
GeometryPtr geo = GeometryPtr::dcast(scene->getChild(0)->getCore());
Now you should see blue and lit water. The blue is not constant any more, you can see that the borders are in a darker blue that the center. This is because the light source is floating right above the center and there the distance is the shortest of course. Now we would have to calculate correct normals in order to render a more realistic result.
Water correct lit with incorrect normals
The normals need to be computed every frame as they change steadily. This is a problem because computation of normals is quite expensive. However this is not our concern for now. If you like to, you can try to compute the normals. You could also use the calcVertexNormals() utility function I introduced in the last chapter, but I warned you, that this will slow down the simulation significantly. calcVertexNormals() is very generic and can handle some pretty nasty situations, but that takes time. My Linux box with 1400 Mhz can't do the job in real time!
The code so far can be found in the file progs/10water_lit.cpp. We will now replace the point light with a directional light. If you have not completed the last part of the tutorial you can take the file mentioned above as a starting point!
Replace the line
PointLightPtr pLight = PointLight::create();
DirectionalLightPtr pLight = DirectionalLight::create();
Just to mention it: We need not to include another header file for the directional light. We have used the simple scene manager which comes along with a default light source and thus includes automatically the header for directional light sources.
Next locate and remove the following lines
pLight->setPosition(Pnt3f(0,0,0));
//Attenuation parameters
pLight->setConstantAttenuation(1);
pLight->setLinearAttenuation(0);
pLight->setQuadraticAttenuation(0);
and add that line, but make sure it is within the begin-/ endEditCP(pLight) block!
pLight->setDirection (Vec3f(0,1,0));
I hope you wonder why the direction of the light is straight "up" into the sky and not the other way round, because I still wonder, too. Maybe I have overseen or missunderstood something, but to me it seems that the direction of a directional light is always inverse. So I am not quite sure, if it is a bug or a feature ;-) Anyway if you have a hint for me, fell free to send me a mail!
Water lit by directional light with wrong normals
If you now compare this result with the last one, where we used a point light you will notice the difference in shading. This one here is again uniformly shaded, whereas the point light result looked better. Well, you can now think about what happened... this question can be found again right down here in the exercises part ;-)
Next Chapter : Windows and Viewports
1.4.3