Created
October 25, 2012 14:46
-
-
Save skrat/3952991 to your computer and use it in GitHub Desktop.
VSM 4 THREE
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 ); | |
// 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() ); | |
shadowCamera.lookAt( light.target.matrixWorld.getPosition() ); | |
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.multiplySelf( shadowCamera.projectionMatrix ); | |
shadowMatrix.multiplySelf( shadowCamera.matrixWorldInverse ); | |
// update camera matrices and frustum | |
if ( ! shadowCamera._viewMatrixArray ) shadowCamera._viewMatrixArray = new Float32Array( 16 ); | |
if ( ! shadowCamera._projectionMatrixArray ) shadowCamera._projectionMatrixArray = new Float32Array( 16 ); | |
shadowCamera.matrixWorldInverse.flattenToArray( shadowCamera._viewMatrixArray ); | |
shadowCamera.projectionMatrix.flattenToArray( shadowCamera._projectionMatrixArray ); | |
_projScreenMatrix.multiply( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); | |
_frustum.setFromMatrix( _projScreenMatrix ); | |
// render shadow map | |
_renderer.setRenderTarget( shadowMap ); | |
_renderer.clear(); | |
// set object matrices & frustum culling | |
renderList = scene.__webglObjects; | |
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.multiply( shadowCamera.matrixWorldInverse, object.matrixWorld ); | |
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.__webglObjectsImmediate; | |
for ( j = 0, jl = renderList.length; 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 ); | |
} | |
} | |
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.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.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
Hi, I'm trying to use your plugin in a new threejs project
I forked it and corrected the errors given by the console for the lastest rev of threejs but I can't make it work
Do you have an old working version of this or some docs about how it should work ?