Search code examples
javascriptthree.jslightingshadows

Is there a way to prevent faces within self shadow from receiving light?


I'm working on a city scene in Three.js and so far I'm experimenting with lighting and shadow on just a single building. The building was modeled in 3DS Max, then exported to OBJ, then converted with the Python converter from command line.

You'll notice the model of the building is a single object, and I'm using self-shadowing. However, the lower part of the building that is sitting within the cast shadow is still getting lit by the yellow-white directional light. I'm guessing this is because its illumination is simply calculated by how much it is facing the light source, without accounting for anything that might be between it and the light source.

I've been searching for a method to "correct" this and make it more realistic, where the mesh would block the light from hitting things behind it, even within itself. Do I need to get into shaders or normal maps? Or something else entirely?

Also, this question may be a duplicate but no code or examples were given so I'm not sure: Light penetrating through meshes. Sorry in advance if that's a problem.

JSFiddle is here: http://jsfiddle.net/mrfolkblues/u01jwg53/

Relevant part of the code is:

var T = THREE,
    stats,
    container,
    camera = new T.PerspectiveCamera(40, window.innerWidth/window.innerHeight, 0.1, 200),
    scene = new T.Scene(),
    renderer = new T.WebGLRenderer({
        antialias: true
    }),
    radius = 35, theta = 0, thetaDelta = 0, camtarget,
    boundingbox, cube, plane, poly, polyMesh, sphere, line,
    clock = new T.Clock();

    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFShadowMap;

// construct
(function(){

    container = document.createElement( 'div' );
    document.body.appendChild( container );

    // set up renderer
    renderer.setClearColor( 0xf0f0f0 );
    renderer.setSize( window.innerWidth, window.innerHeight );
    container.appendChild(renderer.domElement);

    var light = new T.DirectionalLight( 0xfff3cb, 1.25 );
    light.position.set( 60, 25, 40 );

    var light2 = new T.DirectionalLight( 0x204fae, 1.25 );
    light2.position.set( -30, 40, 0 );

    light.castShadow = true;
    light.shadowDarkness = 0.33;
    light.shadowCameraTop = 20;
    light.shadowCameraRight = 20;
    light.shadowCameraBottom = -20;
    light.shadowCameraLeft = -20;
    light.shadowCameraNear = 0;
    light.shadowCameraFar = 200;
    light.shadowMapWidth = 2048;
    light.shadowMapHeight = 2048;
    light.shadowBias = 0.0002;

    scene.add( light );
    scene.add( light2 );

    var cube = createCube([0.5, 0.5, 0.5], [1.5, 10, 0]);

    // position camera
    camera.position.x = 0;
    camera.position.y = 20; // move camera position up
    camera.position.z = radius; // move camera out
    camtarget = cube.position;
    camera.lookAt( camtarget );

    createPlane();

    var loader = new T.JSONLoader();
    var parsed = loader.parse(window.buildingModel);

    var material = new T.MeshLambertMaterial( {
        color: 0xffffff
    } );

    var object = new THREE.Mesh( parsed.geometry, material );
    object.castShadow = true;
    object.receiveShadow = true;
    scene.add( object );

    object.position.x = 30;
    object.position.y = 0;
    object.position.z = 15;

    // add event listeners
    window.addEventListener( 'resize', onWindowResize, false );

    render();
    animate();
})();

Solution

  • What you want to do is not solved by three.js or any other 'similar' webgl based library out there. What you want is solved by Global Illumination methods which is outside the scope of three.js.