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; }