Skip to main content

Overview

Atlas supports multiple light types with real-time shadow mapping and GPU-optimized buffer structures for efficient rendering.

Light Types

DirectionalLight

Simulates distant light sources like the sun. Emits parallel rays in a specific direction.
#include "atlas/light.h"

// Create directional light pointing downward
DirectionalLight dirLight(
    {0.0f, -1.0f, 0.0f},  // Direction (normalized)
    Color::white(),        // Diffuse color
    Color::white(),        // Specular color
    1.0f                   // Intensity
);

// Configure color
dirLight.setColor(Color(1.0f, 0.95f, 0.8f));  // Warm sunlight
dirLight.intensity = 1.5f;

// Enable shadow casting
dirLight.castShadows(window, 4096);  // 4096x4096 shadow map

// Add to scene
scene.addDirectionalLight(&dirLight);
direction
Magnitude3d
Direction in which the light is pointing (automatically normalized)
color
Color
default:"Color::white()"
Diffuse color of the light
shineColor
Color
default:"Color::white()"
Specular highlight color
intensity
float
default:"1.0f"
Light intensity multiplier

PointLight (Light)

Emits light in all directions from a single point in space.
// Create point light at position
Light pointLight(
    {10.0f, 5.0f, 0.0f},  // Position
    Color::white(),        // Color
    50.0f,                 // Distance/radius
    Color::white(),        // Shine color
    1.0f                   // Intensity
);

// Set warm color
pointLight.setColor(Color(1.0f, 0.9f, 0.7f));
pointLight.intensity = 2.0f;

// Enable omnidirectional shadows
pointLight.castShadows(window, 2048);

// Add debug visualization
pointLight.createDebugObject();
pointLight.addDebugObject(window);

// Add to scene
scene.addPointLight(&pointLight);
position
Position3d
3D position of the light source
color
Color
Diffuse color of the light
distance
float
default:"50.0f"
Maximum distance the light reaches
shineColor
Color
Specular highlight color
intensity
float
Light intensity multiplier

Attenuation

Point lights use physical attenuation:
struct PointLightConstants {
    float distance;   // Max distance
    float constant;   // Constant attenuation
    float linear;     // Linear attenuation
    float quadratic;  // Quadratic attenuation
    float radius;     // Derived radius of influence
};

PointLightConstants constants = pointLight.calculateConstants();

SpotLight

Emits light in a cone from a position with inner and outer angles.
// Create spotlight
Spotlight spotLight(
    {0.0f, 10.0f, 0.0f},   // Position
    {0.0f, -1.0f, 0.0f},   // Direction
    Color::white(),         // Color
    30.0f,                  // Inner angle (degrees)
    35.0f,                  // Outer angle (degrees)
    Color::white(),         // Shine color
    1.0f,                   // Intensity
    50.0f                   // Range
);

// Configure appearance
spotLight.setColor(Color(0.7f, 0.8f, 1.0f));  // Cool blue
spotLight.intensity = 3.0f;

// Make it look at a target
spotLight.lookAt({0.0f, 0.0f, 0.0f});

// Enable shadow mapping
spotLight.castShadows(window, 2048);

// Add debug visualization
spotLight.createDebugObject();
spotLight.addDebugObject(window);

// Add to scene
scene.addSpotlight(&spotLight);
position
Position3d
3D position of the spotlight
direction
Magnitude3d
Direction the spotlight is pointing
cutOff
float
Inner cone angle (cosine of angle in radians)
outerCutoff
float
Outer cone angle for smooth falloff
range
float
default:"50.0f"
Maximum distance the light reaches

AreaLight

Rectangular area light with controllable emission angle and two-sided emission.
// Create area light
AreaLight areaLight;
areaLight.position = {0.0f, 2.0f, 0.0f};
areaLight.size = {2.0f, 1.0f};  // Width x Height
areaLight.setColor(Color(1.0f, 0.9f, 0.4f));  // Warm light
areaLight.intensity = 1.0f;
areaLight.range = 5.0f;

// Configure orientation
areaLight.rotate({90.0f, 0.0f, 0.0f});  // Rotate to face down

// Enable two-sided emission
areaLight.castsBothSides = true;

// Set emission angle
areaLight.angle = 90.0f;  // Hemisphere emission

// Add debug visualization
areaLight.createDebugObject();
areaLight.addDebugObject(window);

// Add to scene
scene.addAreaLight(&areaLight);
position
Position3d
Center position of the rectangular light
right
Magnitude3d
Oriented axis for width direction (normalized)
up
Magnitude3d
Oriented axis for height direction (normalized)
size
Size2d
Width and height of the rectangle
angle
float
default:"90.0f"
Emission cone half-angle in degrees around plane normal
castsBothSides
bool
default:"false"
If true, light emits on both sides of the rectangle

