Dart DocumentationthreeShadowMapPlugin

ShadowMapPlugin class

@author alteredq / http://alteredqualia.com/

class ShadowMapPlugin {

 static Projector __projector = new Projector();

 Frustum _frustum;
 Matrix4 _projScreenMatrix;
 Vector3 _min, _max;

 gl.RenderingContext _gl;
 WebGLRenderer _renderer;
 WebGLMaterial _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin;

 ShadowMapPlugin() :
   _frustum = new Frustum(),
   _projScreenMatrix = new Matrix4.identity(),
   _min = new Vector3.zero(),
   _max = new Vector3.zero();


 init( WebGLRenderer renderer ) {

   _gl = renderer.context;
   _renderer = renderer;

   var depthShader = ShaderLib[ "depthRGBA" ];
   var depthUniforms = UniformsUtils.clone( depthShader["uniforms"] );

   _depthMaterial = new WebGLMaterial.from(new ShaderMaterial( fragmentShader: depthShader["fragmentShader"], vertexShader: depthShader["vertexShader"], uniforms: depthUniforms ));
   _depthMaterialMorph =  new WebGLMaterial.from(new ShaderMaterial( fragmentShader: depthShader["fragmentShader"], vertexShader: depthShader["vertexShader"], uniforms: depthUniforms, morphTargets: true ));
   _depthMaterialSkin =  new WebGLMaterial.from(new ShaderMaterial( fragmentShader: depthShader["fragmentShader"], vertexShader: depthShader["vertexShader"], uniforms: depthUniforms, skinning: true ));
   _depthMaterialMorphSkin =  new WebGLMaterial.from(new ShaderMaterial( fragmentShader: depthShader["fragmentShader"], vertexShader: depthShader["vertexShader"], uniforms: depthUniforms, morphTargets: true, skinning: true ));

   _depthMaterial.shadowPass = true;
   _depthMaterialMorph.shadowPass = true;
   _depthMaterialSkin.shadowPass = true;
   _depthMaterialMorphSkin.shadowPass = true;

 }

 render( Scene scene, Camera camera, [num width, num height] ) {

   if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return;

   update( scene, camera );

 }

 update( Scene scene, Camera camera ) {

   var i, il, j, jl, n,

   shadowMap, shadowMatrix, shadowCamera,
   program, buffer, material,
   webglObject, object, light, virtualLight,
   renderList,

   lights = [],

   fog = null;

   // set GL state for depth map

   _gl.clearColor( 1, 1, 1, 1 );
   _gl.disable( gl.BLEND );

   _gl.enable( gl.CULL_FACE );
   _gl.frontFace( gl.CCW );

   if ( _renderer.shadowMapCullFrontFaces == CullFaceFront) {

     _gl.cullFace( gl.FRONT );

   } else {

     _gl.cullFace( gl.BACK );

   }

   _renderer.setDepthTest( true );

   // preprocess lights
   //  - skip lights that are not casting shadows
   //  - create virtual lights for cascaded shadow maps
   il = scene.lights.length;
   for ( i = 0; i < il; i ++ ) {

     light = scene.lights[ i ];

     if ( ! light.castShadow ) continue;

     if ( ( light is DirectionalLight ) && light.shadowCascade ) {

       for ( n = 0; n < light.shadowCascadeCount; n ++ ) {

         if ( ! light.shadowCascadeArray[ n ] ) {

           virtualLight = createVirtualLight( light, n );
           virtualLight.originalCamera = camera;

           var gyro = new Gyroscope();
           gyro.position = light.shadowCascadeOffset;

           gyro.add( virtualLight );
           gyro.add( virtualLight.target );

           camera.add( gyro );

           light.shadowCascadeArray[ n ] = virtualLight;

           print( "Created virtualLight $virtualLight" );

         } else {

           virtualLight = light.shadowCascadeArray[ n ];

         }

         updateVirtualLight( light, n );

         lights.add(virtualLight);

       }

     } else {

       lights.add(light);

     }

   }

   // render depth map
   il = lights.length;
   for ( i = 0; i < il; i ++ ) {

     light = lights[ i ];

     if ( light.shadowMap == null ) {

       var shadowFilter = LinearFilter;

       if ( _renderer.shadowMapType == PCFSoftShadowMap ) {

         shadowFilter = NearestFilter;

       }

       light.shadowMap = new WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, minFilter: shadowFilter, magFilter: shadowFilter, format: RGBAFormat );
       light.shadowMapSize = new Vector2( light.shadowMapWidth.toDouble(), light.shadowMapHeight.toDouble() );

       light.shadowMatrix = new Matrix4.identity();

     }

     if ( light.shadowCamera == null ) {

       if ( light is SpotLight ) {

         light.shadowCamera = new PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );

       } else if ( light is DirectionalLight ) {

         light.shadowCamera = new OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar );

       } else {

         print( "Unsupported light type for shadow" );
         continue;

       }

