Blender Utils
This file contains utility functions for converting Blender-exported GLTF data to match Three.js rendering behavior. It addresses light intensity mismatches, decay settings, and spot light cone angle validation.
Blender exports 3D assets with lighting information, but the unit systems and calculations differ from Three.js. These utilities automatically handle the conversion so your scene renders consistently between Blender and Three.js.
Key Conversion Issues Addressed
- Light Intensity Units: Blender exports intensity in watts, while Three.js expects candela or lumens/lux
- Light Decay: GLTF spec requires inverse square law, which must be explicitly set in Three.js
- Spot Light Cones: GLTF cone angles must be translated to Three.js angle and penumbra values
Note
- Color temperature is already handled by Blender during export (converted to RGB), so no conversion is needed
- Original values are always stored in the light's
userDataobject whenpreserveOriginalis true, allowing you to revert changes if needed - Custom conversion factors can be useful if you've exported lights with non-standard intensity values
Usage Example: Complete Workflow
Here's a typical workflow for loading and adjusting a Blender scene:
import { adjustGltfLightIntensities } from '@/utils/BlenderUtils';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
const scene = new THREE.Scene();
loader.load('my-blender-scene.glb', (gltf) => {
// Add the model to the scene
scene.add(gltf.scene);
// Convert all lights to match Three.js rendering
const adjustments = adjustGltfLightIntensities(gltf.scene);
console.log(`Successfully adjusted ${adjustments.length} lights`);
// Your scene is now ready to render!
renderer.render(scene, camera);
});
GetDefaultConversionFactor
Returns the appropriate intensity conversion factor for a given light type.
Conversion factors are based on standard photometric conversions: 1 W/m² = 683 lumens/m² (lux) at 555nm wavelength. Directional and spot lights use a more conservative factor of 100 for sunlight-like sources, while point, ambient, hemisphere, and rectangular area lights use the standard 683 factor.
Parameters
lightType: Light type as a string (e.g.,'directional','point') or a THREE.Light instance
Returns
A number representing the conversion factor for that light type
import { getDefaultConversionFactor } from '@/utils/BlenderUtils';
// Using a light object
const factor = getDefaultConversionFactor(directionalLight); // returns 100
// Using a string type name
const factor = getDefaultConversionFactor('point'); // returns 683
ConvertBlenderLightIntensity
Converts the intensity value of a single light from Blender's watt units to Three.js compatible units.
Blender exports intensity in watts, but Three.js interprets it differently depending on whether physically correct lighting is enabled. This function applies the appropriate conversion factor and stores the original value for reference.
Parameters
light: THREE.Light object to convertoptions: Object with optional configurationconversionFactor: Custom conversion factor (overrides the default for this light type)usePhysicallyCorrect: Boolean indicating if physically correct lighting is enabled (default:false)preserveOriginal: Whether to store the original intensity in the light's userData (default:true)
Returns
The adjusted intensity value
import { convertBlenderLightIntensity } from '@/utils/BlenderUtils';
// Convert a directional light with default factor
const adjusted = convertBlenderLightIntensity(directionalLight);
directionalLight.intensity = adjusted;
// Use a custom conversion factor
const adjusted = convertBlenderLightIntensity(spotLight, {
conversionFactor: 120
});
spotLight.intensity = adjusted;
// Access the original intensity later
console.log(spotLight.userData._blenderOriginalIntensity);
EnsureCorrectDecay
Sets the decay property to 2 (inverse square law) for point and spot lights, as required by the GLTF specification.
The GLTF spec mandates inverse square law attenuation for point and spot lights. Three.js uses a decay property where 0 = constant, 1 = linear, and 2 = inverse square. This function ensures the correct value is set.
Parameters
light: THREE.PointLight or THREE.SpotLight object to adjustpreserveOriginal: Whether to store the original decay value in userData (default:true)
Returns
Void (modifies the light in place)
import { ensureCorrectDecay } from '@/utils/BlenderUtils';
ensureCorrectDecay(pointLight);
// pointLight.decay is now set to 2
// Access the original decay value if needed
console.log(pointLight.userData._blenderOriginalDecay);
VerifySpotLightCones
Validates spot light cone angles and clamps them to valid ranges.
GLTF uses innerConeAngle and outerConeAngle, which Three.js converts to angle (outer cone) and penumbra (softness). This function verifies these values are valid: angle must be between 0 and PI/2, and penumbra must be between 0 and 1.
Parameters
light: THREE.SpotLight object to verify
Returns
Void (modifies the light in place if invalid values are found)
import { verifySpotLightCones } from '@/utils/BlenderUtils';
verifySpotLightCones(spotLight);
// Logs a warning if angle or penumbra are out of range
// Automatically clamps them to valid ranges
ConvertBlenderLight
Convenience function that applies all conversions to a single light at once.
This is the recommended function for converting individual lights. It handles intensity conversion, decay enforcement, and spot light cone verification in a single call.
Parameters
light: THREE.Light object to convertoptions: Object with optional configuration (same as convertBlenderLightIntensity)conversionFactor: Custom conversion factorusePhysicallyCorrect: Whether physically correct lighting is enabledpreserveOriginal: Whether to store original values in userData
Returns
An object containing the conversion results:
intensity: The adjusted intensity valuedecay: The decay property (for point/spot lights)angle: The angle property (for spot lights)penumbra: The penumbra property (for spot lights)
import { convertBlenderLight } from '@/utils/BlenderUtils';
const result = convertBlenderLight(spotLight, {
usePhysicallyCorrect: false,
preserveOriginal: true
});
console.log(result);
// {
// intensity: 0.5,
// decay: 2,
// angle: 0.7854,
// penumbra: 0.5
// }
ApplyBlenderShadowDefaults
Applies Blender-style shadow defaults to a GLTF scene or subtree after import.
In Blender, every mesh both casts and receives shadows by default, and shadow casting is toggled per light. GLTF export does not preserve this behavior 1:1. applyBlenderShadowDefaults restores these defaults on your loaded Three.js scene.
Parameters
object3d:THREE.Object3D– Root object to traverse (for examplegltf.sceneor a block root).
Behavior
- Traverses the entire subtree under
object3d. - For every mesh (
node.isMesh === true):- Sets
node.castShadow = true - Sets
node.receiveShadow = true
- Sets
- For every light (
node.isLight === true) withnode.userData.castShadow === true:- Sets
node.castShadow = true
- Sets
- If
object3dis falsy, it is a no-op. - Mutates the scene graph in place (it is intentionally not a pure function).
Usage example
import {
adjustGltfLightIntensities,
applyBlenderShadowDefaults,
} from '@/utils/BlenderUtils';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
const scene = new THREE.Scene();
loader.load('my-blender-scene.glb', (gltf) => {
// Normalise light intensities and decay to match Three.js expectations
const adjustments = adjustGltfLightIntensities(gltf.scene, {
usePhysicallyCorrect: false,
preserveOriginal: true,
});
console.log(`Adjusted ${adjustments.length} lights`);
// Restore Blender-like shadow behavior on meshes and lights
applyBlenderShadowDefaults(gltf.scene);
scene.add(gltf.scene);
renderer.render(scene, camera);
});
AdjustGltfLightIntensities
Batch processes all lights in a GLTF scene, applying all necessary conversions to each one.
This is the main function to call after loading a GLTF file exported from Blender. It traverses the scene, finds all lights, and applies intensity conversion, decay enforcement, and cone angle verification to each.
Parameters
gltfScene: THREE.Scene or THREE.Object3D (root object from GLTFLoader)options: Object with optional configuration (same as convertBlenderLightIntensity)conversionFactor: Custom conversion factor for all lightsusePhysicallyCorrect: Whether physically correct lighting is enabledpreserveOriginal: Whether to store original values in userData
Returns
An array of adjustment info objects, one for each light found. Each object contains:
light: The THREE.Light objectname: The light's name (or'(unnamed)')type: The light's type (e.g.,'DirectionalLight')originalIntensity: The intensity before conversionadjustedIntensity: The intensity after conversionconversionFactor: The factor that was applied
import { adjustGltfLightIntensities } from '@/utils/BlenderUtils';
// After loading a GLTF file
const gltfLoader = new GLTFLoader();
gltfLoader.load('scene.glb', (gltf) => {
// Adjust all lights in the scene
const adjustments = adjustGltfLightIntensities(gltf.scene, {
usePhysicallyCorrect: false,
preserveOriginal: true
});
console.log(`Adjusted ${adjustments.length} lights`);
// Log details about each adjustment
adjustments.forEach(adj => {
console.log(`${adj.name}: ${adj.originalIntensity} → ${adj.adjustedIntensity} (factor: ${adj.conversionFactor})`);
});
});