Lysa  0.0
Lysa 3D Engine
Lights

RotatingAssetScene.ixx : light declarations

Two lights are declared as member fields of RotatingAssetScene so that they live for the full lifetime of the scene. Lysa supports three light types: LIGHT_DIRECTIONAL, LIGHT_OMNI (point), and LIGHT_SPOT.

Light type summary

Type Transform usage Shadow support
LIGHT_DIRECTIONAL Rotation only : position has no effect Cascaded shadow maps (CSM)
LIGHT_OMNI Translation only : defines the point-light origin Omnidirectional cube shadow map
LIGHT_SPOT Full transform : position + direction Single shadow map

All light types share the color (RGB) and intensity (scalar multiplier) fields. The intensity value is not clamped; values greater than 1 are valid for physically-based lighting workflows.

Directional light

lysa::Light directionalLight{
/*color*/ {1.00f, 0.95f, 0.85f},
/*intensity*/ 1.5f,
lysa::float4x4::rotation_x(lysa::radians(-50.0f)),
lysa::float4x4::rotation_y(lysa::radians(35.0f)),
lysa::float4x4::translation(0.0f, 0.0f, 0.0f))),
};

The warm-white directional light comes from the upper-right (−50° pitch, +35° yaw). At intensity 1.5 it is the dominant light source. The transform encodes only direction for a directional light; its translation component has no effect on shading but it is still included here as a placeholder for clarity.

The rotation is composed by multiplying individual axis-rotation matrices. Matrix multiplication order in Lysa follows row-vector convention: mul(A, B) applies A first, then B. When building a compound rotation the outer matrix (rotation_x) is applied after the inner (rotation_y).

Omni light

lysa::Light omniLight{
/*color*/ {0.30f, 0.40f, 0.60f},
/*intensity*/ 0.6f,
lysa::float4x4::translation(0.0f, -2.0f, 3.0f),
/*range*/ 12.0f,
};

The cool-blue omni light sits below and slightly in front of the model. At intensity 0.6 and range 12 it softens the hard shadow cast by the directional light without overriding it. Omni lights are attenuated over distance using the inverse-square law; surfaces beyond range receive no contribution. Setting range too large wastes shader cycles evaluating lights that contribute negligibly to distant surfaces.

Registering lights

Lights are registered in the constructor after shadow configuration:

directionalLight.castShadows = true;
directionalLight.shadowMapSize = 2048;
directionalLight.shadowMapCascadesCount = 3;
directionalLight.shadowMapCascadesSplitLambda = 0.75f;
addLight(directionalLight);
addLight(omniLight);

Cascaded shadow maps (CSM) are configured on the directional light:

Field Value Meaning
castShadows true Enables shadow map generation for this light
shadowMapSize 2048 Per-cascade shadow map resolution in texels
shadowMapCascadesCount 3 Number of CSM cascades
shadowMapCascadesSplitLambda 0.75 Split blend: 0 = uniform, 1 = logarithmic

A lambda of 0.75 biases toward a logarithmic split, concentrating shadow detail in the region close to the camera. Higher values of shadowMapSize produce sharper shadows but multiply VRAM usage (e.g. three cascades at 2048 use 3 × 2048² texels of depth memory).

The omni light does not cast shadows in this sample. Enabling omni shadows adds a cube shadow map (six faces) per light, which is expensive; reserve it for lights that are close to large opaque surfaces where the shadow contribution is clearly visible.

Note
Lights must remain alive for as long as the scene uses them. Declaring them as member fields of the scene class is the simplest way to guarantee their lifetime. Calling removeLight is only necessary if you want to disable a light dynamically at runtime.

Next : Rotation