From c16ce4b26a57f7a82e6726a5f891693c5489b1a6 Mon Sep 17 00:00:00 2001 From: alteredq Date: Fri, 18 Nov 2011 23:55:35 +0100 Subject: [PATCH] Moved shadow map into separate plugin. This is still pretty rough, will need to clean it more. --- src/core/Frustum.js | 59 +++++++ src/extras/plugins/ShadowMapPlugin.js | 244 ++++++++++++++++++++++++++ 2 files changed, 303 insertions(+) create mode 100644 src/core/Frustum.js create mode 100644 src/extras/plugins/ShadowMapPlugin.js diff --git a/src/core/Frustum.js b/src/core/Frustum.js new file mode 100644 index 0000000000..ab2044a7fd --- /dev/null +++ b/src/core/Frustum.js @@ -0,0 +1,59 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Frustum = function ( ) { + + this.planes = [ + + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4() + + ]; + +}; + +THREE.Frustum.prototype.setFromMatrix = function ( m ) { + + var i, plane, + planes = this.planes; + + planes[ 0 ].set( m.n41 - m.n11, m.n42 - m.n12, m.n43 - m.n13, m.n44 - m.n14 ); + planes[ 1 ].set( m.n41 + m.n11, m.n42 + m.n12, m.n43 + m.n13, m.n44 + m.n14 ); + planes[ 2 ].set( m.n41 + m.n21, m.n42 + m.n22, m.n43 + m.n23, m.n44 + m.n24 ); + planes[ 3 ].set( m.n41 - m.n21, m.n42 - m.n22, m.n43 - m.n23, m.n44 - m.n24 ); + planes[ 4 ].set( m.n41 - m.n31, m.n42 - m.n32, m.n43 - m.n33, m.n44 - m.n34 ); + planes[ 5 ].set( m.n41 + m.n31, m.n42 + m.n32, m.n43 + m.n33, m.n44 + m.n34 ); + + for ( i = 0; i < 6; i ++ ) { + + plane = planes[ i ]; + plane.divideScalar( Math.sqrt( plane.x * plane.x + plane.y * plane.y + plane.z * plane.z ) ); + + } + +}; + +THREE.Frustum.prototype.contains = function ( object ) { + + var distance, + planes = this.planes, + matrix = object.matrixWorld, + radius = - object.geometry.boundingSphere.radius * Math.max( object.scale.x, Math.max( object.scale.y, object.scale.z ) ); + + for ( var i = 0; i < 6; i ++ ) { + + distance = planes[ i ].x * matrix.n14 + planes[ i ].y * matrix.n24 + planes[ i ].z * matrix.n34 + planes[ i ].w; + if ( distance <= radius ) return false; + + } + + return true; + +}; + diff --git a/src/extras/plugins/ShadowMapPlugin.js b/src/extras/plugins/ShadowMapPlugin.js new file mode 100644 index 0000000000..2c71e7f815 --- /dev/null +++ b/src/extras/plugins/ShadowMapPlugin.js @@ -0,0 +1,244 @@ +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ShadowMapPlugin = function ( ) { + + var _gl, _renderer, + + _depthMaterial, _depthMaterialMorph, + + _cameraLight, + + _frustum = new THREE.Frustum(), + + _projScreenMatrix = new THREE.Matrix4(); + + this.shadowMatrix = []; + this.shadowMap = []; + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + + _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); + _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); + + _depthMaterial._shadowPass = true; + _depthMaterialMorph._shadowPass = true; + + }; + + this.render = function ( scene, camera ) { + + if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return; + + this.update( scene, camera ); + + }; + + this.update = function ( scene, camera ) { + + var i, il, j, jl, + + shadowMap, shadowMatrix, + program, buffer, material, + webglObject, object, light, + + shadowIndex = 0, + + lights = scene.lights, + fog = null; + + if ( ! _cameraLight ) { + + _cameraLight = new THREE.PerspectiveCamera( _renderer.shadowCameraFov, _renderer.shadowMapWidth / _renderer.shadowMapHeight, _renderer.shadowCameraNear, _renderer.shadowCameraFar ); + + } + + for ( i = 0, il = lights.length; i < il; i ++ ) { + + light = lights[ i ]; + + if ( light.castShadow && light instanceof THREE.SpotLight ) { + + if ( ! this.shadowMap[ shadowIndex ] ) { + + var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat }; + + this.shadowMap[ shadowIndex ] = new THREE.WebGLRenderTarget( _renderer.shadowMapWidth, _renderer.shadowMapHeight, pars ); + this.shadowMatrix[ shadowIndex ] = new THREE.Matrix4(); + + } + + shadowMap = this.shadowMap[ shadowIndex ]; + shadowMatrix = this.shadowMatrix[ shadowIndex ]; + + _cameraLight.position.copy( light.position ); + _cameraLight.lookAt( light.target.position ); + + if ( _cameraLight.parent == null ) { + + console.warn( "Camera is not on the Scene. Adding it..." ); + scene.add( _cameraLight ); + + if ( _renderer.autoUpdateScene ) scene.updateMatrixWorld(); + + } + + _cameraLight.matrixWorldInverse.getInverse( _cameraLight.matrixWorld ); + + // compute shadow matrix + + shadowMatrix.set( 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.multiplySelf( _cameraLight.projectionMatrix ); + shadowMatrix.multiplySelf( _cameraLight.matrixWorldInverse ); + + // render shadow map + + if ( ! _cameraLight._viewMatrixArray ) _cameraLight._viewMatrixArray = new Float32Array( 16 ); + _cameraLight.matrixWorldInverse.flattenToArray( _cameraLight._viewMatrixArray ); + + if ( ! _cameraLight._projectionMatrixArray ) _cameraLight._projectionMatrixArray = new Float32Array( 16 ); + _cameraLight.projectionMatrix.flattenToArray( _cameraLight._projectionMatrixArray ); + + _projScreenMatrix.multiply( _cameraLight.projectionMatrix, _cameraLight.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + _renderer.setRenderTarget( shadowMap ); + + // using arbitrary clear color in depth pass + // creates variance in shadows + + _gl.clearColor( 1, 0, 1, 1 ); + //_gl.clearColor( 0, 0, 0, 1 ); + + _renderer.clear(); + + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + + _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); + + // set matrices & frustum culling + + jl = scene.__webglObjects.length; + + for ( j = 0; j < jl; j ++ ) { + + webglObject = scene.__webglObjects[ j ]; + object = webglObject.object; + + webglObject.render = false; + + if ( object.visible && object.castShadow ) { + + if ( ! ( object instanceof THREE.Mesh ) || ! ( object.frustumCulled ) || _frustum.contains( object ) ) { + + object.matrixWorld.flattenToArray( object._objectMatrixArray ); + object._modelViewMatrix.multiplyToArray( _cameraLight.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray ); + + webglObject.render = true; + + } + + } + + } + + // render regular objects + + _renderer.setDepthTest( true ); + _renderer.setBlending( THREE.NormalBlending ); // maybe blending should be just disabled? + + //_gl.cullFace( _gl.FRONT ); + + for ( j = 0; j < jl; j ++ ) { + + webglObject = scene.__webglObjects[ j ]; + + if ( webglObject.render ) { + + object = webglObject.object; + buffer = webglObject.buffer; + + _renderer.setObjectFaces( object ); + + if ( object.customDepthMaterial ) { + + material = object.customDepthMaterial; + + } else if ( object.geometry.morphTargets.length ) { + + material = _depthMaterialMorph; + + } else { + + material = _depthMaterial; + + } + + _renderer.renderBuffer( _cameraLight, lights, fog, material, buffer, object ); + + } + + } + + // set matrices and render immediate objects + + jl = scene.__webglObjectsImmediate.length; + + for ( j = 0; j < jl; j ++ ) { + + webglObject = scene.__webglObjectsImmediate[ j ]; + object = webglObject.object; + + if ( object.visible && object.castShadow ) { + + _currentGeometryGroupHash = -1; + + if( object.matrixAutoUpdate ) { + + object.matrixWorld.flattenToArray( object._objectMatrixArray ); + + } + + object._modelViewMatrix.multiplyToArray( _cameraLight.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray ); + + _renderer.setObjectFaces( object ); + + program = _renderer.setProgram( _cameraLight, lights, fog, _depthMaterial, object ); + + if ( object.immediateRenderCallback ) { + + object.immediateRenderCallback( program, _gl, _frustum ); + + } else { + + object.render( function( object ) { _renderer.renderBufferImmediate( object, program, _depthMaterial.shading ); } ); + + } + + } + + } + + //_gl.cullFace( _gl.BACK ); + + shadowIndex ++; + + } + + } + + }; + +}; \ No newline at end of file -- GitLab