Lighting
Lighting system with directional, point, and spot lights, cascaded shadow maps, environment mapping, and light probes.
Web Engine's lighting system provides realistic illumination using a combination of direct lights, shadows, environment maps, and global illumination techniques. Built on Three.js, it supports all standard light types with advanced shadow mapping.
Light Types#
Directional Light
Parallel rays from infinite distance, like the sun
Point Light
Omnidirectional light from a single point
Spot Light
Cone-shaped light with distance falloff
Ambient Light
Uniform light from all directions
Hemisphere Light
Sky/ground color gradient
Area Light
Rectangular light source (rect area only)
Directional Light#
Directional lights simulate the sun or moon, with parallel rays from an infinite distance. Perfect for outdoor scenes.
import * as THREE from 'three'; // Create directional light (sun)const sunLight = new THREE.DirectionalLight( 0xffffff, // Color (white) 1.0 // Intensity); // Position (direction is from position to target)sunLight.position.set(50, 100, 50);sunLight.target.position.set(0, 0, 0); // Enable shadowssunLight.castShadow = true; // Configure shadow camera (orthographic frustum)sunLight.shadow.camera.left = -100;sunLight.shadow.camera.right = 100;sunLight.shadow.camera.top = 100;sunLight.shadow.camera.bottom = -100;sunLight.shadow.camera.near = 0.5;sunLight.shadow.camera.far = 500; // Shadow map resolutionsunLight.shadow.mapSize.width = 2048;sunLight.shadow.mapSize.height = 2048; // Shadow bias to reduce artifactssunLight.shadow.bias = -0.0001; scene.add(sunLight);scene.add(sunLight.target);Shadow Camera Size
The shadow camera bounds (left, right, top, bottom) determine the shadow map coverage. Make them large enough to cover your scene, but not too large or shadow quality will suffer.
Point Light#
Point lights emit light in all directions from a single point, like a light bulb. They support distance falloff and shadows.
// Create point lightconst pointLight = new THREE.PointLight( 0xff6600, // Orange color 1.0, // Intensity 100, // Distance (0 = infinite) 2.0 // Decay factor (physically correct = 2)); pointLight.position.set(10, 5, 0); // Enable shadows (uses cubemap)pointLight.castShadow = true;pointLight.shadow.mapSize.width = 512;pointLight.shadow.mapSize.height = 512;pointLight.shadow.camera.near = 0.5;pointLight.shadow.camera.far = 100; scene.add(pointLight); // Optional: Add visual helperconst helper = new THREE.PointLightHelper(pointLight, 0.5);scene.add(helper);Point Light Shadows
Point light shadows require rendering the scene 6 times (one for each cubemap face), making them expensive. Use sparingly or disable shadows for distant point lights.
Spot Light#
Spot lights emit a cone of light, like a flashlight or stage light. They support angle, penumbra, and distance falloff.
// Create spot lightconst spotLight = new THREE.SpotLight( 0xffffff, // Color 1.0, // Intensity 100, // Distance Math.PI / 4, // Angle (45 degrees) 0.5, // Penumbra (soft edge) 2.0 // Decay); spotLight.position.set(0, 10, 0);spotLight.target.position.set(0, 0, 0); // Enable shadowsspotLight.castShadow = true;spotLight.shadow.mapSize.width = 1024;spotLight.shadow.mapSize.height = 1024;spotLight.shadow.camera.near = 1;spotLight.shadow.camera.far = 100;spotLight.shadow.camera.fov = 45; scene.add(spotLight);scene.add(spotLight.target);Ambient and Hemisphere Lights#
Ambient lights provide uniform illumination from all directions, while hemisphere lights create a sky/ground color gradient.
// Ambient light (uniform)const ambientLight = new THREE.AmbientLight( 0x404040, // Dark gray 0.5 // Intensity);scene.add(ambientLight); // Hemisphere light (sky/ground gradient)const hemisphereLight = new THREE.HemisphereLight( 0x87ceeb, // Sky color (light blue) 0x543210, // Ground color (brown) 0.6 // Intensity);hemisphereLight.position.set(0, 50, 0);scene.add(hemisphereLight);Shadow Mapping#
Web Engine uses shadow mapping to render realistic shadows. Configure shadow properties for quality and performance:
| Property | Effect | Recommended |
|---|---|---|
| mapSize | Shadow resolution | 1024-2048 for directional, 512 for point/spot |
| bias | Reduce shadow acne | -0.0001 to -0.001 |
| normalBias | Reduce peter panning | 0 to 0.05 |
| radius | Blur amount (PCF) | 1-3 for soft shadows |
| camera.near/far | Shadow depth range | Match scene bounds |
// Enable shadows on rendererrenderer.shadowMap.enabled = true;renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Soft shadows // Mark objects to cast/receive shadowsmesh.castShadow = true;mesh.receiveShadow = true; // Configure light shadowslight.castShadow = true;light.shadow.bias = -0.0001;light.shadow.normalBias = 0.02;light.shadow.radius = 2;light.shadow.mapSize.set(2048, 2048);Shadow Map Types#
| Type | Quality | Performance | Use Case |
|---|---|---|---|
| BasicShadowMap | Low (hard edges) | Fast | Mobile/low-end |
| PCFShadowMap | Medium (soft) | Medium | Default |
| PCFSoftShadowMap | High (very soft) | Slow | Desktop/high-end |
| VSMShadowMap | High (soft + blur) | Slow | Advanced effects |
Cascaded Shadow Maps (CSM)#
CSM provides high-quality shadows at all distances by splitting the camera frustum into multiple cascades, each with its own shadow map:
import { Environment } from '@web-engine-dev/core/engine/ecs/components'; // Enable CSM in the environmentconst envEntity = createEntity(world);addComponent(world, envEntity, Environment); // Configure CSMEnvironment.csmEnabled[envEntity] = 1; // Enable CSMEnvironment.csmIntensity[envEntity] = 1.0; // Shadow strength // Sun position (light direction)Environment.sunPosition[envEntity][0] = 50; // xEnvironment.sunPosition[envEntity][1] = 100; // yEnvironment.sunPosition[envEntity][2] = 50; // z // CSM automatically:// - Creates 3 shadow cascades// - Uses 2048x2048 shadow maps per cascade// - Updates shadow cameras each frame// - Handles cascade transitions smoothlyCSM Performance
CSM renders the scene 3 times (once per cascade), but provides excellent shadow quality at all distances. The engine uses "practical" cascade distribution for balanced near/far quality.
Environment Maps#
Environment maps provide reflections and ambient lighting from the surrounding environment:
import * as THREE from 'three';import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'; // Load HDR environment mapconst rgbeLoader = new RGBELoader();rgbeLoader.load('environment.hdr', (texture) => { // Convert to cubemap for reflections const pmremGenerator = new THREE.PMREMGenerator(renderer); pmremGenerator.compileEquirectangularShader(); const envMap = pmremGenerator.fromEquirectangular(texture).texture; // Apply to scene scene.environment = envMap; // IBL lighting scene.background = envMap; // Skybox // Apply to materials material.envMap = envMap; material.envMapIntensity = 1.0; pmremGenerator.dispose(); texture.dispose();});Skybox#
Create a skybox using a cubemap or equirectangular texture:
// Cubemap skyboxconst cubeTextureLoader = new THREE.CubeTextureLoader();const skybox = cubeTextureLoader.load([ 'px.jpg', 'nx.jpg', // +x, -x 'py.jpg', 'ny.jpg', // +y, -y 'pz.jpg', 'nz.jpg', // +z, -z]); scene.background = skybox; // Equirectangular skyboxconst textureLoader = new THREE.TextureLoader();const equirect = textureLoader.load('sky.jpg');equirect.mapping = THREE.EquirectangularReflectionMapping; scene.background = equirect;Light Probes#
Light probes capture lighting information at specific points for image-based lighting:
import { LightProbeGenerator } from 'three/examples/jsm/lights/LightProbeGenerator.js'; // Create light probe from cubemapconst cubeCamera = new THREE.CubeCamera(0.1, 100, 256);cubeCamera.position.set(0, 5, 0);scene.add(cubeCamera); // Update cubemapcubeCamera.update(renderer, scene); // Generate light probeconst lightProbe = LightProbeGenerator.fromCubeRenderTarget( renderer, cubeCamera.renderTarget); scene.add(lightProbe); // Influence objects near the probe positionlightProbe.intensity = 1.0;Global Illumination#
Achieve realistic indirect lighting using environment maps and light probes:
Image-Based Lighting (IBL)#
Use HDR environment maps for realistic ambient lighting:
// HDR environment for IBLscene.environment = hdrEnvironmentMap; // Materials automatically use environment for:// - Ambient lighting (indirect diffuse)// - Reflections (indirect specular) // Control intensity per materialmaterial.envMapIntensity = 1.0; // Full strengthmaterial.envMapIntensity = 0.5; // Half strengthAmbient Occlusion#
Bake AO maps or use SSAO post-processing for contact shadows:
// Baked AO mapmaterial.aoMap = aoTexture;material.aoMapIntensity = 1.0; // Requires second UV channelgeometry.setAttribute('uv2', geometry.attributes.uv); // Or use SSAO post-processing (see Post-Processing docs)const ssaoPass = new SSAOPass(scene, camera);composer.addPass(ssaoPass);Lighting Performance#
| Technique | Cost | Notes |
|---|---|---|
| Directional Light | Low | One light per scene recommended |
| Point Light | Medium | Avoid many overlapping point lights |
| Spot Light | Medium | Similar cost to point lights |
| Point Light Shadows | High | 6 shadow maps per light |
| CSM Shadows | Medium | 3 shadow maps total |
| Environment Map | Low | One-time setup, no per-frame cost |
Optimization Tips#
- Limit dynamic lights to 3-5 per scene (1 directional + 2-4 point/spot)
- Use baked lighting for static objects (lightmaps, AO)
- Disable shadows for small or distant lights
- Reduce shadow map resolution for mobile (512-1024)
- Use hemisphere light instead of ambient for outdoor scenes
- Combine multiple lights into environment maps when possible
Light Count Limits
Three.js limits the number of lights per material (default: 16 total, 4 shadows). Exceeding these limits can cause lights to be ignored. Use baked lighting or light probes for static illumination.
Best Practices#
Light Setup#
- Use one directional light as the sun with CSM shadows
- Add 2-3 point/spot lights for accents and local illumination
- Use ambient or hemisphere light for fill lighting
- Apply environment map for reflections and IBL
- Position lights to create depth and visual interest
Shadow Quality#
- Use 2048x2048 for primary directional light shadows
- Use 512-1024 for secondary light shadows
- Adjust shadow camera bounds to fit your scene tightly
- Use shadow bias to eliminate shadow acne
- Enable PCFSoftShadowMap for smooth shadow edges
Mobile Optimization#
- Reduce shadow map resolution to 512-1024
- Use BasicShadowMap instead of PCF on low-end devices
- Limit to 1-2 dynamic lights with shadows
- Use baked lightmaps for static geometry
- Disable shadows entirely on very low-end devices