Wednesday, September 19, 2012

Shadows and Compound Physijs Objects

‹prev | My Chain | next›

I went to all sorts of trouble to include shadow support in the spotlight on my Three.js / Physijs tilt-board game. And yet, no shadow:


Well, to be perfectly honest, I didn't go to that much trouble. I copied and pasted most of it:
    var spotLight = new THREE.SpotLight( 0xffffff ); 
    spotLight.position.set( -250, 250, 250 );  
    spotLight.castShadow = true;  
    spotLight.shadowMapWidth = 1024; 
    spotLight.shadowMapHeight = 1024;  
    spotLight.shadowCameraNear = 500; 
    spotLight.shadowCameraFar = 4000; 
    spotLight.shadowCameraFov = 30; 
    scene.add( spotLight );
Still, with all of that shadow stuff, I ought to actually have a shadow. However, more is needed for Three.js to cast a shadow. First up, I need to tell Three.js that my game ball casts a shadow and that the game platform receives the shadow (the shadow is cast onto the platform):
    platform.receiveShadow = true;
// ...
    ball.castShadow = true;
I still do not see a shadow.

Ah, silly me, I forgot to tell the renderer that I want it to cast shadows:
    renderer.shadowMapEnabled = true;
But even after that, I still do not have shadows. Under normal circumstances this ought to be enough to cast shadows in Three.js. It turns out that I am suffering from not one, but two self-inflicted wounds.

First, I cargo-coded the spotlight shadow definition without paying it much attention. A closer inspection reveals that I must specify a "near" shadow casting point that is between the spotlight and the ball. Since the spotlight is 300 pixels or so away from the center of the platform, I need to reduce the shadowCameraNear setting:
    var spotLight = new THREE.SpotLight( 0xffffff ); 
    spotLight.position.set( -250, 250, 250 );  
    spotLight.castShadow = true;  
    spotLight.shadowMapWidth = 1024; 
    spotLight.shadowMapHeight = 1024;  
    spotLight.shadowCameraNear = 250; 
    spotLight.shadowCameraFar = 750; 
    spotLight.shadowCameraFov = 30; 
    scene.add( spotLight );
I also up the shadowCameraFar property for good measure—there is no reason to cast shadows beyond 750 pixels away from the spotlight since the platform is only 200 pixels wide.

The second problem is that setting the receiveShadow property on the compound Physijs platform object has no effect. I have to set that property on each of the beams that comprise the platform:
    var beam = new Physijs.BoxMesh(
      new THREE.CubeGeometry(50, 2, 200),
      material,
      0
    );
    beam.receiveShadow = true;
    
    var beam2 = new Physijs.BoxMesh(
      new THREE.CubeGeometry(50, 2, 200),
      material
    );
    beam2.position.x = 75;
    beam2.receiveShadow = true;
    beam.add(beam2);
    // ....
Once I have those two issues addressed, I have my shadow:


All of this is more than a tiny hassle, though I can certainly understand the necessity of it all. Shadow casting is described in the Three.js documentation as an expensive operation, so it makes sense that the renderer needs to be explicitly told to do it. Similarly, it makes sense to keep the calculations to a minimum, so any way that can constrain the calculations would be a good thing. That said, it would be nice if it were possible to do this without all of the ceremony.

Regardless, the effect is nice, making the trouble worthwhile. That will do for a stopping point tonight. Tomorrow, I will add some visual feedback that a winning score was made.

(The game so far)


Day #514

No comments:

Post a Comment