jonarya
jonarya

Reputation: 323

Lighting not working properly with MeshLambertMaterial in THREE js

I am creating 3D rooms with Three.js. I have used MeshLambertMaterial as wall materials. When I add a PointLight into the room it is working properly. But when I add another bulb outside of the room it it not working.

var cubeMat = new THREE.MeshLambertMaterial({ color: 0xD3D3D3, side: THREE.DoubleSide });

Here is a reproducing test-case:

var camera, scene, renderer;

init();
animate();

var bulbLight;
var bulbLight2;
var bulbLightDirection = 1;

function addLights() {
  var bulbGeometry = new THREE.SphereGeometry(0.02, 16, 8);
  bulbLight = new THREE.PointLight(0xffee88, 2, 100, 2);
  bulbLight2 = new THREE.PointLight(0xffee88, 2, 100, 2);

  var bulbMat = new THREE.MeshStandardMaterial({
    emissive: 0xffffee,
    emissiveIntensity: 1,
    color: 0x000000
  });
  bulbLight.add(new THREE.Mesh(bulbGeometry, bulbMat));
  bulbLight.position.set(-0.5, 0.6, -1.5);
  bulbLight.shadow.camera.near = 0.01;
  bulbLight.castShadow = true;
  scene.add(bulbLight);
  
  bulbLight2.add(new THREE.Mesh(bulbGeometry, bulbMat));
  bulbLight2.position.set(-0.5, 0.6, -1.5);
  bulbLight2.shadow.camera.near = 0.01;
  bulbLight2.castShadow = true;
  scene.add(bulbLight2);

  var hemiLight = new THREE.HemisphereLight(0xddeeff, 0x0f0e0d, 0.02);
  scene.add(hemiLight);
}

function addFloor() {
  var floorMat = new THREE.MeshLambertMaterial({ color: 0xD3D3D3, side: THREE.DoubleSide });

  var floorGeometry = new THREE.PlaneBufferGeometry(20, 20);
  //floorGeometry.computeFaceNormals();
  //floorGeometry.computeVertexNormals();
  var floorMesh = new THREE.Mesh(floorGeometry, floorMat);
  floorMesh.receiveShadow = true;
  floorMesh.rotation.x = -Math.PI / 2.0;
  scene.add(floorMesh);
}

function addWalls() {
  var cubeMat = new THREE.MeshLambertMaterial({ color: 0xD3D3D3, side: THREE.DoubleSide });
  var boxGeometry = new THREE.BoxGeometry(0.1, 2, 5);
  //boxGeometry.computeFaceNormals();
  //boxGeometry.computeVertexNormals();
  var boxMesh = new THREE.Mesh(boxGeometry, cubeMat);
  boxMesh.position.set(0.1, 0.25, -0.5);
  boxMesh.castShadow = true;
  boxMesh.receiveShadow = true;
  scene.add(boxMesh);

  var boxMesh2 = new THREE.Mesh(boxGeometry, cubeMat);
  boxMesh2.position.set(0.5, 0.25, 0.5);
  boxMesh2.castShadow = true;
  boxMesh2.receiveShadow = true;
  scene.add(boxMesh2);
}

function addControls() {
  var controls = new THREE.OrbitControls(camera, renderer.domElement);
  controls.target.set(0, 0, 0);
  controls.update();
}

function init() {
  camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100);
  camera.position.x = -4;
  camera.position.z = 4;
  camera.position.y = 2;

  scene = new THREE.Scene();

  addLights();
  addFloor();
  addWalls();

  renderer = new THREE.WebGLRenderer();
  renderer.physicallyCorrectLights = true;
  renderer.gammaInput = true;
  renderer.gammaOutput = true;
  renderer.shadowMap.enabled = true;
  renderer.toneMapping = THREE.ReinhardToneMapping;
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  addControls();

  window.addEventListener('resize', onWindowResize, false);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);
  render();

  bulbLight.position.x += (bulbLight.direction === 1) ? 0.01 : -0.01;

  if (bulbLight.position.x > 1) bulbLight.direction = 0;
  if (bulbLight.position.x < -1) bulbLight.direction = 1;

}

function render() {
  renderer.render(scene, camera);
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

What is wrong here ?

MeshPhongMaterial gives the same result. But working with MeshStandardMaterial

Upvotes: 0

Views: 1955

Answers (2)

WestLangley
WestLangley

Reputation: 104763

What you are seeing are self-shadowing artifacts due to the fact that you have both receiveShadow and castShadow set to true for your mesh.

Self-shadowing artifacts can be reduced by adjusting light.shadow.bias. Unfortunately, doing so can cause other artifacts.

Shadows can be tricky. I suggest you google the topic and make sure you understand all the issues involved.

Also, MeshLambertMaterial is limited in how it handles shadows. I would use MeshStandardMaterial if you have an environment map, or MeshPhongMaterial otherwise.

three.js r.87

Upvotes: 1

M -
M -

Reputation: 28462

It looks like you may have found a bug with MeshLambertMaterial where it's miscalculating multiple shadows. I played with your code for a while, and from the looks of it, the material is performing a multiply operation, where it only illuminates where both lights shine, but not when only one shines:

1 x 1 = 1;
1 x 0 = 0;
0 x 0 = 0;

In reality, lights should be calculated in an additive way:

1 + 1 = 2;
1 + 0 = 1;
0 + 0 = 0;

The only solution I found was changing the walls and floor material to MeshPhongMaterial. Also, you probably don't need THREE.DoubleSide on the walls.

var floorMat = new THREE.MeshPhongMaterial({ color: 0xD3D3D3, side: THREE.DoubleSide });
var cubeMat = new THREE.MeshPhongMaterial({ color: 0xD3D3D3 });

See working fiddle

Upvotes: 1

Related Questions