|
@@ -11,6 +11,9 @@ THREE.ShadowMapPlugin = function ( ) {
|
|
|
_frustum = new THREE.Frustum(),
|
|
|
_projScreenMatrix = new THREE.Matrix4();
|
|
|
|
|
|
+ _min = new THREE.Vector3(),
|
|
|
+ _max = new THREE.Vector3();
|
|
|
+
|
|
|
this.init = function ( renderer ) {
|
|
|
|
|
|
_gl = renderer.context;
|
|
@@ -37,14 +40,16 @@ THREE.ShadowMapPlugin = function ( ) {
|
|
|
|
|
|
this.update = function ( scene, camera ) {
|
|
|
|
|
|
- var i, il, j, jl,
|
|
|
+ var i, il, j, jl, n,
|
|
|
|
|
|
shadowMap, shadowMatrix, shadowCamera,
|
|
|
program, buffer, material,
|
|
|
webglObject, object, light,
|
|
|
renderList,
|
|
|
|
|
|
- lights = scene.lights,
|
|
|
+ lights = [],
|
|
|
+ k = 0,
|
|
|
+
|
|
|
fog = null;
|
|
|
|
|
|
// set GL state for depth map
|
|
@@ -55,14 +60,67 @@ THREE.ShadowMapPlugin = function ( ) {
|
|
|
|
|
|
_renderer.setDepthTest( true );
|
|
|
|
|
|
+ // preprocess lights
|
|
|
+ // - skip lights that are not casting shadows
|
|
|
+ // - create virtual lights for cascaded shadow maps
|
|
|
+
|
|
|
+ for ( i = 0, il = scene.lights.length; i < il; i ++ ) {
|
|
|
+
|
|
|
+ light = scene.lights[ i ];
|
|
|
+
|
|
|
+ if ( ! light.castShadow ) continue;
|
|
|
+
|
|
|
+ if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) {
|
|
|
+
|
|
|
+ for ( n = 0; n < light.shadowCascadeCount; n ++ ) {
|
|
|
+
|
|
|
+ var virtualLight;
|
|
|
+
|
|
|
+ if ( ! light.shadowCascadeArray[ n ] ) {
|
|
|
+
|
|
|
+ virtualLight = createVirtualLight( light, n );
|
|
|
+ virtualLight.originalCamera = camera;
|
|
|
+
|
|
|
+ var gyro = new THREE.Gyroscope();
|
|
|
+ gyro.position = light.shadowCascadeOffset;
|
|
|
+
|
|
|
+ gyro.add( virtualLight );
|
|
|
+ gyro.add( virtualLight.target );
|
|
|
+
|
|
|
+ camera.add( gyro );
|
|
|
+
|
|
|
+ light.shadowCascadeArray[ n ] = virtualLight;
|
|
|
+
|
|
|
+ console.log( "Created virtualLight", virtualLight );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ virtualLight = light.shadowCascadeArray[ n ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ updateVirtualLight( light, n );
|
|
|
+
|
|
|
+ lights[ k ] = virtualLight;
|
|
|
+ k ++;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ lights[ k ] = light;
|
|
|
+ k ++;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
// render depth map
|
|
|
|
|
|
for ( i = 0, il = lights.length; i < il; i ++ ) {
|
|
|
|
|
|
light = lights[ i ];
|
|
|
|
|
|
- if ( ! light.castShadow ) continue;
|
|
|
-
|
|
|
if ( ! light.shadowMap ) {
|
|
|
|
|
|
var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat };
|
|
@@ -97,7 +155,6 @@ THREE.ShadowMapPlugin = function ( ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
-
|
|
|
if ( light.shadowCameraVisible && ! light.cameraHelper ) {
|
|
|
|
|
|
light.cameraHelper = new THREE.CameraHelper( light.shadowCamera );
|
|
@@ -105,6 +162,12 @@ THREE.ShadowMapPlugin = function ( ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ if ( light.isVirtual && virtualLight.originalCamera == camera ) {
|
|
|
+
|
|
|
+ updateShadowCamera( camera, light );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
shadowMap = light.shadowMap;
|
|
|
shadowMatrix = light.shadowMatrix;
|
|
|
shadowCamera = light.shadowCamera;
|
|
@@ -197,11 +260,11 @@ THREE.ShadowMapPlugin = function ( ) {
|
|
|
|
|
|
if ( buffer instanceof THREE.BufferGeometry ) {
|
|
|
|
|
|
- _renderer.renderBufferDirect( shadowCamera, lights, fog, material, buffer, object );
|
|
|
+ _renderer.renderBufferDirect( shadowCamera, scene.lights, fog, material, buffer, object );
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- _renderer.renderBuffer( shadowCamera, lights, fog, material, buffer, object );
|
|
|
+ _renderer.renderBuffer( shadowCamera, scene.lights, fog, material, buffer, object );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -228,7 +291,7 @@ THREE.ShadowMapPlugin = function ( ) {
|
|
|
|
|
|
object._modelViewMatrix.multiplyToArray( shadowCamera.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray );
|
|
|
|
|
|
- _renderer.renderImmediateObject( shadowCamera, lights, fog, _depthMaterial, object );
|
|
|
+ _renderer.renderImmediateObject( shadowCamera, scene.lights, fog, _depthMaterial, object );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -247,4 +310,137 @@ THREE.ShadowMapPlugin = function ( ) {
|
|
|
|
|
|
};
|
|
|
|
|
|
-};
|
|
|
+ function createVirtualLight( light, cascade ) {
|
|
|
+
|
|
|
+ var virtualLight = new THREE.DirectionalLight();
|
|
|
+
|
|
|
+ virtualLight.isVirtual = true;
|
|
|
+
|
|
|
+ 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 THREE.Vector3();
|
|
|
+ pointsFrustum[ i ] = new THREE.Vector3();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ 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
|
|
|
+
|
|
|
+ function 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
|
|
|
+
|
|
|
+ function updateShadowCamera( camera, light ) {
|
|
|
+
|
|
|
+ var shadowCamera = light.shadowCamera,
|
|
|
+ pointsFrustum = light.pointsFrustum,
|
|
|
+ pointsWorld = light.pointsWorld;
|
|
|
+
|
|
|
+ _min.set( Infinity, Infinity, Infinity );
|
|
|
+ _max.set( -Infinity, -Infinity, -Infinity );
|
|
|
+
|
|
|
+ for ( var i = 0; i < 8; i ++ ) {
|
|
|
+
|
|
|
+ var p = pointsWorld[ i ];
|
|
|
+
|
|
|
+ p.copy( pointsFrustum[ i ] );
|
|
|
+ THREE.ShadowMapPlugin.__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();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+THREE.ShadowMapPlugin.__projector = new THREE.Projector();
|