       scene.add( light.shadowCamera );

       if ( _renderer.autoUpdateScene ) scene.updateMatrixWorld();

     }

     if ( light.shadowCameraVisible && light.cameraHelper == null ) {

       light.cameraHelper = new CameraHelper( light.shadowCamera );
       light.shadowCamera.add( light.cameraHelper );

     }

     if ( light is VirtualLight && virtualLight.originalCamera == camera ) {

       updateShadowCamera( camera, light );

     }

     shadowMap = light.shadowMap;
     shadowMatrix = light.shadowMatrix;
     shadowCamera = light.shadowCamera;

     shadowCamera.position = light.matrixWorld.getTranslation();
     shadowCamera.lookAt( light.target.matrixWorld.getTranslation() );
     shadowCamera.updateMatrixWorld();

     shadowCamera.matrixWorldInverse.copyInverse(shadowCamera.matrixWorld);

     if ( light.cameraHelper != null ) light.cameraHelper.visible = light.shadowCameraVisible;
     if ( light.shadowCameraVisible ) light.cameraHelper.update();

     // compute shadow matrix

     shadowMatrix.setValues( 0.5, 0.0, 0.0, 0.5,
               0.0, 0.5, 0.0, 0.5,
               0.0, 0.0, 0.5, 0.5,
               0.0, 0.0, 0.0, 1.0 );

     shadowMatrix.multiply( shadowCamera.projectionMatrix );
     shadowMatrix.multiply( shadowCamera.matrixWorldInverse );

     // update camera matrices and frustum

     _projScreenMatrix = shadowCamera.projectionMatrix * shadowCamera.matrixWorldInverse;
     _frustum.setFromMatrix( _projScreenMatrix );

     // render shadow map

     _renderer.setRenderTarget( shadowMap );
     _renderer.clear();

     // set object matrices & frustum culling

     renderList = scene["__webglObjects"];

     jl = renderList.length;
     for ( j = 0; j < jl; j ++ ) {

       webglObject = renderList[ j ];
       object = webglObject.object;

       webglObject.render = false;

       if ( object.visible && object.castShadow ) {

         if ( ! ( object is Mesh ) || (object is ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.contains( object ) ) {

           webglObject._modelViewMatrix = shadowCamera.matrixWorldInverse * object.matrixWorld;

           webglObject.render = true;

         }

       }

     }

     // render regular objects

     var objectMaterial, useMorphing, useSkinning;

     jl = renderList.length;
     for ( j = 0; j < jl; j ++ ) {

       webglObject = renderList[ j ];

       if ( webglObject.render ) {

         object = webglObject.object;
         buffer = webglObject.buffer;

         // culling is overriden globally for all objects
         // while rendering depth map

         // need to deal with MeshFaceMaterial somehow
         // in that case just use the first of geometry.materials for now
         // (proper solution would require to break objects by materials
         //  similarly to regular rendering and then set corresponding
         //  depth materials per each chunk instead of just once per object)

         objectMaterial = getObjectMaterial( object );

         useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
         useSkinning = object is SkinnedMesh && objectMaterial.skinning;

         if ( object.customDepthMaterial != null ) {

           material = object.customDepthMaterial;

         } else if ( useSkinning ) {

           material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;

         } else if ( useMorphing ) {

           material = _depthMaterialMorph;

         } else {

           material = _depthMaterial;

         }

         if ( buffer is BufferGeometry ) {

           _renderer.renderBufferDirect( shadowCamera, scene.lights, fog, material, buffer, object );

         } else {

           _renderer.renderBuffer( shadowCamera, scene.lights, fog, material, buffer, object );

         }

       }

     }

     // set matrices and render immediate objects

     renderList = scene["__webglObjectsImmediate"];

     jl = renderList.length;
     for ( j = 0; j < jl; j ++ ) {

       webglObject = renderList[ j ];
       object = webglObject.object;

       if ( object.visible && object.castShadow ) {

         object._modelViewMatrix.multiply( shadowCamera.matrixWorldInverse, object.matrixWorld );

         _renderer.renderImmediateObject( shadowCamera, scene.lights, fog, _depthMaterial, object );

       }

     }

   }

   // restore GL state

   var clearColor = _renderer._clearColor;
   var clearAlpha = _renderer._clearAlpha;

   _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
   _gl.enable( gl.BLEND );

   if ( _renderer.shadowMapCullFrontFaces == CullFaceFront ) {

     _gl.cullFace( gl.BACK );

   }

 }

 createVirtualLight( light, cascade ) {

   var virtualLight = new VirtualLight();

   virtualLight.onlyShadow = true;
   virtualLight.castShadow = true;

   virtualLight.shadowCameraNear = light.shadowCameraNear;
   virtualLight.shadowCameraFar = light.shadowCameraFar;

   virtualLight.shadowCameraLeft = light.shadowCameraLeft;
   virtualLight.shadowCameraRight = light.shadowCameraRight;
   virtualLight.shadowCameraBottom = light.shadowCameraBottom;
   virtualLight.shadowCameraTop = light.shadowCameraTop;

   virtualLight.shadowCameraVisible = light.shadowCameraVisible;

   virtualLight.shadowDarkness = light.shadowDarkness;

   virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
   virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ];
   virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ];

   virtualLight.pointsWorld = [];
   virtualLight.pointsFrustum = [];

   var pointsWorld = virtualLight.pointsWorld,
     pointsFrustum = virtualLight.pointsFrustum;

   for ( var i = 0; i < 8; i ++ ) {

     pointsWorld[ i ] = new Vector3.zero();
     pointsFrustum[ i ] = new Vector3.zero();

   }

   var nearZ = light.shadowCascadeNearZ[ cascade ];
   var farZ = light.shadowCascadeFarZ[ cascade ];

   pointsFrustum[ 0 ].set( -1, -1, nearZ );
   pointsFrustum[ 1 ].set(  1, -1, nearZ );
   pointsFrustum[ 2 ].set( -1,  1, nearZ );
   pointsFrustum[ 3 ].set(  1,  1, nearZ );

   pointsFrustum[ 4 ].set( -1, -1, farZ );
   pointsFrustum[ 5 ].set(  1, -1, farZ );
   pointsFrustum[ 6 ].set( -1,  1, farZ );
   pointsFrustum[ 7 ].set(  1,  1, farZ );

   return virtualLight;

 }

 // Synchronize virtual light with the original light

 updateVirtualLight( light, cascade ) {

   var virtualLight = light.shadowCascadeArray[ cascade ];

   virtualLight.position.copy( light.position );
   virtualLight.target.position.copy( light.target.position );
   virtualLight.lookAt( virtualLight.target );

   virtualLight.shadowCameraVisible = light.shadowCameraVisible;
   virtualLight.shadowDarkness = light.shadowDarkness;

   virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];

   var nearZ = light.shadowCascadeNearZ[ cascade ];
   var farZ = light.shadowCascadeFarZ[ cascade ];

   var pointsFrustum = virtualLight.pointsFrustum;

   pointsFrustum[ 0 ].z = nearZ;
   pointsFrustum[ 1 ].z = nearZ;
   pointsFrustum[ 2 ].z = nearZ;
   pointsFrustum[ 3 ].z = nearZ;

   pointsFrustum[ 4 ].z = farZ;
   pointsFrustum[ 5 ].z = farZ;
   pointsFrustum[ 6 ].z = farZ;
   pointsFrustum[ 7 ].z = farZ;

 }

 // Fit shadow camera's ortho frustum to camera frustum

 updateShadowCamera( camera, light ) {

   var shadowCamera = light.shadowCamera,
     pointsFrustum = light.pointsFrustum,
     pointsWorld = light.pointsWorld;

   _min.setValues( double.INFINITY, double.INFINITY, double.INFINITY );
   _max.setValues( double.NEGATIVE_INFINITY, double.NEGATIVE_INFINITY, double.NEGATIVE_INFINITY );

   for ( var i = 0; i < 8; i ++ ) {

     var p = pointsWorld[ i ];

     p.copy( pointsFrustum[ i ] );
     __projector.unprojectVector( p, camera );

     shadowCamera.matrixWorldInverse.multiplyVector3( p );

     if ( p.x < _min.x ) _min.x = p.x;
     if ( p.x > _max.x ) _max.x = p.x;

     if ( p.y < _min.y ) _min.y = p.y;
     if ( p.y > _max.y ) _max.y = p.y;

     if ( p.z < _min.z ) _min.z = p.z;
     if ( p.z > _max.z ) _max.z = p.z;

   }

   shadowCamera.left = _min.x;
   shadowCamera.right = _max.x;
   shadowCamera.top = _max.y;
   shadowCamera.bottom = _min.y;

   // can't really fit near/far
   //shadowCamera.near = _min.z;
   //shadowCamera.far = _max.z;

   shadowCamera.updateProjectionMatrix();

 }

 // For the moment just ignore objects that have multiple materials with different animation methods
 // Only the first material will be taken into account for deciding which depth material to use for shadow maps

 getObjectMaterial( object ) {

   return object.material is MeshFaceMaterial ? object.geometry.materials[ 0 ] : object.material;

 }

}

