Three.js: planeGeometry not casting shadows

Created on 10 Jul 2016  路  14Comments  路  Source: mrdoob/three.js

Description of the problem

I have a series of PlaneGeometry that I merge into a larger geometry. They can receive shadow from other objects, such as BoxGeometry, but the PlaneGeometry is not casting shadows, either stitched into the larger merged geometry or added to the scene separately. Is this intended behavior? Thank you!

Code:

EO.map.HandleChunk = function(chunkObj) {

  var chunk = chunkObj.data;
  var structures = chunkObj.structures;

  //the main chunk geo
  var chunkGeometry = new THREE.Geometry();
  //material lists
  var materialListDictionary = [];
  var materialListIndex = [];

  //individual geo reference
  var geometry = new THREE.PlaneGeometry( 64, 64 );
  //blank tile material
  var material = new THREE.MeshPhongMaterial( { color: 0x001111, vertexColors: true, shading: THREE.SmoothShading } );
  //individual tile mesh
  var mesh = new THREE.Mesh( geometry, material );
  //loop through tiles
  for (var i = 0; i < chunk.length; i++) {

    if (typeof EO.tiles.library[chunk[i].tile_id] === 'undefined') {
      // tile does not exist in database, use blank tile material
      mesh.material = material;
    } else {
      // tile exists in database, clone the source from tile library
      mesh.material = EO.tiles.library[chunk[i].tile_id].clone(true);
    }
    //add material to dictionary if does not exist
    var mat_array_index = materialListIndex.indexOf(chunk[i].tile_id);
    if (mat_array_index < 0) {
      materialListIndex.push(chunk[i].tile_id);
      materialListDictionary.push(mesh.material);
      mat_array_index = materialListIndex.length - 1;
    }

    //set tile position
    mesh.position.set( chunk[i].x * 64, chunk[i].y * 64, 0 );
    mesh.updateMatrix();

    //merge to main chunk geometry
    mesh.castShadow = true;
    mesh.receiveShadow = true;
    chunkGeometry.merge(mesh.geometry, mesh.matrix, mat_array_index);

  }

  //new structure loop, create them all as planes...
  // [ Ceiling, North, East, South, West]
  //// [ x, y, rotx, roty, rotz ];
  var pi2 = Math.PI / 2;
  var structureOffsetArray = [
    [0, 0, 0, 0, 0],
    [0, 32, pi2, 0, 0],
    [32, 0, pi2, pi2, 0],
    [0, -32, pi2, 0, 0],
    [-32, 0, pi2, pi2, 0]
  ];
  for (var s = 0; s < structures.length; s++) {

    //grab a structure,
    var structure = structures[s];
    var x = structure.x;
    var y = structure.y;
    var height = structure.height;
    var base = 32;
    var texture_id = structure.texture_id;
    //setup materials
    var material = EO.structures.library[texture_id].clone();
    var material2 = EO.structures.library[texture_id].clone();
    var wallTexture = material2.map.clone();
    mesh.material = material;
    wallTexture.needsUpdate = true;
    wallTexture.repeat.set(1, height);
    material2.map = wallTexture;
    for (var si = 0; si < 5; si++) {

      //mesh.geometry = ;
      //var mesh = new THREE.Mesh( new THREE.PlaneGeometry( 64, base * height ), material );
      //handle material key index
      var materialKey = texture_id;

      if (si > 0) {
        mesh.geometry = new THREE.PlaneGeometry( 64, base * height );
        materialKey = texture_id+'_'+height;
        mesh.material = material2;
      } else {
        //base = 32;
        mesh.geometry =  new THREE.PlaneGeometry( 64, 64 );
      }

      var mat_array_index = materialListIndex.indexOf(materialKey);
      if (mat_array_index < 0) {
        materialListIndex.push(materialKey);
        materialListDictionary.push(mesh.material);
        mat_array_index = materialListIndex.length - 1;
      }

      var offset = structureOffsetArray[si];

      if (si === 0) {
        mesh.position.set( x * 64 + offset[0], y * 64 + offset[1], (base * height) );
      } else {
        mesh.position.set( x * 64 + offset[0], y * 64 + offset[1], (base * height) / 2 );
      }
      mesh.rotation.set( offset[2], offset[3], offset[4] );
      mesh.geometry.computeFaceNormals();
      mesh.updateMatrix();
      mesh.castShadow = true;
      mesh.receiveShadow = true;

      chunkGeometry.merge(mesh.geometry, mesh.matrix, mat_array_index);

    }

  }

  chunkGeometry.sortFacesByMaterialIndex();
  var chunkMesh = new THREE.Mesh(chunkGeometry, new THREE.MeshFaceMaterial( materialListDictionary ) );
  chunkMesh.castShadow = true;
  chunkMesh.receiveShadow = true;
  chunkMesh.name = "Chunk"

  EO.three.scene.add(chunkMesh);

}
Three.js version
  • [x] Dev
  • [x] r78
  • [ ] ...

    Browser
  • [ ] All of them

  • [x] Chrome
  • [ ] Firefox
  • [ ] Internet Explorer

    OS
  • [ ] All of them

  • [ ] Windows
  • [ ] Linux
  • [ ] Android
  • [ ] IOS
  • [x] macOS
    Hardware Requirements (graphics card, VR Device, ...)
