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 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
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);
3D position of the light source
Diffuse color of the light
Maximum distance the light reaches
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);
3D position of the spotlight
Direction the spotlight is pointing
Inner cone angle (cosine of angle in radians)
Outer cone angle for smooth falloff
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);
Center position of the rectangular light
Oriented axis for width direction (normalized)
Oriented axis for height direction (normalized)
Width and height of the rectangle
Emission cone half-angle in degrees around plane normal
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