Constructors

new ShadowMapPlugin() #

Creates a new Object instance.

Object instances have no meaningful state, and are only useful through their identity. An Object instance is equal to itself only.

docs inherited from Object
ShadowMapPlugin() :
 _frustum = new Frustum(),
 _projScreenMatrix = new Matrix4.identity(),
 _min = new Vector3.zero(),
 _max = new Vector3.zero();

Methods

dynamic createVirtualLight(light, cascade) #

createVirtualLight( light, cascade ) {

 var virtualLight = new VirtualLight();

 virtualLight.onlyShadow = true;
 virtualLight.castShadow = true;

 virtualLight.shadowCameraNear = light.shadowCameraNear;
 virtualLight.shadowCameraFar = light.shadowCameraFar;

 virtualLight.shadowCameraLeft = light.shadowCameraLeft;
 virtualLight.shadowCameraRight = light.shadowCameraRight;
 virtualLight.shadowCameraBottom = light.shadowCameraBottom;
 virtualLight.shadowCameraTop = light.shadowCameraTop;

 virtualLight.shadowCameraVisible = light.shadowCameraVisible;

 virtualLight.shadowDarkness = light.shadowDarkness;

 virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
 virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ];
 virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ];

 virtualLight.pointsWorld = [];
 virtualLight.pointsFrustum = [];

 var pointsWorld = virtualLight.pointsWorld,
   pointsFrustum = virtualLight.pointsFrustum;

 for ( var i = 0; i < 8; i ++ ) {

   pointsWorld[ i ] = new Vector3.zero();
   pointsFrustum[ i ] = new Vector3.zero();

 }

 var nearZ = light.shadowCascadeNearZ[ cascade ];
 var farZ = light.shadowCascadeFarZ[ cascade ];

 pointsFrustum[ 0 ].set( -1, -1, nearZ );
 pointsFrustum[ 1 ].set(  1, -1, nearZ );
 pointsFrustum[ 2 ].set( -1,  1, nearZ );
 pointsFrustum[ 3 ].set(  1,  1, nearZ );

 pointsFrustum[ 4 ].set( -1, -1, farZ );
 pointsFrustum[ 5 ].set(  1, -1, farZ );
 pointsFrustum[ 6 ].set( -1,  1, farZ );
 pointsFrustum[ 7 ].set(  1,  1, farZ );

 return virtualLight;

}