Help (please use the forum)

Most helpful comment

@mrdoob

line 105&106 at /src/renderers/webgl/WebGLShadowMap.js

this.renderReverseSided = true;
this.renderSingleSided = true;

set this.renderReverseSided = false; can solve the problem.

Is there any special reason for this line?

All 14 comments

Shadows currently need objects to be solid and, ideally, without back side surfaces and front side surfaces in the same position. Try using a BoxGeometry with 0.01 height for now.

@mrdoob

line 105&106 at /src/renderers/webgl/WebGLShadowMap.js

this.renderReverseSided = true;
this.renderSingleSided = true;

set this.renderReverseSided = false; can solve the problem.

Is there any special reason for this line?

thankyou very much gaomeng1900, u solve my one year struggle for PlaneMesh shadow.

this.renderReverseSided = false;//true;
this.renderSingleSided = false;//true;

I set both to false and now I have shadow on each side of PlaneMesh.

@nagakontot

actually there is a api for that
https://threejs.org/docs/index.html#api/renderers/WebGLRenderer.shadowMap

renderReverseSided = true solves problems caused by precision. Please make sure everything works fine when u change it to false.

@gaomeng1900 that only works for PlaneGeometry though...? Maybe we can make an exception in the code...

@mrdoob renderReverseSided = true only works fine for closed geometries, not for unclosed geometry such as a semisphere/plane/anything with a hole on its face.

I've been hassling with this unintuitive bug, thinking I was doing something wrong.

The red squares should be casting shadows:
https://codepen.io/trusktr/pen/POrOoK?editors=0010

What's the solution that doesn't involve multiplying the polygon count by 6 (i.e. not converting from Plane to Box)?

I tried using a thin box in the following example (line 145), but it generates weird glitchy shadows that aren't supposed to be there:

https://codepen.io/trusktr/pen/EbBoLb

The only shadows should be from the sphere and the button, cast onto the background element.

If I make the thickness bigger (line 145), then the glitch goes away:

https://codepen.io/trusktr/pen/LOKemr

@gaomeng1900 that only works for PlaneGeometry though...? Maybe we can make an exception in the code...

What's the status of this idea? Seems like you guys know how to officially fix this. Decreasing my polygon count back down would be awesome!

Ah, maybe LightShadow.bias can help reduce the artefacts I'm seeing. This is good enough for now, but I would really really like to get Plane shadow working because it is imperative to what I'm making, and I don't want it to look as clean as possible.

@trusktr Please refrain from posting your help questions here.

https://stackoverflow.com/questions/44989568/three-js-plane-doesnt-cast-shadow

light.shadow.bias = - 0.01;
renderer.shadowMap.renderReverseSided = false;

Hello @WestLangley, I think it's nice to have these things listed here. Maybe it'll help someone.

Both "workarounds" listed in that SO question introduce other problems. I don't want to change rendering for all objects just to fix plane shadows, and thin boxes introduce unwanted artefacts on the "plane" edges in certain cases and cost more CPU power than necessary.

IMHO, this issue should be open still, and a solution should be at least desired.

Probably that solution is to make a special case for Planes and not to cull them. Eventually I'll try to see if I can do it unless someone gets to it first.

I gave @gaomeng1900's idea a shot: https://github.com/mrdoob/three.js/pull/12830

It'd be great to have this solved, so that Three can do its job in making rendering 3D simply easy for people in every way that it can.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fuzihaofzh picture fuzihaofzh  路  3Comments

akshaysrin picture akshaysrin  路  3Comments

jlaquinte picture jlaquinte  路  3Comments

Horray picture Horray  路  3Comments

filharvey picture filharvey  路  3Comments