Configure realistic lighting, shadows, and environment maps to enhance the visual quality and realism of 3D models.
Set environment maps for realistic lighting and reflections.
/**
* URL to HDR environment image for lighting and reflections
* Supports HDR (.hdr, .exr) and LDR (.jpg, .png) formats
* Set to 'neutral' for built-in neutral lighting
*/
environmentImage: string;Usage:
<!-- HDR environment for realistic lighting -->
<model-viewer
src="models/car.glb"
environment-image="environments/studio.hdr">
</model-viewer>
<!-- Built-in neutral lighting -->
<model-viewer
src="models/product.glb"
environment-image="neutral">
</model-viewer>// Switch environments dynamically
const environments = [
'environments/studio.hdr',
'environments/outdoor.hdr',
'environments/indoor.hdr'
];
let currentEnv = 0;
function cycleEnvironment() {
modelViewer.environmentImage = environments[currentEnv];
currentEnv = (currentEnv + 1) % environments.length;
}Control the background environment visible behind the model.
/**
* URL to skybox background image
* Can be different from environmentImage for creative effects
* Set to same as environmentImage for consistent lighting
*/
skyboxImage: string;Usage:
<!-- Different skybox and environment -->
<model-viewer
src="models/spaceship.glb"
environment-image="environments/studio.hdr"
skybox-image="backgrounds/space.jpg">
</model-viewer>
<!-- Matching environment and skybox -->
<model-viewer
src="models/outdoor-scene.glb"
environment-image="environments/park.hdr"
skybox-image="environments/park.hdr">
</model-viewer>Adjust overall brightness and exposure of the scene.
/**
* Scene exposure/brightness multiplier
* Values > 1.0 increase brightness, < 1.0 decrease brightness
* Typical range: 0.1 to 3.0
*/
exposure: number;Usage:
<!-- Brighter exposure for dark models -->
<model-viewer
src="models/dark-material.glb"
exposure="1.5">
</model-viewer>// Dynamic exposure adjustment
const exposureSlider = document.getElementById('exposure');
exposureSlider.addEventListener('input', (e) => {
modelViewer.exposure = parseFloat(e.target.value);
});Control shadow rendering for increased realism.
/**
* Shadow intensity (0 = no shadows, 1 = full shadows)
* Shadows are cast onto an invisible ground plane
*/
shadowIntensity: number;
/**
* Shadow softness/blur amount
* Higher values create softer, more diffused shadows
* Range: 0.0 (sharp) to 2.0 (very soft)
*/
shadowSoftness: number;Usage:
<!-- Soft, realistic shadows -->
<model-viewer
src="models/furniture.glb"
shadow-intensity="0.8"
shadow-softness="1.2">
</model-viewer>
<!-- Sharp shadows for technical visualization -->
<model-viewer
src="models/machine.glb"
shadow-intensity="1.0"
shadow-softness="0.1">
</model-viewer>Enable simplified, consistent lighting for product visualization.
/**
* Use neutral lighting setup instead of environment-based lighting
* Provides consistent, diffused lighting ideal for product shots
* Overrides environmentImage when enabled
*/
neutralLighting: boolean;
/**
* Tone mapping method for HDR rendering
*/
toneMapping: 'auto' | 'aces' | 'agx' | 'commerce' | 'neutral' | 'reinhard' | 'cineon' | 'linear' | 'none';
/**
* Height positioning for grounded skybox
*/
skyboxHeight: string;Usage:
<!-- Consistent product lighting -->
<model-viewer
src="models/jewelry.glb"
neutral-lighting>
</model-viewer>
<!-- Compare neutral vs environment lighting -->
<button onclick="toggleLighting()">Toggle Lighting</button>
<script>
function toggleLighting() {
modelViewer.neutralLighting = !modelViewer.neutralLighting;
}
</script>Professional studio setup with controlled lighting:
<model-viewer
src="models/product.glb"
environment-image="environments/studio_small_03_1k.hdr"
exposure="1.0"
shadow-intensity="0.7"
shadow-softness="1.0">
</model-viewer>Natural outdoor lighting with soft shadows:
<model-viewer
src="models/architecture.glb"
environment-image="environments/kloofendal_48d_partly_cloudy_1k.hdr"
exposure="1.2"
shadow-intensity="0.9"
shadow-softness="1.5">
</model-viewer>High contrast lighting for artistic effects:
<model-viewer
src="models/sculpture.glb"
environment-image="environments/venice_sunset_1k.hdr"
exposure="0.8"
shadow-intensity="1.0"
shadow-softness="0.5">
</model-viewer>Clean, consistent lighting for e-commerce:
<model-viewer
src="models/product.glb"
neutral-lighting
shadow-intensity="0.3"
shadow-softness="1.8">
</model-viewer>Create interactive lighting controls:
<div class="lighting-controls">
<label>
Exposure: <input type="range" id="exposure" min="0.1" max="3.0" step="0.1" value="1.0">
</label>
<label>
Shadow Intensity: <input type="range" id="shadow" min="0" max="1" step="0.1" value="0.7">
</label>
<label>
Shadow Softness: <input type="range" id="softness" min="0" max="2" step="0.1" value="1.0">
</label>
</div>
<script>
document.getElementById('exposure').addEventListener('input', (e) => {
modelViewer.exposure = parseFloat(e.target.value);
});
document.getElementById('shadow').addEventListener('input', (e) => {
modelViewer.shadowIntensity = parseFloat(e.target.value);
});
document.getElementById('softness').addEventListener('input', (e) => {
modelViewer.shadowSoftness = parseFloat(e.target.value);
});
</script>Create preset lighting environments:
const lightingPresets = {
studio: {
environmentImage: 'environments/studio.hdr',
exposure: 1.0,
shadowIntensity: 0.7,
shadowSoftness: 1.0,
neutralLighting: false
},
outdoor: {
environmentImage: 'environments/park.hdr',
exposure: 1.2,
shadowIntensity: 0.9,
shadowSoftness: 1.5,
neutralLighting: false
},
neutral: {
environmentImage: null,
exposure: 1.0,
shadowIntensity: 0.3,
shadowSoftness: 1.8,
neutralLighting: true
}
};
function applyLightingPreset(presetName) {
const preset = lightingPresets[presetName];
if (!preset) return;
Object.keys(preset).forEach(key => {
if (preset[key] !== null) {
modelViewer[key] = preset[key];
}
});
}
// UI for preset selection
document.getElementById('preset-studio').addEventListener('click',
() => applyLightingPreset('studio'));
document.getElementById('preset-outdoor').addEventListener('click',
() => applyLightingPreset('outdoor'));
document.getElementById('preset-neutral').addEventListener('click',
() => applyLightingPreset('neutral'));Adjust lighting based on screen size or device capabilities:
function setupResponsiveLighting() {
const isMobile = window.innerWidth < 768;
const hasHDRSupport = checkHDRSupport();
if (isMobile) {
// Lighter lighting processing for mobile
modelViewer.neutralLighting = true;
modelViewer.shadowIntensity = 0.3;
modelViewer.shadowSoftness = 1.0;
} else if (hasHDRSupport) {
// Full HDR environment for capable devices
modelViewer.environmentImage = 'environments/studio_4k.hdr';
modelViewer.shadowIntensity = 0.8;
modelViewer.shadowSoftness = 1.2;
} else {
// Fallback to LDR environment
modelViewer.environmentImage = 'environments/studio.jpg';
modelViewer.shadowIntensity = 0.6;
modelViewer.shadowSoftness = 1.0;
}
}
function checkHDRSupport() {
// Simplified HDR detection
return window.screen && window.screen.colorGamut === 'p3';
}
window.addEventListener('resize', setupResponsiveLighting);
modelViewer.addEventListener('load', setupResponsiveLighting);Animate lighting changes for dynamic effects:
class LightingAnimator {
constructor(modelViewer) {
this.modelViewer = modelViewer;
this.isAnimating = false;
}
async animateToPreset(targetPreset, duration = 1000) {
if (this.isAnimating) return;
this.isAnimating = true;
const startValues = {
exposure: this.modelViewer.exposure,
shadowIntensity: this.modelViewer.shadowIntensity,
shadowSoftness: this.modelViewer.shadowSoftness
};
const startTime = performance.now();
const animate = (currentTime) => {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Smooth easing function
const eased = 1 - Math.pow(1 - progress, 3);
// Interpolate values
this.modelViewer.exposure = this.lerp(
startValues.exposure, targetPreset.exposure, eased);
this.modelViewer.shadowIntensity = this.lerp(
startValues.shadowIntensity, targetPreset.shadowIntensity, eased);
this.modelViewer.shadowSoftness = this.lerp(
startValues.shadowSoftness, targetPreset.shadowSoftness, eased);
if (progress < 1) {
requestAnimationFrame(animate);
} else {
// Set final values and environment
if (targetPreset.environmentImage) {
this.modelViewer.environmentImage = targetPreset.environmentImage;
}
this.modelViewer.neutralLighting = targetPreset.neutralLighting;
this.isAnimating = false;
}
};
requestAnimationFrame(animate);
}
lerp(start, end, t) {
return start + (end - start) * t;
}
}
// Usage
const animator = new LightingAnimator(modelViewer);
animator.animateToPreset(lightingPresets.outdoor, 2000);