Ambient Light

Global illumination applied to all objects:
// Set ambient light intensity
scene.setAmbientIntensity(0.2f);

// Or configure manually
AmbientLight ambient;
ambient.color = Color(0.1f, 0.1f, 0.15f);  // Cool ambient
ambient.intensity = 0.3f;

Shadow Mapping

Enabling Shadows

All light types support shadow mapping:
// Directional light (cascade shadow maps)
dirLight.castShadows(window, 4096);  // High resolution

// Point light (cubemap shadows)
pointLight.castShadows(window, 2048);

// Spotlight (perspective shadow map)
spotLight.castShadows(window, 2048);

// Area light (perspective shadow map)
areaLight.castShadows(window, 2048);

Shadow Parameters

struct ShadowParams {
    glm::mat4 lightView;        // View matrix from light
    glm::mat4 lightProjection;  // Projection matrix
    float bias;                 // Shadow acne prevention
    float farPlane;             // Far plane (point lights)
};
Access shadow parameters:
ShadowParams shadowParams = dirLight.lastShadowParams;
float shadowBias = shadowParams.bias;

GPU Buffer Structures

Atlas uses aligned GPU structures for efficient light data transfer:
// GPU-compatible directional light (matches GLSL layout)
struct alignas(16) GPUDirectionalLight {
    glm::vec3 direction;
    float _pad1;
    glm::vec3 diffuse;
    float _pad2;
    glm::vec3 specular;
    float intensity;
};

// GPU-compatible point light
struct alignas(16) GPUPointLight {
    glm::vec3 position;
    float _pad1;
    glm::vec3 diffuse;
    float _pad2;
    glm::vec3 specular;
    float intensity;
    float constant;
    float linear;
    float quadratic;
    float radius;
    float _pad3;
};

// GPU-compatible spotlight
struct alignas(16) GPUSpotLight {
    glm::vec3 position;
    float _pad1;
    glm::vec3 direction;
    float cutOff;
    float outerCutOff;
    float intensity;
    float range;
    float _pad4;
    glm::vec3 diffuse;
    float _pad5;
    glm::vec3 specular;
    float _pad6;
};

// GPU-compatible area light
struct alignas(16) GPUAreaLight {
    glm::vec3 position;
    float _pad1;
    glm::vec3 right;
    float _pad2;
    glm::vec3 up;
    float _pad3;
    glm::vec2 size;
    float _pad4;
    float _pad5;
    glm::vec3 diffuse;
    float _pad6;
    glm::vec3 specular;
    float angle;
    int castsBothSides;
    float intensity;
    float range;
    float _pad9;
};
These structures are used with Pipeline::bindBuffer() for efficient GPU uploads.

Complete Example

From the test suite:
class MainScene : public Scene {
    DirectionalLight light;
    AreaLight areaLight;
    Camera camera;

public:
    void initialize(Window &window) override {
        // Configure environment
        Environment env;
        env.fog.intensity = 0.0;
        env.volumetricLighting.enabled = false;
        env.lightBloom.radius = 0.008f;
        env.lightBloom.maxSamples = 5;
        this->setEnvironment(env);

        // Setup camera
        camera.setPosition({-5.0f, 1.0f, 0.0f});
        camera.lookAt({0.0f, 1.0f, 0.0f});
        camera.farClip = 1000.0f;
        window.setCamera(&camera);

        // Create area light
        areaLight = AreaLight();
        areaLight.position = {0.0f, 2.0f, 0.0f};
        areaLight.setColor(Color(1.0f, 0.9f, 0.4f));  // Warm color
        areaLight.intensity = 1.0f;
        areaLight.range = 5.0f;
        this->addAreaLight(&areaLight);
        areaLight.rotate({90.0f, 0.0f, 0.0f});
        areaLight.createDebugObject();
        areaLight.addDebugObject(window);

        // Set ambient
        this->setAmbientIntensity(0.0f);
    }
};

Deferred Rendering

Atlas supports deferred rendering for multiple lights:
Window window({
    .title = "Deferred Rendering",
    .width = 1920,
    .height = 1080
});

window.usesDeferred = true;  // Enable deferred shading
With deferred rendering, you can have many lights without performance degradation.

Best Practices

Use deferred rendering for scenes with 10+ lights. Forward rendering works well for 1-4 lights.
Use higher resolutions (4096) for directional lights, lower (1024-2048) for point/spot lights based on importance.
Set appropriate distance/range values to avoid wasted computation on distant surfaces.
Use createDebugObject() during development to visualize light positions and ranges.
Use warm colors (yellow/orange) for artificial lights, cool colors (blue) for moonlight.

See Also

Build docs developers (and LLMs) love