dynamic getObjectMaterial(object) #

getObjectMaterial( object ) {

 return object.material is MeshFaceMaterial ? object.geometry.materials[ 0 ] : object.material;

}

dynamic init(WebGLRenderer renderer) #

init( WebGLRenderer renderer ) {

 _gl = renderer.context;
 _renderer = renderer;

 var depthShader = ShaderLib[ "depthRGBA" ];
 var depthUniforms = UniformsUtils.clone( depthShader["uniforms"] );

 _depthMaterial = new WebGLMaterial.from(new ShaderMaterial( fragmentShader: depthShader["fragmentShader"], vertexShader: depthShader["vertexShader"], uniforms: depthUniforms ));
 _depthMaterialMorph =  new WebGLMaterial.from(new ShaderMaterial( fragmentShader: depthShader["fragmentShader"], vertexShader: depthShader["vertexShader"], uniforms: depthUniforms, morphTargets: true ));
 _depthMaterialSkin =  new WebGLMaterial.from(new ShaderMaterial( fragmentShader: depthShader["fragmentShader"], vertexShader: depthShader["vertexShader"], uniforms: depthUniforms, skinning: true ));
 _depthMaterialMorphSkin =  new WebGLMaterial.from(new ShaderMaterial( fragmentShader: depthShader["fragmentShader"], vertexShader: depthShader["vertexShader"], uniforms: depthUniforms, morphTargets: true, skinning: true ));

 _depthMaterial.shadowPass = true;
 _depthMaterialMorph.shadowPass = true;
 _depthMaterialSkin.shadowPass = true;
 _depthMaterialMorphSkin.shadowPass = true;

}

