2
votes

I'm trying to render shadows using the latest version of three.js (r102), and I'm not sure what I am doing wrong. I am using MeshPhongMaterial with castShadow and receiveShadow set on all relevant meshes, and a directional light facing towards the scene content. Could someone take a look at this and help me figure out how to get these shadows working? Thanks!

Live demo (toggle shadows in the menu): https://argonjs.github.io/three-web-layer/

Source: https://github.com/argonjs/three-web-layer

2
upvoting your question because of cool OSS project. But three-web-layer.ts needs a bit of cleanup, if you ask me. As a first draft, it is quite impressive though. - Alex Pakka
Thanks for the feedback! I literally just put it together last week, so it's definitely a first draft. What kind of cleanup are you thinking? - speigg
@spiegg three-web-layer.ts is a single long class bordering on 1k lines with a lot of stuff going on in constructor. At the very least, I would split it in 2-3 classes, each extending the other. - Alex Pakka

2 Answers

2
votes

If you add a small cube in front of your WebLayer3D, it correctly casts shadows on rendered DOM layers:

//in app.ts just after light with shadow camera:
let geometryBox = new THREE.BoxBufferGeometry( 0.01, 0.01, 0.01 )
let materialRed = new THREE.MeshPhongMaterial( {color: 0xff0000} )
let cubeSmall = new THREE.Mesh( geometryBox, materialRed )
cubeSmall.position.set( 0.1, -0.03, 0.1 )
cubeSmall.castShadow = true
cubeSmall.receiveShadow = true
scene.add( cubeSmall )

So, only the planes produced by the WebLayer3D do not cast shadows, the setup for the scene / camera / light is correct.

Update: the explanation below is not the reason, see the solution with material.shadowSide in another answer.

If you look at the tree of objects in three.js realm (i.e. traversing through children[]), starting with todoLayer - a lot of them will have castShadow at "false". You will have to re-think your strategy here. Also note, castShadow=false on parent Object3D turns it off for the children.

1
votes

I figured it out after the hint from Alex (thanks Alex!).

Basically, as strange as it seems, a plane in three.js will not cast shadows unless it is double-sided (Update: Or unless material.shadowSide is set to THREE.FrontSide). Once I set THREE.DoubleSide on the plane material, it worked as expected. Basically, for a textured plane to cast shadows, the following is needed (as of three.js r102):

  var mesh = new THREE.Mesh(
    new THREE.PlaneGeometry(1,1,2,2), 
    new THREE.MeshPhongMaterial({
      map: texture,
      side: THREE.DoubleSide, // important!
      alphaTest: 0.1,
  })
  mesh.customDepthMaterial = new THREE.MeshDepthMaterial({
    map: texture
    depthPacking: THREE.RGBADepthPacking,
    alphaTest: 0.1
  })

I also had to adjust the light's shadow bias in order to eliminate artifacts.