Overview
Proper materials and lighting are essential for creating realistic 3D scenes. Threebox provides multiple lighting options and supports all Three.js material types.
Materials
Material Types
Threebox supports all Three.js materials. Choose based on your rendering needs:
Material Use Case Lighting Required MeshBasicMaterialFlat colors, overlays, UI elements No MeshLambertMaterialMatte surfaces, simple objects Yes MeshPhongMaterialShiny surfaces, metals Yes MeshStandardMaterialRealistic PBR rendering (recommended) Yes MeshPhysicalMaterialAdvanced PBR with clearcoat, transmission Yes
Specifying Materials
You can specify materials in three ways:
String Name
Custom Material Object
Default Material
// Use material name with basic properties
const sphere = tb . sphere ({
radius: 50 ,
material: 'MeshStandardMaterial' ,
color: 'red' ,
opacity: 1
});
Material Properties
Common properties available when using material strings:
Property Type Default Description colorstring | number'black'Material color opacitynumber1Opacity (0-1, sets transparent=true if < 1) sideTHREE.DoubleSideundefinedRender side(s)
src/utils/material.js:18-50
function material ( options ) {
var output ;
if ( options ) {
options = utils . _validate ( options , defaults );
// check if user provided material object
if ( options . material && options . material . isMaterial )
output = options . material ;
// check if user provided any material parameters
else if ( options . material || options . color || options . opacity ){
output = new THREE [ options . material ]({
color: options . color ,
transparent: options . opacity < 1
});
}
// if neither, return default material
else output = generateDefaultMaterial ();
output . opacity = options . opacity ;
if ( options . side ) output . side = options . side
}
// if no options, return default
else output = generateDefaultMaterial ();
function generateDefaultMaterial (){
return new THREE [ defaults . material ]({ color: defaults . color });
}
return output
}
Lighting
Default Lights
Enable basic scene lighting with the defaultLights option:
window . tb = new Threebox (
map ,
gl ,
{ defaultLights: true }
);
This creates:
Ambient Light : Soft overall illumination (75% intensity, white)
Directional Light 1 : Front-left lighting (25% intensity)
Directional Light 2 : Back-right lighting (25% intensity)
src/Threebox.js:1117-1131
defaultLights : function () {
this . lights . ambientLight = new THREE . AmbientLight (
new THREE . Color ( 'hsl(0, 0%, 100%)' ),
0.75
);
this . scene . add ( this . lights . ambientLight );
this . lights . dirLightBack = new THREE . DirectionalLight (
new THREE . Color ( 'hsl(0, 0%, 100%)' ),
0.25
);
this . lights . dirLightBack . position . set ( 30 , 100 , 100 );
this . scene . add ( this . lights . dirLightBack );
this . lights . dirLight = new THREE . DirectionalLight (
new THREE . Color ( 'hsl(0, 0%, 100%)' ),
0.25
);
this . lights . dirLight . position . set ( - 30 , 100 , - 100 );
this . scene . add ( this . lights . dirLight );
}
Default lights provide balanced illumination suitable for most scenes. They don’t cast shadows.
Real Sunlight
Enable realistic sun-based lighting with the realSunlight option:
window . tb = new Threebox (
map ,
gl ,
{
realSunlight: true ,
realSunlightHelper: true // Optional: show sun position helper
}
);
This creates:
Directional Light : Positioned based on sun calculations (100% intensity)
Hemisphere Light : Sky and ground color simulation (60% intensity)
Shadow Mapping : Enabled with high-quality settings
src/Threebox.js:1133-1164
realSunlight : function ( helper = false ) {
this . renderer . shadowMap . enabled = true ;
this . lights . dirLight = new THREE . DirectionalLight ( 0xffffff , 1 );
this . scene . add ( this . lights . dirLight );
if ( helper ) {
this . lights . dirLightHelper = new THREE . DirectionalLightHelper (
this . lights . dirLight ,
5
);
this . scene . add ( this . lights . dirLightHelper );
}
// Shadow configuration
let d2 = 1000 ;
let r2 = 2 ;
let mapSize2 = 8192 ;
this . lights . dirLight . castShadow = true ;
this . lights . dirLight . shadow . radius = r2 ;
this . lights . dirLight . shadow . mapSize . width = mapSize2 ;
this . lights . dirLight . shadow . mapSize . height = mapSize2 ;
this . lights . dirLight . shadow . camera . top = d2 ;
this . lights . dirLight . shadow . camera . right = d2 ;
this . lights . dirLight . shadow . camera . bottom = - d2 ;
this . lights . dirLight . shadow . camera . left = - d2 ;
this . lights . dirLight . shadow . camera . near = 1 ;
this . lights . dirLight . shadow . camera . visible = true ;
this . lights . dirLight . shadow . camera . far = 400000000 ;
this . lights . hemiLight = new THREE . HemisphereLight (
new THREE . Color ( 0xffffff ),
new THREE . Color ( 0xffffff ),
0.6
);
this . lights . hemiLight . color . setHSL ( 0.661 , 0.96 , 0.12 );
this . lights . hemiLight . groundColor . setHSL ( 0.11 , 0.96 , 0.14 );
this . lights . hemiLight . position . set ( 0 , 0 , 50 );
this . scene . add ( this . lights . hemiLight );
this . setSunlight ();
this . map . once ( 'idle' , () => {
this . setSunlight ();
this . repaint ();
});
}
Sunlight Features
Sun position is calculated based on:
Map center coordinates
Current date and time
Uses suncalc library for accurate astronomical calculations
Update sunlight position: tb . setSunlight ( new Date (), [ - 122.4194 , 37.7749 ]);
High-quality shadows with:
8192x8192 shadow map resolution
2000m shadow camera frustum
Soft shadow radius
Enable shadows on objects: tb . loadObj ( options , function ( model ) {
model . castShadow = true ;
model . receiveShadow = true ;
tb . add ( model );
});
Hemisphere light simulates:
Sky color (HSL: 0.661, 0.96, 0.12)
Ground reflection (HSL: 0.11, 0.96, 0.14)
Natural ambient bounce light
Custom Lighting
Adding Custom Lights
You can add your own lights to the scene:
// Point light
const pointLight = new THREE . PointLight ( 0xff0000 , 1 , 100 );
pointLight . position . set ( 50 , 50 , 50 );
tb . scene . add ( pointLight );
// Spot light with shadows
const spotLight = new THREE . SpotLight ( 0xffffff , 1 );
spotLight . position . set ( 100 , 100 , 100 );
spotLight . castShadow = true ;
spotLight . shadow . mapSize . width = 2048 ;
spotLight . shadow . mapSize . height = 2048 ;
tb . scene . add ( spotLight );
// Ambient light for overall brightness
const ambient = new THREE . AmbientLight ( 0x404040 , 0.5 );
tb . scene . add ( ambient );
Accessing Light Objects
Threebox exposes light objects through tb.lights:
// Modify ambient light
if ( tb . lights . ambientLight ) {
tb . lights . ambientLight . intensity = 0.5 ;
tb . lights . ambientLight . color . setHex ( 0xffffcc );
}
// Modify directional light
if ( tb . lights . dirLight ) {
tb . lights . dirLight . intensity = 1.5 ;
tb . lights . dirLight . position . set ( 100 , 200 , 100 );
}
// Update light helper
if ( tb . lights . dirLightHelper ) {
tb . updateLightHelper ();
}
Light Properties
Property Type Description tb.lights.ambientLightTHREE.AmbientLightOverall scene illumination tb.lights.dirLightTHREE.DirectionalLightMain directional light tb.lights.dirLightBackTHREE.DirectionalLightBack directional light (defaultLights only) tb.lights.hemiLightTHREE.HemisphereLightSky/ground hemisphere light (realSunlight only) tb.lights.dirLightHelperTHREE.DirectionalLightHelperVisual helper for sun direction tb.lights.pointLightTHREE.PointLightCustom point light (not set by default)
Material & Lighting Examples
const metallicMaterial = new THREE . MeshStandardMaterial ({
color: 0xcccccc ,
metalness: 0.9 ,
roughness: 0.1 ,
envMapIntensity: 1.0
});
tb . loadObj ({
obj: '/models/car.glb' ,
type: 'gltf' ,
units: 'meters' ,
material: metallicMaterial // Override model materials
}, function ( model ) {
model . setCoords ([ - 122.4194 , 37.7749 ]);
model . castShadow = true ;
model . receiveShadow = true ;
tb . add ( model );
});
Glass Material
const glassMaterial = new THREE . MeshPhysicalMaterial ({
color: 0xffffff ,
metalness: 0 ,
roughness: 0 ,
transmission: 0.9 ,
thickness: 0.5 ,
transparent: true ,
opacity: 0.5
});
const glassBuilding = tb . extrusion ({
coordinates: [ /* ... */ ],
height: 200 ,
materials: glassMaterial
});
Emissive (Glowing) Material
const glowMaterial = new THREE . MeshStandardMaterial ({
color: 0x00ff00 ,
emissive: 0x00ff00 ,
emissiveIntensity: 0.5
});
const beacon = tb . sphere ({
radius: 10 ,
material: glowMaterial ,
units: 'meters'
});
beacon . setCoords ([ - 122.4194 , 37.7749 , 100 ]);
tb . add ( beacon );
Day/Night Lighting Transition
function updateLighting () {
const now = new Date ();
const coords = map . getCenter ();
// Get sun position
const sunPos = tb . getSunPosition ( now , [ coords . lng , coords . lat ]);
// Update sun
tb . setSunlight ( now , [ coords . lng , coords . lat ]);
// Adjust ambient based on time of day
const hour = now . getHours ();
const isNight = hour < 6 || hour > 20 ;
if ( tb . lights . ambientLight ) {
tb . lights . ambientLight . intensity = isNight ? 0.3 : 0.75 ;
tb . lights . ambientLight . color . setHex ( isNight ? 0x6666ff : 0xffffff );
}
tb . map . repaint = true ;
}
// Update every minute
setInterval ( updateLighting , 60000 );
Reduce shadow map size for better performance
Limit number of shadow-casting lights (1-2 max)
Use smaller shadow camera frustum when possible
// Lower quality but faster
light . shadow . mapSize . width = 1024 ;
light . shadow . mapSize . height = 1024 ;
Use MeshBasicMaterial for UI and overlays (no lighting needed)
Use MeshLambertMaterial for simple lit objects (cheapest)
Reserve MeshStandardMaterial for hero objects
Avoid MeshPhysicalMaterial unless necessary
Minimize number of lights (2-4 total recommended)
Use baked lighting/shadows when possible
Consider hemisphere light instead of multiple directional lights
// Good: Simple lighting setup
const ambient = new THREE . AmbientLight ( 0xffffff , 0.6 );
const sun = new THREE . DirectionalLight ( 0xffffff , 0.8 );
Common Patterns
Museum Lighting (Spotlights)
function addSpotlight ( position , target , color = 0xffffff ) {
const spotlight = new THREE . SpotLight ( color , 1 , 200 , Math . PI / 6 , 0.5 );
spotlight . position . set ( position [ 0 ], position [ 1 ], position [ 2 ]);
spotlight . target . position . set ( target [ 0 ], target [ 1 ], target [ 2 ]);
spotlight . castShadow = true ;
tb . scene . add ( spotlight );
tb . scene . add ( spotlight . target );
return spotlight ;
}
// Illuminate specific objects
addSpotlight ([ 0 , 0 , 100 ], [ 0 , 0 , 0 ]);
addSpotlight ([ 50 , 50 , 100 ], [ 50 , 50 , 0 ]);
Street Lighting
function createStreetLight ( coords , height = 10 ) {
// Light post
const post = tb . tube ({
geometry: [[ 0 , 0 , 0 ], [ 0 , 0 , height ]],
radius: 0.5 ,
color: 0x333333 ,
units: 'meters'
});
post . setCoords ( coords );
tb . add ( post );
// Point light
const light = new THREE . PointLight ( 0xffaa00 , 1 , 50 );
const worldPos = tb . projectToWorld ( coords );
light . position . set ( worldPos . x , worldPos . y , height );
tb . scene . add ( light );
return { post , light };
}
// Create street lights along a path
const lights = [];
for ( let i = 0 ; i < 10 ; i ++ ) {
const lng = - 122.419 + i * 0.0005 ;
lights . push ( createStreetLight ([ lng , 37.7749 ]));
}
Lighting Best Practices
Too many lights or high shadow map resolutions can severely impact performance. Start simple and optimize as needed.
For realistic outdoor scenes, use realSunlight combined with subtle ambient lighting. For indoor or stylized scenes, use defaultLights and add custom accent lights.
Next Steps
Loading Models Apply materials and lighting to 3D models
Primitives Use materials with primitive objects