-
-
Save wlalele/1b5a0471f0b1bf75afee to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Extended version of THREE.ShadowMapPluginVSM using VSM shadow mapping. | |
* @author Dusan Maliarik | |
*/ | |
THREE.ShadowMapPluginVSM = function ( ) { | |
var _gl, | |
_renderer, | |
_depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, | |
_frustum = new THREE.Frustum(), | |
_projScreenMatrix = new THREE.Matrix4(), | |
_min = new THREE.Vector3(), | |
_max = new THREE.Vector3(); | |
this.init = function ( renderer ) { | |
_gl = renderer.context; | |
_renderer = renderer; | |
var depthShader = THREE.ShaderLib[ "depthRGBAsqrd" ]; | |
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 } ); | |
_depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } ); | |
_depthMaterialMorphSkin = new THREE.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; | |
}; | |
this.render = function ( scene, camera ) { | |
if ( ! _renderer.shadowMapEnabled ) return; | |
if ( ! _renderer.shadowMapAutoUpdate && this.updated_ ) return; | |
this.updated_ = true; | |
this.update( scene, camera ); | |
}; | |
this.update = function ( scene, camera ) { | |
var i, il, j, jl, n, | |
shadowMap, shadowMatrix, shadowCamera, | |
program, buffer, material, | |
webglObject, object, light, | |
renderList, | |
lights = [], | |
k = 0, | |
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 ) { | |
_gl.cullFace( _gl.FRONT ); | |
} else { | |
_gl.cullFace( _gl.BACK ); | |
} | |
//_renderer.setDepthTest( true ); // @TODO Trouver une équivalence | |
// preprocess lights | |
// - skip lights that are not casting shadows | |
for ( i = 0, il = scene.lights.length; i < il; i ++ ) { | |
light = scene.lights[ i ]; | |
if ( ! light.castShadow ) continue; | |
lights[ k ] = light; | |
k ++; | |
} | |
// render depth map | |
for ( i = 0, il = lights.length; i < il; i ++ ) { | |
light = lights[ i ]; | |
var hblur = new THREE.ShaderPass( THREE.ShaderLib["blur"] ), | |
vblur = new THREE.ShaderPass( THREE.ShaderLib["blur"] ), | |
texelSize = new THREE.Vector2(1 / light.shadowMapWidth, 1 / light.shadowMapHeight); | |
hblur.uniforms['orientation'].value = 0; | |
vblur.uniforms['orientation'].value = 1; | |
hblur.uniforms['amount'].value = 3; | |
vblur.uniforms['amount'].value = 3; | |
hblur.uniforms['texelSize'].value = texelSize; | |
vblur.uniforms['texelSize'].value = texelSize; | |
if ( ! light.shadowMap ) { | |
var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat }; | |
light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); | |
light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); | |
light.shadowMatrix = new THREE.Matrix4(); | |
} | |
if ( ! light.shadowMapComposer ) { | |
light.shadowMapComposer = new THREE.EffectComposer( _renderer, light.shadowMap ); | |
// light.shadowMapComposer.addPass(hblur); | |
// light.shadowMapComposer.addPass(vblur); | |
} | |
if ( ! light.shadowCamera ) { | |
if ( light instanceof THREE.SpotLight ) { | |
light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); | |
} else if ( light instanceof THREE.DirectionalLight ) { | |
light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); | |
} else { | |
console.error( "Unsupported light type for shadow" ); | |
continue; | |
} | |
scene.add( light.shadowCamera ); | |
if ( _renderer.autoUpdateScene ) scene.updateMatrixWorld(); | |
} | |
if ( light.shadowCameraVisible && ! light.cameraHelper ) { | |
light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); | |
light.shadowCamera.add( light.cameraHelper ); | |
} | |
if ( light.isVirtual && virtualLight.originalCamera == camera ) { | |
updateShadowCamera( camera, light ); | |
} | |
shadowMap = light.shadowMap; | |
shadowMatrix = light.shadowMatrix; | |
shadowCamera = light.shadowCamera; | |
//shadowCamera.position.copy( light.matrixWorld.getPosition() ); | |
var lightMatrixWorld = new THREE.Vector3(); | |
shadowCamera.position.copy( lightMatrixWorld.setFromMatrixPosition( light.matrixWorld ) ); | |
//shadowCamera.lookAt( light.target.matrixWorld.getPosition() ); | |
var lightTargetMatrixWorld = new THREE.Vector3(); | |
shadowCamera.lookAt( lightTargetMatrixWorld.setFromMatrixPosition( light.target.matrixWorld ) ); | |
shadowCamera.updateMatrixWorld(); | |
shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); | |
if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; | |
if ( light.shadowCameraVisible ) light.cameraHelper.update(); | |
// 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.multiply( shadowCamera.projectionMatrix ); // before: multiplySelf | |
shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); // before: multiplySelf | |
// update camera matrices and frustum | |
if ( ! shadowCamera._viewMatrixArray ) shadowCamera._viewMatrixArray = new Float32Array( 16 ); | |
if ( ! shadowCamera._projectionMatrixArray ) shadowCamera._projectionMatrixArray = new Float32Array( 16 ); | |
shadowCamera.matrixWorldInverse.flattenToArrayOffset( shadowCamera._viewMatrixArray ); // before: flattenToArray | |
shadowCamera.projectionMatrix.flattenToArrayOffset( shadowCamera._projectionMatrixArray ); // before: flattenToArray | |
_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); // before: multiply | |
_frustum.setFromMatrix( _projScreenMatrix ); | |
// render shadow map | |
_renderer.setRenderTarget( shadowMap ); | |
_renderer.clear(); | |
// set object matrices & frustum culling | |
renderList = scene.objects; | |
for ( j = 0, jl = renderList.length; j < jl; j ++ ) { | |
webglObject = renderList[ j ]; | |
object = webglObject.object; | |
webglObject.render = false; | |
if ( object.visible && object.castShadow ) { | |
if ( ! ( object instanceof THREE.Mesh ) || ! ( object.frustumCulled ) || _frustum.contains( object ) ) { | |
object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); // before: multiply | |
webglObject.render = true; | |
} | |
} | |
} | |
// render regular objects | |
for ( j = 0, jl = renderList.length; 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 | |
if ( object.customDepthMaterial ) { | |
material = object.customDepthMaterial; | |
} else if ( object instanceof THREE.SkinnedMesh ) { | |
material = object.geometry.morphTargets.length ? _depthMaterialMorphSkin : _depthMaterialSkin; | |
} else if ( object.geometry.morphTargets.length ) { | |
material = _depthMaterialMorph; | |
} else { | |
material = _depthMaterial; | |
} | |
if ( buffer instanceof THREE.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.objectsImmediate; | |
for ( j = 0, jl = renderList.length; j < jl; j ++ ) { | |
webglObject = renderList[ j ]; | |
object = webglObject.object; | |
if ( object.visible && object.castShadow ) { | |
object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); // before: multiply | |
_renderer.renderImmediateObject( shadowCamera, scene.lights, fog, _depthMaterial, object ); | |
} | |
} | |
light.shadowMapComposer.render(); | |
} | |
// restore GL state | |
var clearColor = _renderer.getClearColor(), | |
clearAlpha = _renderer.getClearAlpha(); | |
_gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); | |
_gl.enable( _gl.BLEND ); | |
if ( _renderer.shadowMapCullFrontFaces ) { | |
_gl.cullFace( _gl.BACK ); | |
} | |
}; | |
// 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.ShadowMapPluginVSM.__projector.unproject( p, camera ); // avant : unprojectVector | |
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.ShadowMapPluginVSM.__projector = new THREE.Projector(); | |
THREE.ShaderLib["depthRGBAsqrd"] = { | |
uniforms: THREE.ShaderLib["depthRGBA"].uniforms, | |
vertexShader: [ | |
THREE.ShaderChunk[ "morphtarget_pars_vertex" ], | |
THREE.ShaderChunk[ "skinning_pars_vertex" ], | |
"varying vec4 vPosition;", | |
"void main() {", | |
THREE.ShaderChunk[ "skinbase_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_vertex" ], | |
THREE.ShaderChunk[ "skinning_vertex" ], | |
THREE.ShaderChunk[ "default_vertex" ], | |
"vPosition = mvPosition;", | |
"}" | |
].join("\n"), | |
fragmentShader: [ | |
// "const float LinearDepthConstant = 1.0 / (Far - Near);", | |
"varying vec4 vPosition;", | |
"vec2 packHalf( const in float depth ) {", | |
"const vec2 bias = vec2(1.0 / 255.0, 0.0);", | |
"vec2 colour = vec2(depth, fract(depth * 255.0));", | |
"return colour - (colour.yy * bias);", | |
"}", | |
"void main() {", | |
"float linearDepth = length(vPosition);", // * LinearDepthConstant | |
"float moment1 = gl_FragCoord.z;", | |
"float moment2 = moment1 * moment1;", | |
"gl_FragData[ 0 ] = vec4(packHalf(moment1), packHalf(moment2));", | |
"}" | |
].join("\n") | |
}; | |
THREE.ShaderLib["blur"] = { | |
uniforms: { | |
'tDiffuse': { | |
type: 't', | |
texture: null, | |
value: 0 | |
}, | |
'amount': { | |
type: 'i', | |
value: 3 | |
}, | |
'orientation': { | |
type: 'i', | |
value: 0 | |
}, | |
'texelSize': { | |
type: 'v2', | |
value: new THREE.Vector2(0, 0) | |
} | |
}, | |
vertexShader: [ | |
"varying vec2 vUv;", | |
"void main() {", | |
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", | |
"vUv = vec2( uv.x, 1.0 - uv.y );", | |
"}" | |
].join("\n"), | |
fragmentShader: [ | |
"varying vec2 vUv;", | |
"uniform sampler2D tDiffuse;", | |
"uniform int amount;", | |
"uniform int orientation;", | |
"uniform vec2 texelSize;", | |
"void main() {", | |
"float halfBlur = float(amount) * 0.5;", | |
"vec4 colour;", | |
"if ( orientation == 0 ) {", | |
// blur horizontal | |
"for (int i = 0; i < 10; ++i) {", | |
"if ( i >= amount )", | |
"break;", | |
"float offset = float(i) - halfBlur;", | |
"colour += texture2D(tDiffuse, vUv + vec2(offset * texelSize.x, 0.0));", | |
"}", | |
"} else {", | |
// blur vertical | |
"for (int i = 0; i < 10; ++i) {", | |
"if ( i >= amount )", | |
"break;", | |
"float offset = float(i) - halfBlur;", | |
"colour += texture2D(tDiffuse, vUv + vec2(0.0, offset * texelSize.y));", | |
"}", | |
"}", | |
// Calculate average | |
"colour = colour / float(amount);", | |
// "gl_FragColor = colour;", | |
"gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);", | |
"}" | |
].join("\n") | |
}; | |
THREE.ShaderChunk["shadowmap_pars_fragment"] = [ | |
"#ifdef USE_SHADOWMAP", | |
"uniform sampler2D shadowMap[ MAX_SHADOWS ];", | |
"uniform vec2 shadowMapSize[ MAX_SHADOWS ];", | |
"uniform float shadowDarkness[ MAX_SHADOWS ];", | |
"uniform float shadowBias[ MAX_SHADOWS ];", | |
"varying vec4 vShadowCoord[ MAX_SHADOWS ];", | |
"float unpackHalf (vec2 colour) {", | |
"return colour.x + (colour.y / 255.0);", | |
"}", | |
"float ChebychevInequality (vec2 moments, float t) {", | |
// No shadow if depth of fragment is in front | |
"if ( t <= moments.x )", | |
"return 1.0;", | |
// Calculate variance, which is actually the amount of | |
// error due to precision loss from fp32 to RG/BA | |
// (moment1 / moment2) | |
"float variance = moments.y - (moments.x * moments.x);", | |
"variance = max(variance, 0.02);", | |
// Calculate the upper bound | |
"float d = t - moments.x;", | |
"return variance / (variance + d * d);", | |
"}", | |
"#endif" | |
].join("\n"); | |
THREE.ShaderChunk[ "shadowmap_fragment" ] = [ | |
"#ifdef USE_SHADOWMAP", | |
"#ifdef SHADOWMAP_DEBUG", | |
"vec3 frustumColors[3];", | |
"frustumColors[0] = vec3( 1.0, 0.5, 0.0 );", | |
"frustumColors[1] = vec3( 0.0, 1.0, 0.8 );", | |
"frustumColors[2] = vec3( 0.0, 0.5, 1.0 );", | |
"#endif", | |
"#ifdef SHADOWMAP_CASCADE", | |
"int inFrustumCount = 0;", | |
"#endif", | |
"vec3 shadowColor = vec3( 1.0 );", | |
"for( int i = 0; i < MAX_SHADOWS; i ++ ) {", | |
"vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;", | |
"bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );", | |
"bool inFrustum = all( inFrustumVec );", | |
"bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );", | |
"bool frustumTest = all( frustumTestVec );", | |
"if ( frustumTest ) {", | |
"shadowCoord.z += shadowBias[ i ];", | |
"vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );", | |
"vec2 moments = vec2(unpackHalf(rgbaDepth.xy), unpackHalf(rgbaDepth.zw));", | |
"float shadow = ChebychevInequality(moments, shadowCoord.z);", | |
"if ( shadow < 0.999999 )", | |
"shadowColor = vec3( 1.0 - shadowDarkness[ i ] );", | |
"}", | |
"#ifdef SHADOWMAP_DEBUG", | |
"#ifdef SHADOWMAP_CASCADE", | |
"if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];", | |
"#else", | |
"if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];", | |
"#endif", | |
"#endif", | |
"}", | |
"#ifdef GAMMA_OUTPUT", | |
"shadowColor *= shadowColor;", | |
"#endif", | |
"gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;", | |
"#endif" | |
].join("\n"); | |
THREE.ShaderLib["lambert"] = { | |
uniforms: THREE.ShaderLib["lambert"].uniforms, | |
vertexShader: THREE.ShaderLib["lambert"].vertexShader, | |
fragmentShader: [ | |
"uniform float opacity;", | |
"varying vec3 vLightFront;", | |
"#ifdef DOUBLE_SIDED", | |
"varying vec3 vLightBack;", | |
"#endif", | |
THREE.ShaderChunk[ "color_pars_fragment" ], | |
THREE.ShaderChunk[ "map_pars_fragment" ], | |
THREE.ShaderChunk[ "lightmap_pars_fragment" ], | |
THREE.ShaderChunk[ "envmap_pars_fragment" ], | |
THREE.ShaderChunk[ "fog_pars_fragment" ], | |
THREE.ShaderChunk[ "shadowmap_pars_fragment" ], | |
THREE.ShaderChunk[ "specularmap_pars_fragment" ], | |
"void main() {", | |
"gl_FragColor = vec4( vec3 ( 1.0 ), opacity );", | |
THREE.ShaderChunk[ "map_fragment" ], | |
THREE.ShaderChunk[ "alphatest_fragment" ], | |
THREE.ShaderChunk[ "specularmap_fragment" ], | |
"#ifdef DOUBLE_SIDED", | |
//"float isFront = float( gl_FrontFacing );", | |
//"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;", | |
"if ( gl_FrontFacing )", | |
"gl_FragColor.xyz *= vLightFront;", | |
"else", | |
"gl_FragColor.xyz *= vLightBack;", | |
"#else", | |
"gl_FragColor.xyz *= vLightFront;", | |
"#endif", | |
THREE.ShaderChunk[ "lightmap_fragment" ], | |
THREE.ShaderChunk[ "color_fragment" ], | |
THREE.ShaderChunk[ "envmap_fragment" ], | |
THREE.ShaderChunk[ "shadowmap_fragment" ], | |
THREE.ShaderChunk[ "linear_to_gamma_fragment" ], | |
THREE.ShaderChunk[ "fog_fragment" ], | |
"}" | |
].join("\n") | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment