jonarya
jonarya

Reputation: 323

Custom plane does not cast/ receive shadows in THREE js

I am trying to create a box (a wall) using six planes. I have created planes but shadows are not there.

This is how I create custom planes.

function createWall(vertices) {
  var geometry = new THREE.Geometry(), i;
  for (i = 0; i < vertices.length; i = i + 1) {
    geometry.vertices.push(vertices[i]);
  }
  geometry.faces.push(new THREE.Face3(0, 1, 2));
  geometry.faces.push(new THREE.Face3(0, 2, 3));
 
  geometry.computeVertexNormals();
  geometry.computeFaceNormals();

  var material = new THREE.MeshStandardMaterial({
    emissive: 0x708090,
    emissiveIntensity: 1,
    side: THREE.DoubleSide,
    color: 0xD3D3D3
  });
  var mesh = new THREE.Mesh(geometry, material);
  mesh.castShadow = true;
  mesh.receiveShadow = true;
  return mesh;
}

Here is the complete code.

var camera, scene, renderer;

function addFloor() {
  var material = new THREE.MeshStandardMaterial({
    roughness: 0.8,
    color: 0x696969,
    metalness: 0.2,
    bumpScale: 0.0005
  });

  var geometry = new THREE.PlaneBufferGeometry(2000, 2000);
  var mesh = new THREE.Mesh(geometry, material);
  mesh.receiveShadow = true;
  mesh.rotation.x = -Math.PI / 2.0;
  scene.add(mesh);
}

function createWall(vertices) {
  var geometry = new THREE.Geometry(), i;
  for (i = 0; i < vertices.length; i = i + 1) {
    geometry.vertices.push(vertices[i]);
  }
  geometry.faces.push(new THREE.Face3(0, 1, 2));
  geometry.faces.push(new THREE.Face3(0, 2, 3));
 
  geometry.computeVertexNormals();
  geometry.computeFaceNormals();

  var material = new THREE.MeshStandardMaterial({
    emissive: 0x708090,
    emissiveIntensity: 1,
    side: THREE.DoubleSide,
    color: 0xD3D3D3
  });
  var mesh = new THREE.Mesh(geometry, material);
  mesh.castShadow = true;
  mesh.receiveShadow = true;
  return mesh;
}

function addBulb(location) {
  var geometry = new THREE.SphereGeometry(2, 20, 20);
  var light = new THREE.PointLight(0xffffff, 1, 100, 2);

  var material = new THREE.MeshStandardMaterial({
    emissive: 0xffffee,
    emissiveIntensity: 1,
    color: 0x000000
  });
  light.add(new THREE.Mesh(geometry, material));
  light.position.set(location.x, location.y, location.z);
  light.shadow.camera.near = 0.0001;
  light.castShadow = true;
  //light.shadow.darkness = 0.5;
  //light.shadow.camera.vsible = true;
  return light;
}

function addWalls() {
	var wall1 = createWall([
    new THREE.Vector3(0, 0, 0), //vertex0
    new THREE.Vector3(200, 0, 0), //1
    new THREE.Vector3(200, 100, 0), //2
    new THREE.Vector3(0, 100, 0) //3
  ]);
  var wall2 = createWall([
    new THREE.Vector3(0, 0, 5), //vertex0
    new THREE.Vector3(200, 0, 5), //1
    new THREE.Vector3(200, 100, 5), //2
    new THREE.Vector3(0, 100, 5) //3
  ]);
  scene.add(wall1);
  scene.add(wall2);
}

function addCamera() {
	camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.set(50, 100, 300);
  scene.add(camera);
}

function init() {
  scene = new THREE.Scene();

  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  renderer.shadowMap.renderSingleSided = false;
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setClearColor(0xffffff, 1);
	document.body.appendChild(renderer.domElement);

	addCamera();
  addFloor();
  addWalls();
  scene.add(addBulb({x: 100, y: 50, z: 25}));
  var ambientLight = new THREE.AmbientLight(0x999999, 0.6);
  scene.add(ambientLight);
  var controls = new THREE.OrbitControls(camera, renderer.domElement);
}