dynamic render(Scene scene, Camera camera, [num width, num height]) #

render( Scene scene, Camera camera, [num width, num height] ) {

 if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return;

 update( scene, camera );

}

dynamic update(Scene scene, Camera camera) #

update( Scene scene, Camera camera ) {

 var i, il, j, jl, n,

 shadowMap, shadowMatrix, shadowCamera,
 program, buffer, material,
 webglObject, object, light, virtualLight,
 renderList,

 lights = [],

 fog = null;

 // set GL state for depth map

 _gl.clearColor( 1, 1, 1, 1 );
 _gl.disable( gl.BLEND );

 _gl.enable( gl.CULL_FACE );
 _gl.frontFace( gl.CCW );

 if ( _renderer.shadowMapCullFrontFaces == CullFaceFront) {

   _gl.cullFace( gl.FRONT );

 } else {

   _gl.cullFace( gl.BACK );

 }

 _renderer.setDepthTest( true );

 // preprocess lights
 //  - skip lights that are not casting shadows
 //  - create virtual lights for cascaded shadow maps
 il = scene.lights.length;
 for ( i = 0; i < il; i ++ ) {

   light = scene.lights[ i ];

   if ( ! light.castShadow ) continue;

   if ( ( light is DirectionalLight ) && light.shadowCascade ) {

     for ( n = 0; n < light.shadowCascadeCount; n ++ ) {

       if ( ! light.shadowCascadeArray[ n ] ) {

         virtualLight = createVirtualLight( light, n );
         virtualLight.originalCamera = camera;

         var gyro = new Gyroscope();
         gyro.position = light.shadowCascadeOffset;

         gyro.add( virtualLight );
         gyro.add( virtualLight.target );

         camera.add( gyro );

         light.shadowCascadeArray[ n ] = virtualLight;

         print( "Created virtualLight $virtualLight" );

       } else {

         virtualLight = light.shadowCascadeArray[ n ];

       }

       updateVirtualLight( light, n );

       lights.add(virtualLight);

     }

   } else {

     lights.add(light);

   }

 }

 // render depth map
 il = lights.length;
 for ( i = 0; i < il; i ++ ) {

   light = lights[ i ];

   if ( light.shadowMap == null ) {

     var shadowFilter = LinearFilter;

     if ( _renderer.shadowMapType == PCFSoftShadowMap ) {

       shadowFilter = NearestFilter;

     }

     light.shadowMap = new WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, minFilter: shadowFilter, magFilter: shadowFilter, format: RGBAFormat );
     light.shadowMapSize = new Vector2( light.shadowMapWidth.toDouble(), light.shadowMapHeight.toDouble() );

     light.shadowMatrix = new Matrix4.identity();

   }

   if ( light.shadowCamera == null ) {

     if ( light is SpotLight ) {

       light.shadowCamera = new PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );

     } else if ( light is DirectionalLight ) {

       light.shadowCamera = new OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar );

     } else {

       print( "Unsupported light type for shadow" );
       continue;

     }

     scene.add( light.shadowCamera );

     if ( _renderer.autoUpdateScene ) scene.updateMatrixWorld();

   }

   if ( light.shadowCameraVisible && light.cameraHelper == null ) {

     light.cameraHelper = new CameraHelper( light.shadowCamera );
     light.shadowCamera.add( light.cameraHelper );

   }

   if ( light is VirtualLight && virtualLight.originalCamera == camera ) {

     updateShadowCamera( camera, light );

   }

   shadowMap = light.shadowMap;
   shadowMatrix = light.shadowMatrix;
   shadowCamera = light.shadowCamera;

   shadowCamera.position = light.matrixWorld.getTranslation();
   shadowCamera.lookAt( light.target.matrixWorld.getTranslation() );
   shadowCamera.updateMatrixWorld();

   shadowCamera.matrixWorldInverse.copyInverse(shadowCamera.matrixWorld);

   if ( light.cameraHelper != null ) light.cameraHelper.visible = light.shadowCameraVisible;
   if ( light.shadowCameraVisible ) light.cameraHelper.update();

   // compute shadow matrix

   shadowMatrix.setValues( 0.5, 0.0, 0.0, 0.5,
             0.0, 0.5, 0.0, 0.5,
             0.0, 0.0, 0.5, 0.5,
             0.0, 0.0, 0.0, 1.0 );

   shadowMatrix.multiply( shadowCamera.projectionMatrix );
   shadowMatrix.multiply( shadowCamera.matrixWorldInverse );

   // update camera matrices and frustum

   _projScreenMatrix = shadowCamera.projectionMatrix * shadowCamera.matrixWorldInverse;
   _frustum.setFromMatrix( _projScreenMatrix );

   // render shadow map

   _renderer.setRenderTarget( shadowMap );
   _renderer.clear();

   // set object matrices & frustum culling

   renderList = scene["__webglObjects"];

   jl = renderList.length;
   for ( j = 0; j < jl; j ++ ) {

     webglObject = renderList[ j ];
     object = webglObject.object;

     webglObject.render = false;

     if ( object.visible && object.castShadow ) {

       if ( ! ( object is Mesh ) || (object is ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.contains( object ) ) {

         webglObject._modelViewMatrix = shadowCamera.matrixWorldInverse * object.matrixWorld;

         webglObject.render = true;

       }

     }

   }

   // render regular objects

   var objectMaterial, useMorphing, useSkinning;

   jl = renderList.length;
   for ( j = 0; j < jl; j ++ ) {

     webglObject = renderList[ j ];

     if ( webglObject.render ) {

       object = webglObject.object;
       buffer = webglObject.buffer;

       // culling is overriden globally for all objects
       // while rendering depth map

       // need to deal with MeshFaceMaterial somehow
       // in that case just use the first of geometry.materials for now
       // (proper solution would require to break objects by materials
       //  similarly to regular rendering and then set corresponding
       //  depth materials per each chunk instead of just once per object)

       objectMaterial = getObjectMaterial( object );

       useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
       useSkinning = object is SkinnedMesh && objectMaterial.skinning;

       if ( object.customDepthMaterial != null ) {

         material = object.customDepthMaterial;

       } else if ( useSkinning ) {

         material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;

       } else if ( useMorphing ) {

         material = _depthMaterialMorph;

       } else {

         material = _depthMaterial;

       }

       if ( buffer is BufferGeometry ) {

         _renderer.renderBufferDirect( shadowCamera, scene.lights, fog, material, buffer, object );

       } else {

         _renderer.renderBuffer( shadowCamera, scene.lights, fog, material, buffer, object );

       }

     }

   }

   // set matrices and render immediate objects

   renderList = scene["__webglObjectsImmediate"];

   jl = renderList.length;
   for ( j = 0; j < jl; j ++ ) {

     webglObject = renderList[ j ];
     object = webglObject.object;

     if ( object.visible && object.castShadow ) {

       object._modelViewMatrix.multiply( shadowCamera.matrixWorldInverse, object.matrixWorld );

       _renderer.renderImmediateObject( shadowCamera, scene.lights, fog, _depthMaterial, object );

     }

   }

 }

 // restore GL state

 var clearColor = _renderer._clearColor;
 var clearAlpha = _renderer._clearAlpha;

 _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
 _gl.enable( gl.BLEND );

 if ( _renderer.shadowMapCullFrontFaces == CullFaceFront ) {

   _gl.cullFace( gl.BACK );

 }

}

