I used to have hard time to set up my scene lights properly. It was mostly because I did not understood how attenuation works. It is also I think, because default ILightSceneNode attributes created by ISceneManager::addLightSceneNode() are not set very well.
SLight::Attenuation have three members(factors): constant, linear and quadratic.
Those are set by default to 0, 0.01, 0 in ILightSceneNode created by ISceneManager. Now formula which calculates light in OpenGL is this:
Code: Select all
attentuation = 1.0 / (constantAttenuation + linearAttenuation * distance + quadraticAttenuation * distance * distance);
final colour = colour * attenuation;With preset attributes we can see that attenuation at some distances can actually be larger that 1 (1 / 0.01*10 = 10 for example), multiplicating all other effects in process. This cause object at those distances bee lit by too strong light (those are near distances by default).
Attenuation should cause light effect to fade away with distance but never to increase its strength beyond values set in diffuse specular and ambient attributes. This of course requires, that attenuation is newer larger than 1. Given formula above it means that sum of constant, linear and quadratic attributes should newer be smaller than 1. If this sum is exactly 1, light do not fade over distance and is constant. So 1 should be default sum of those attributes.
My suggestion is that constant attenuation should be set at 1 by default as it is most logical value in relation to other two. If for example linear attenuation is then set to 0.01 (as it is now per default) it will cause light to decrease by 1% per unit of distance which is what would user expect I think.
Here is example I set up to demonstrate problem. Comment/uncomment line ldata->Attenuation.X = 1.f; to see the difference:
Code: Select all
#include <irrlicht.h>
using namespace irr;
int main()
{
IrrlichtDevice *device = createDevice(video::EDT_OPENGL,
core::dimension2d<u32>(640, 480), 16, false, false, false, 0);
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
// sphere
scene::ISceneNode *sphere = smgr->addSphereSceneNode(5.f, 32);
sphere->setPosition(core::vector3df(0.f,0.f,7.f));
// light position marker
scene::ISceneNode *lightMarker = smgr->addSphereSceneNode(0.2f);
lightMarker->setMaterialFlag(video::EMF_WIREFRAME, true);
lightMarker->setMaterialFlag(video::EMF_LIGHTING, false);
// camera
scene::ICameraSceneNode* camera = smgr->addCameraSceneNode();
camera->setPosition(core::vector3df(10.f, 0.f, -5.f));
// light
scene::ILightSceneNode *light = smgr->addLightSceneNode();
video::SLight *ldata = &light->getLightData();
//ldata->Attenuation.X = 1.f;
// some text
gui::IGUIStaticText *text1 =
guienv->addStaticText(0, core::rect<s32>(10,10,500,30));
gui::IGUIStaticText *text2 =
guienv->addStaticText(0, core::rect<s32>(10,30,500,60));
u32 oldTime = 1000;
while(device->run())
{
driver->beginScene(true, true, video::SColor(0,100,100,120));
// move sphere away afther 1 second
u32 time = device->getTimer()->getTime();
if(time > 1000)
{
u32 deltaT = time - oldTime;
if(deltaT) oldTime = time;
sphere->setPosition(sphere->getPosition() +
core::vector3df(0.f, 0.f, 0.005f * deltaT));
}
// point camera at sphere
camera->setTarget(sphere->getPosition());
// set text
core::stringw str(L"Constant Attenuation = ");
str += ldata->Attenuation.X;
str += L" Linear Attenuation = ";
str += ldata->Attenuation.Y;
str += L" Quadratic Attenuation = ";
str += ldata->Attenuation.Z;
text1->setText(str.c_str());
str = L"Distance: ";
str += (s32)(sphere->getPosition()-light->getPosition()).getLength();
text2->setText(str.c_str());
smgr->drawAll();
guienv->drawAll();
driver->endScene();
}
device->drop();
return 0;
}