scene_lighting - ryzom/ryzomcore GitHub Wiki
title: Scene lighting description: Add dynamic point lights and configure the sun in a scene published: true date: 2026-03-14T00:00:00.000Z tags: editor: markdown dateCreated: 2026-03-14T00:00:00.000Z
In the previous tutorial we loaded a shape into a scene with a simple directional light. Now we'll explore NeL's lighting system in more depth: configuring the scene's sun, creating dynamic point lights that move around the scene, and understanding the difference between driver-level and scene-level lighting.
This tutorial covers:
- Configuring the scene sun (ambient, diffuse, specular, direction)
- Creating and positioning
UPointLightinstances in the scene - Setting attenuation and color on point lights
- The difference between
ULight(driver) andUPointLight(scene graph)
NeL has two separate lighting mechanisms:
Driver-level lights (ULight) are set directly on the driver with setLight()/enableLight(). These apply globally when drawing with the driver's immediate-mode functions (drawQuad, etc.) or when no scene is active. They're simple but don't interact with the scene graph's visibility or attenuation systems.
Scene-graph lights are managed by the scene and come in two forms:
- The sun is a global directional light configured through
UScenemethods. It affects all objects in the scene. -
Point lights (
UPointLight) are created throughUScene::createPointLight(). They have position, color, and attenuation, and NeL automatically determines which objects they affect based on distance.
For scene-based rendering, use the scene sun and UPointLight. Reserve ULight for immediate-mode drawing or cases where you need to bypass the scene.
The sun is the primary light source and is configured directly on the scene:
// Enable the lighting system
m_Scene->enableLightingSystem(true);
// Global ambient light (affects everything uniformly)
m_Scene->setAmbientGlobal(NLMISC::CRGBA(40, 40, 50));
// Sun colors
m_Scene->setSunAmbient(NLMISC::CRGBA(40, 40, 50));
m_Scene->setSunDiffuse(NLMISC::CRGBA(255, 255, 240));
m_Scene->setSunSpecular(NLMISC::CRGBA(255, 255, 255));
// Sun direction (pointing down and to the side)
m_Scene->setSunDirection(NLMISC::CVector(1.f, 1.f, -2.f).normed());The ambient color provides a base level of light everywhere, simulating indirect illumination. The diffuse color is the main directional light. The specular color controls shiny highlights on materials that support it.
You can also limit how many dynamic lights affect each object:
m_Scene->setMaxLightContribution(3);A UPointLight is a scene-graph object with position, color, and attenuation range. It inherits from UTransform, so you position it using setPos():
#include <nel/3d/u_point_light.h> NL3D::UPointLight pointLight = m_Scene->createPointLight();
// Position the light
pointLight.setPos(NLMISC::CVector(2.f, 0.f, 3.f));
// Set colors
pointLight.setAmbient(NLMISC::CRGBA(0, 0, 0));
pointLight.setDiffuse(NLMISC::CRGBA(255, 100, 50));
pointLight.setSpecular(NLMISC::CRGBA(255, 200, 150));
// Attenuation: light fades from full at 'begin' to zero at 'end'
pointLight.setupAttenuation(1.f, 8.f);The setupAttenuation parameters define the distance range. Between attenuationBegin and attenuationEnd, the light intensity smoothly falls off to zero. Objects beyond attenuationEnd are not affected.
You can also set all three color channels at once with setColor(), which sets both diffuse and specular to the same value:
pointLight.setColor(NLMISC::CRGBA(100, 200, 255));Since UPointLight inherits from UTransform, you can move it each frame:
float lightAngle = m_Time * 2.f;
float lightRadius = 3.f;
m_PointLight.setPos(NLMISC::CVector(
cosf(lightAngle) * lightRadius,
sinf(lightAngle) * lightRadius,
2.f));You can also animate the color to create flickering or pulsing effects:
float pulse = (sinf(m_Time * 5.f) + 1.f) * 0.5f;
uint8 r = (uint8)(200.f * pulse + 55.f);
m_PointLight.setDiffuse(NLMISC::CRGBA(r, r / 3, r / 6));Delete point lights before deleting the scene:
m_Scene->deletePointLight(m_PointLight);This example loads a shape, sets up the sun, and adds two orbiting colored point lights.
#include <nel/misc/types_nl.h>
#include <nel/misc/app_context.h>
#include <nel/misc/event_listener.h>
#include <nel/misc/debug.h>
#include <nel/misc/time_nl.h>
#include <nel/misc/path.h>
#include <nel/3d/u_driver.h>
#include <nel/3d/u_scene.h>
#include <nel/3d/u_camera.h>
#include <nel/3d/u_instance.h>
#include <nel/3d/u_point_light.h>
using namespace std;
using namespace NLMISC;
class CMyGame : public IEventListener
{
public:
CMyGame();
~CMyGame();
void run();
virtual void operator()(const CEvent &event) NL_OVERRIDE;
private:
NL3D::UDriver *m_Driver;
NL3D::UScene *m_Scene;
NL3D::UInstance m_Entity;
NL3D::UPointLight m_Light0;
NL3D::UPointLight m_Light1;
bool m_CloseWindow;
double m_LastTime;
float m_Time;
float m_CamAngle;
};
CMyGame::CMyGame()
: m_CloseWindow(false)
, m_Scene(NULL)
, m_Time(0.f)
, m_CamAngle(0.f)
{
m_Driver = NL3D::UDriver::createDriver(0, NL3D::UDriver::OpenGl3);
if (!m_Driver) { nlerror("Failed to create driver"); return; }
m_Driver->EventServer.addListener(EventCloseWindowId, this);
m_Driver->setDisplay(NL3D::UDriver::CMode(800, 600, 32));
m_Driver->setWindowTitle("Scene Lighting");
CPath::addSearchPath("data", true, false);
CPath::remapExtension("dds", "tga", true);
// Create scene
m_Scene = m_Driver->createScene(true);
if (!m_Scene) { nlerror("Failed to create scene"); return; }
// Camera
NL3D::UCamera cam = m_Scene->getCam();
cam.setTransformMode(NL3D::UTransformable::DirectMatrix);
cam.setPerspective(float(Pi / 3.0), 800.f / 600.f, 0.1f, 1000.f);
cam.lookAt(CVector(0.f, -6.f, 3.f), CVector(0.f, 0.f, 0.f));
// Configure the sun
m_Scene->enableLightingSystem(true);
m_Scene->setAmbientGlobal(CRGBA(30, 30, 40));
m_Scene->setSunAmbient(CRGBA(30, 30, 40));
m_Scene->setSunDiffuse(CRGBA(140, 140, 160));
m_Scene->setSunSpecular(CRGBA(200, 200, 200));
m_Scene->setSunDirection(CVector(1.f, 1.f, -2.f).normed());
m_Scene->setMaxLightContribution(3);
// Create two orbiting point lights
m_Light0 = m_Scene->createPointLight();
m_Light0.setAmbient(CRGBA(0, 0, 0));
m_Light0.setDiffuse(CRGBA(255, 120, 50));
m_Light0.setSpecular(CRGBA(255, 200, 150));
m_Light0.setupAttenuation(1.f, 8.f);
m_Light0.setPos(CVector(3.f, 0.f, 2.f));
m_Light1 = m_Scene->createPointLight();
m_Light1.setAmbient(CRGBA(0, 0, 0));
m_Light1.setDiffuse(CRGBA(50, 120, 255));
m_Light1.setSpecular(CRGBA(150, 200, 255));
m_Light1.setupAttenuation(1.f, 8.f);
m_Light1.setPos(CVector(-3.f, 0.f, 2.f));
// Load a shape
m_Entity = m_Scene->createInstance("sphere01.shape");
if (!m_Entity.empty())
m_Entity.setPos(CVector(0.f, 0.f, 0.f));
m_LastTime = CTime::ticksToSecond(CTime::getPerformanceTime());
}
CMyGame::~CMyGame()
{
if (!m_Entity.empty()) m_Scene->deleteInstance(m_Entity);
if (!m_Light0.empty()) m_Scene->deletePointLight(m_Light0);
if (!m_Light1.empty()) m_Scene->deletePointLight(m_Light1);
if (m_Scene) m_Driver->deleteScene(m_Scene);
m_Driver->release();
delete m_Driver;
}
void CMyGame::operator()(const CEvent &event)
{
if (event == EventCloseWindowId)
m_CloseWindow = true;
}
void CMyGame::run()
{
while (m_Driver->isActive() && !m_CloseWindow)
{
m_Driver->EventServer.pump();
double now = CTime::ticksToSecond(CTime::getPerformanceTime());
float dt = float(now - m_LastTime);
m_LastTime = now;
m_Time += dt;
m_CamAngle += dt * 0.2f;
// Orbit camera
CVector eye(cosf(m_CamAngle) * 6.f, sinf(m_CamAngle) * 6.f, 3.f);
m_Scene->getCam().lookAt(eye, CVector(0.f, 0.f, 0.f));
// Orbit the point lights in opposite directions
float r = 3.f;
m_Light0.setPos(CVector(cosf(m_Time) * r, sinf(m_Time) * r, 2.f));
m_Light1.setPos(CVector(cosf(m_Time + float(Pi)) * r,
sinf(m_Time + float(Pi)) * r, 1.5f));
m_Driver->clearBuffers(CRGBA(10, 10, 15));
m_Scene->animate(CTime::getLocalTime() / 1000.0);
m_Scene->render();
m_Driver->swapBuffers();
}
}
int main(int argc, char *argv[])
{
CApplicationContext applicationContext;
CMyGame myGame;
myGame.run();
return EXIT_SUCCESS;
}To run this, place a shape file (e.g. sphere01.shape from nel/samples/3d/cluster_viewer/shapes/) in a data directory next to the executable.
In the next tutorial, you will learn how to play animations on a skeleton.
- Have a look at the complete list of NeL tutorials.