Skip to content

Instantly share code, notes, and snippets.

@skrat
Created October 25, 2012 14:46
Show Gist options
  • Save skrat/3952991 to your computer and use it in GitHub Desktop.
Save skrat/3952991 to your computer and use it in GitHub Desktop.
VSM 4 THREE
/**
* 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")
};
@wlalele
Copy link

wlalele commented Jun 4, 2015

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 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment