Metallness and Non-Directional Lights - or - Why does my material appear black in ThreeJs?
TLDR; Fully Metallic materials in ThreeJS don’t respond to non-directional lights - eg. Ambient or Hemisphere Lights. Adjust the material to be non-metallic, or add Directional lights to the scene
MeshStandardMaterial in ThreeJS (and all materials that inherit from it -
MeshPhysicalMaterial etc.) have a
metallness attribute defines how metalic the material should appear. It’s a float, with values from
0 being not metallic at all,
1.0 being fully metallic.
Often when working with scenes, you may see a material showing up as fully black, even when there are lights in the scene. This
metallness value might be the cause of that.
In ThreeJs fully metallic materials only respond to non-directional lights. If you’ve only got
HemisphereLight lights lighting your scene, anything with a
metallness value of
1.0 will be fully black.
A test prop with a material set to be fully metalic, and only
AmbientLight lights lighting the scene, leaving a fully black output material
There’s a handful of different ways to fix this, depending on the scene, materials and effect you need.
- Pros It’s simpler to test
- Cons With only non-direction lights you’ll have a flat looking scene
metallness value might have been mis-set in the original scene. Simply setting it to
0 will allow the material to be lit:
mesh.material.metallness = 0
mesh.material.needsUpdate = true
Materials adjusted to be non-metalic, with only an
AmbientLight lighting the scene
2. Add directional lights to the scene
- Pros Properly setup lighting will give good looking shadows
- Cons It’s more involved and expensive on the render
This depends on what your scene needs to look like, but just adding a directional light will show up the material properly.
You’ll probably want a handful of lights at different intensities and positions around the center point.
var light = new THREE.DirectionalLight('#ffe6ae', 5)
light.position.set(7.5, 5, 10)
The same scene, with the materials set to non-metalic, but with 3
DirectionalLight lights added around the object, and shadow maps enabled
In a ThreeJs scene you might want to lock down certain camera motions.
Using the standard
OrbitControls controls system, you’d have something like this setup for your controls in the scene:
controls = new OrbitControls(camera, container)
Controls have 3 basic movements available - zoom, pan, and rotate.
It’s easy to disable each of these directly like so:
controls.enableZoom = false
controls.enablePan = false
controls.enableRotate = false
Disabling all three of the movement types effectively locks the camera in place, which is great if you want to take control of the view for something specific.
Bonus - Disabling controls unbinds any associated input listeners!
The most notable benefit of this - normally with Zoom enabled, the canvas will capture any scroll events and interpret them as zoom in/out motion. For users on a page with a viewer in the content area, this can easily disrupt the scrolling down.
controls.enableZoom = false unbinds the scroll input listeners, so scrolling down a page behaves unaffected by the viewer. Super!