dynamic updateShadowCamera(camera, light) #

updateShadowCamera( camera, light ) {

 var shadowCamera = light.shadowCamera,
   pointsFrustum = light.pointsFrustum,
   pointsWorld = light.pointsWorld;

 _min.setValues( double.INFINITY, double.INFINITY, double.INFINITY );
 _max.setValues( double.NEGATIVE_INFINITY, double.NEGATIVE_INFINITY, double.NEGATIVE_INFINITY );

 for ( var i = 0; i < 8; i ++ ) {

   var p = pointsWorld[ i ];

   p.copy( pointsFrustum[ i ] );
   __projector.unprojectVector( p, camera );

   shadowCamera.matrixWorldInverse.multiplyVector3( p );

   if ( p.x < _min.x ) _min.x = p.x;
   if ( p.x > _max.x ) _max.x = p.x;

   if ( p.y < _min.y ) _min.y = p.y;
   if ( p.y > _max.y ) _max.y = p.y;

   if ( p.z < _min.z ) _min.z = p.z;
   if ( p.z > _max.z ) _max.z = p.z;

 }

 shadowCamera.left = _min.x;
 shadowCamera.right = _max.x;
 shadowCamera.top = _max.y;
 shadowCamera.bottom = _min.y;

 // can't really fit near/far
 //shadowCamera.near = _min.z;
 //shadowCamera.far = _max.z;

 shadowCamera.updateProjectionMatrix();

}

dynamic updateVirtualLight(light, cascade) #

updateVirtualLight( light, cascade ) {

 var virtualLight = light.shadowCascadeArray[ cascade ];

 virtualLight.position.copy( light.position );
 virtualLight.target.position.copy( light.target.position );
 virtualLight.lookAt( virtualLight.target );

 virtualLight.shadowCameraVisible = light.shadowCameraVisible;
 virtualLight.shadowDarkness = light.shadowDarkness;

 virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];

 var nearZ = light.shadowCascadeNearZ[ cascade ];
 var farZ = light.shadowCascadeFarZ[ cascade ];

 var pointsFrustum = virtualLight.pointsFrustum;

 pointsFrustum[ 0 ].z = nearZ;
 pointsFrustum[ 1 ].z = nearZ;
 pointsFrustum[ 2 ].z = nearZ;
 pointsFrustum[ 3 ].z = nearZ;

 pointsFrustum[ 4 ].z = farZ;
 pointsFrustum[ 5 ].z = farZ;
 pointsFrustum[ 6 ].z = farZ;
 pointsFrustum[ 7 ].z = farZ;

}