function animate() {
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

init();
animate();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

I may have done something wrong while creating custom plane. I do not understand what is wrong here.

UPDATED:

renderer.shadowMap.renderSingleSided = false;

It seems shadows are working but lighting has strange square effect.

Here I have tried BoxGeometry which has little depth as suggested in three-js-plane-doesnt-cast-shadow , As I noted this occurs only for small values for depth. (depth < 1)

BoxGeometry with 0.1 depth (with or without single sided render

var camera, scene, renderer;

function addFloor() {
  var material = new THREE.MeshStandardMaterial({
    roughness: 0.9,
    color: 0xffffff,
    metalness: 0.1,
    bumpScale: 0.0005
  });

  var geometry = new THREE.PlaneBufferGeometry(2000, 2000);
  var mesh = new THREE.Mesh(geometry, material);
  mesh.receiveShadow = true;
  mesh.rotation.x = -Math.PI / 2.0;
  scene.add(mesh);
}

function createWall(location) {
  var geometry = new THREE.BoxGeometry(200, 100, 0.1);
  geometry.translate((location.x1 + location.x2) / 2, 150 / 2, location.z);
  var material = new THREE.MeshStandardMaterial({
    emissive: 0x708090,
    emissiveIntensity: 1,
    side: THREE.DoubleSide,
    color: 0xD3D3D3,
    roughness: 0.9,
    metalness: 0.1
  });
  var mesh = new THREE.Mesh(geometry, material);
  mesh.castShadow = true;
  mesh.receiveShadow = true;
  return mesh;
}

function addBulb(options) {
  var geometry = new THREE.SphereGeometry(2, 20, 20);
  var light = new THREE.PointLight(options.color, 1, 500, 2);

  var material = new THREE.MeshStandardMaterial({
    emissive: options.color,
    emissiveIntensity: 1,
    color: options.color
  });
  light.add(new THREE.Mesh(geometry, material));
  light.position.set(options.x, options.y, options.z);
  light.shadow.camera.near = 0.0001;
  light.castShadow = true;
  light.shadow.darkness = 0.5;
  light.shadow.camera.vsible = true;
  return light;
}

function addWalls() {
	var wall1 = createWall({ x1: 0, x2: 200, z: 0});
  var wall2 = createWall({ x1: 0, x2: 200, z: 5});
  scene.add(wall1);
  //scene.add(wall2);
}

function addCamera() {
	camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.set(100, 100, 300);
  scene.add(camera);
}

function init() {
  scene = new THREE.Scene();

  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  renderer.shadowMap.renderSingleSided = false;
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setClearColor(0xffffff, 1);
	document.body.appendChild(renderer.domElement);

	addCamera();
  addFloor();
  addWalls();
  scene.add(addBulb({x: 0, y: 100, z: 25, color: 0xff0000 }));
  scene.add(addBulb({x: 100, y: 100, z: 25, color: 0x00ff00 }));
  scene.add(addBulb({x: 200, y: 100, z: 25, color: 0x0000ff }));
  var ambientLight = new THREE.AmbientLight(0x999999, 0.6);
  scene.add(ambientLight);
  var controls = new THREE.OrbitControls(camera, renderer.domElement);
}

function animate() {
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

init();
animate();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Upvotes: 1

Views: 3843

Answers (1)

juanferrer
juanferrer

Reputation: 1250

You're facing Z fighting. Since the thickness of the wall is so small, the 2 faces are nearly touching each other, so they cast shadows on each other.

You can either remove the casting/receiving of shadows on the wall or, better, increase the z value in the geometry, like so:

var geometry = new THREE.BoxGeometry(200, 100, 1);

Also, since you changed to a box from a plane, the line side: THREE.DoubleSide, in the wall material is no longer needed. In fact, it's part of the self-shadowing problem. Changing those two lines should solve your problem.

Here's a working Fiddle.

Upvotes: 1

Related Questions