diff --git a/docs/api/en/constants/Renderer.html b/docs/api/en/constants/Renderer.html index 754b02bb3010a6e45a9a71d80b759a5bb72f8bb1..ac06ee3aaa0ca883e0b28cf54d928a47f7a4d2fc 100644 --- a/docs/api/en/constants/Renderer.html +++ b/docs/api/en/constants/Renderer.html @@ -39,13 +39,15 @@ THREE.BasicShadowMap THREE.PCFShadowMap THREE.PCFSoftShadowMap + THREE.VSMShadowMap

These define the WebGLRenderer's [page:WebGLRenderer.shadowMap.type shadowMap.type] property.

[page:constant BasicShadowMap] gives unfiltered shadow maps - fastest, but lowest quality.
[page:constant PCFShadowMap] filters shadow maps using the Percentage-Closer Filtering (PCF) algorithm (default).
- [page:constant PCFSoftShadowMap] filters shadow maps using the Percentage-Closer Soft Shadows (PCSS) algorithm. + [page:constant PCFSoftShadowMap] filters shadow maps using the Percentage-Closer Soft Shadows (PCSS) algorithm.
+ [page:constant VSMShadowMap] filters shadow maps using the Variance Shadow Map (VSM) algorithm.

Tone Mapping

diff --git a/docs/api/en/lights/shadows/LightShadow.html b/docs/api/en/lights/shadows/LightShadow.html index 0ffc7c9630ea310877ed7b1b1f1ceadad37814dd..6f5f02e5e648ee7ea786a8045bea736d93a11537 100644 --- a/docs/api/en/lights/shadows/LightShadow.html +++ b/docs/api/en/lights/shadows/LightShadow.html @@ -22,7 +22,7 @@

[page:Camera camera] - the light's view of the world.

- Create a new [name]. This is not intended to be called directly - it is used as a base class by + Create a new [name]. This is not intended to be called directly - it is used as a base class by other light shadows.

@@ -46,6 +46,12 @@ in shadow. Computed internally during rendering.

+

[property:WebGLRenderTarget mapPass]

+

+ The distribution map generated using the internal camera; an occlusion is calculated based + on the distribution of depths. Computed internally during rendering. +

+

[property:Vector2 mapSize]

A [Page:Vector2] defining the width and height of the shadow map.

@@ -56,7 +62,6 @@ The default is *( 512, 512 )*.

-

[property:Matrix4 matrix]

Model to shadow camera space, to compute location and depth in shadow map. Stored @@ -118,8 +123,6 @@

Source

-

- [link:https://github.com/mrdoob/three.js/blob/master/src/lights/[name].js src/lights/[name].js] -

+ [link:https://github.com/mrdoob/three.js/blob/master/src/lights/[name].js src/lights/[name].js] diff --git a/docs/api/en/renderers/WebGLRenderer.html b/docs/api/en/renderers/WebGLRenderer.html index b39f75da0d1d455cb853f8cb07ef1b1f2de01c38..fbf2f2c39d90e5e039df49b6f7e4820825fb89a4 100644 --- a/docs/api/en/renderers/WebGLRenderer.html +++ b/docs/api/en/renderers/WebGLRenderer.html @@ -248,7 +248,7 @@

[property:Integer shadowMap.type]

Defines shadow map type (unfiltered, percentage close filtering, percentage close filtering with bilinear filtering in shader)

-

Options are THREE.BasicShadowMap, THREE.PCFShadowMap (default) and THREE.PCFSoftShadowMap. See [page:Renderer Renderer constants] for details.

+

Options are THREE.BasicShadowMap, THREE.PCFShadowMap (default), THREE.PCFSoftShadowMap and THREE.VSMShadowMap. See [page:Renderer Renderer constants] for details.

[property:Boolean sortObjects]

diff --git a/examples/files.js b/examples/files.js index 10f4ff92ddf1de87e8effdae5e9e6655056ea184..8357ec7528a46f05d3bf0bd00b93dbf679e95f78 100644 --- a/examples/files.js +++ b/examples/files.js @@ -229,6 +229,7 @@ var files = { "webgl_shaders_vector", "webgl_shading_physical", "webgl_shadowmap", + "webgl_shadowmap_vsm", "webgl_shadowmap_performance", "webgl_shadowmap_pointlight", "webgl_shadowmap_viewer", diff --git a/examples/webgl_shadowmap_vsm.html b/examples/webgl_shadowmap_vsm.html new file mode 100644 index 0000000000000000000000000000000000000000..df9f7f9ee1a0b4eca0455ced257ce07e93f4ddd9 --- /dev/null +++ b/examples/webgl_shadowmap_vsm.html @@ -0,0 +1,222 @@ + + + + three.js webgl - VSM Shadows example + + + + + +

+ three.js - VSM Shadows example by Paul Brunt +
+ + + + diff --git a/src/constants.d.ts b/src/constants.d.ts index 403381f1246f5e0547ad55bb1e80907e96b94b7c..8d200621acbfbd3f8a0146422bd43d081404ae80 100644 --- a/src/constants.d.ts +++ b/src/constants.d.ts @@ -33,6 +33,7 @@ export enum ShadowMapType {} export const BasicShadowMap: ShadowMapType; export const PCFShadowMap: ShadowMapType; export const PCFSoftShadowMap: ShadowMapType; +export const VSMShadowMap: ShadowMapType; // MATERIAL CONSTANTS diff --git a/src/constants.js b/src/constants.js index 1fdb24e1e811ada3f9d55b04c0ec0794c79acad3..0c95fc2c755deb0b033996b1072d450bdcbba8c6 100644 --- a/src/constants.js +++ b/src/constants.js @@ -10,6 +10,7 @@ export var FrontFaceDirectionCCW = 1; export var BasicShadowMap = 0; export var PCFShadowMap = 1; export var PCFSoftShadowMap = 2; +export var VSMShadowMap = 3; export var FrontSide = 0; export var BackSide = 1; export var DoubleSide = 2; diff --git a/src/lights/LightShadow.d.ts b/src/lights/LightShadow.d.ts index b5b337b23020997b3a4e14b2ed6866559aaadcc4..e2c8b00171fe97c6841442454c31170ba45895ba 100644 --- a/src/lights/LightShadow.d.ts +++ b/src/lights/LightShadow.d.ts @@ -14,6 +14,7 @@ export class LightShadow { radius: number; mapSize: Vector2; map: RenderTarget; + mapPass: RenderTarget; matrix: Matrix4; copy( source: LightShadow ): this; diff --git a/src/lights/LightShadow.js b/src/lights/LightShadow.js index d8ae6551ce0a4f7bb2e6beb4d493322e49826430..87c6bde9f4cded4579e2195308af901dcf848c68 100644 --- a/src/lights/LightShadow.js +++ b/src/lights/LightShadow.js @@ -18,6 +18,7 @@ function LightShadow( camera ) { this.mapSize = new Vector2( 512, 512 ); this.map = null; + this.mapPass = null; this.matrix = new Matrix4(); this._frustum = new Frustum(); diff --git a/src/renderers/shaders/ShaderChunk.js b/src/renderers/shaders/ShaderChunk.js index 15a9777f4b3b614b6fba175fd33e751aff220322..f7f13087bd75a278b54c881d63563ee595966d90 100644 --- a/src/renderers/shaders/ShaderChunk.js +++ b/src/renderers/shaders/ShaderChunk.js @@ -122,6 +122,8 @@ import shadow_frag from './ShaderLib/shadow_frag.glsl.js'; import shadow_vert from './ShaderLib/shadow_vert.glsl.js'; import sprite_frag from './ShaderLib/sprite_frag.glsl.js'; import sprite_vert from './ShaderLib/sprite_vert.glsl.js'; +import vsm_frag from './ShaderLib/vsm_frag.glsl.js'; +import vsm_vert from './ShaderLib/vsm_vert.glsl.js'; export var ShaderChunk = { alphamap_fragment: alphamap_fragment, @@ -247,5 +249,7 @@ export var ShaderChunk = { shadow_frag: shadow_frag, shadow_vert: shadow_vert, sprite_frag: sprite_frag, - sprite_vert: sprite_vert + sprite_vert: sprite_vert, + vsm_frag: vsm_frag, + vsm_vert: vsm_vert }; diff --git a/src/renderers/shaders/ShaderChunk/packing.glsl.js b/src/renderers/shaders/ShaderChunk/packing.glsl.js index 19a5abf7eaae68bcf58029fae761260c54ba1923..1a4ec36bcfa04a031a61bb6247d07c0290f64cfb 100644 --- a/src/renderers/shaders/ShaderChunk/packing.glsl.js +++ b/src/renderers/shaders/ShaderChunk/packing.glsl.js @@ -25,6 +25,23 @@ float unpackRGBAToDepth( const in vec4 v ) { return dot( v, UnpackFactors ); } +vec4 encodeHalfRGBA ( vec2 v ) { + vec4 encoded = vec4( 0.0 ); + const vec2 offset = vec2( 1.0 / 255.0, 0.0 ); + + encoded.xy = vec2( v.x, fract( v.x * 255.0 ) ); + encoded.xy = encoded.xy - ( encoded.yy * offset ); + + encoded.zw = vec2( v.y, fract( v.y * 255.0 ) ); + encoded.zw = encoded.zw - ( encoded.ww * offset ); + + return encoded; +} + +vec2 decodeHalfRGBA( vec4 v ) { + return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); +} + // NOTE: viewZ/eyeZ is < 0 when in front of the camera per OpenGL conventions float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { diff --git a/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js index 83ff4536e88a0dfc655541cd3909d320cd23c601..c24a6114b522f8082f5a99daf03dab0eaa56edd6 100644 --- a/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl.js @@ -36,6 +36,33 @@ export default /* glsl */` } + vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { + + return decodeHalfRGBA( texture2D( shadow, uv ) ); + + } + + float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ + + float occlusion = 1.0; + + vec2 distribution = texture2DDistribution( shadow, uv ); + + float hard_shadow = step( compare , distribution.x ); // Hard Shadow + + if (hard_shadow != 1.0 ) { + + float distance = compare - distribution.x ; + float variance = max( 0.00000, distribution.y * distribution.y ); + float softness_probability = variance / (variance + distance * distance ); // Chebeyshevs inequality + softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); // 0.3 reduces light bleed + occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); + + } + return occlusion; + + } + float texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) { const vec2 offset = vec2( 0.0, 1.0 ); @@ -131,6 +158,10 @@ export default /* glsl */` texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) ) * ( 1.0 / 9.0 ); + #elif defined( SHADOWMAP_TYPE_VSM ) + + shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); + #else // no percentage-closer filtering: shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); @@ -229,7 +260,7 @@ export default /* glsl */` // bd3D = base direction 3D vec3 bd3D = normalize( lightToPosition ); - #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) + #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; diff --git a/src/renderers/shaders/ShaderLib/vsm_frag.glsl.js b/src/renderers/shaders/ShaderLib/vsm_frag.glsl.js new file mode 100644 index 0000000000000000000000000000000000000000..1d5db3d3a56deff7c89562b4692d2af4220afad5 --- /dev/null +++ b/src/renderers/shaders/ShaderLib/vsm_frag.glsl.js @@ -0,0 +1,42 @@ +export default /* glsl */` +uniform sampler2D shadow_pass; +uniform vec2 resolution; +uniform float radius; + +#include + +void main() { + + float mean = 0.0; + float squared_mean = 0.0; + + // This seems totally useless but it's a crazy work around for a Adreno compiler bug + float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) ); + + for ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) { + + #ifdef HORIZONAL_PASS + + vec2 distribution = decodeHalfRGBA ( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) ); + mean += distribution.x; + squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; + + #else + + float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) ); + mean += depth; + squared_mean += depth * depth; + + #endif + + } + + mean = mean * HALF_SAMPLE_RATE; + squared_mean = squared_mean * HALF_SAMPLE_RATE; + + float std_dev = pow( squared_mean - mean * mean, 0.5 ); + + gl_FragColor = encodeHalfRGBA( vec2( mean, std_dev ) ); + +} +`; diff --git a/src/renderers/shaders/ShaderLib/vsm_vert.glsl.js b/src/renderers/shaders/ShaderLib/vsm_vert.glsl.js new file mode 100644 index 0000000000000000000000000000000000000000..ee0c7f8b74a250f9aa405b0e8dbfca32040054cd --- /dev/null +++ b/src/renderers/shaders/ShaderLib/vsm_vert.glsl.js @@ -0,0 +1,9 @@ +export default /* glsl */` + +void main() { + + gl_Position = vec4( position, 1.0 ); + +} + +`; diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js index 86e316dc94fd2ec7d48ff1354001228848fff1c6..81b47a2866b2c03601a7ea010ffbe7d4e445af68 100644 --- a/src/renderers/webgl/WebGLProgram.js +++ b/src/renderers/webgl/WebGLProgram.js @@ -5,7 +5,7 @@ import { WebGLUniforms } from './WebGLUniforms.js'; import { WebGLShader } from './WebGLShader.js'; import { ShaderChunk } from '../shaders/ShaderChunk.js'; -import { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, EquirectangularRefractionMapping, CubeRefractionMapping, SphericalReflectionMapping, EquirectangularReflectionMapping, CubeUVRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFSoftShadowMap, PCFShadowMap, ACESFilmicToneMapping, CineonToneMapping, Uncharted2ToneMapping, ReinhardToneMapping, LinearToneMapping, GammaEncoding, RGBDEncoding, RGBM16Encoding, RGBM7Encoding, RGBEEncoding, sRGBEncoding, LinearEncoding, LogLuvEncoding } from '../../constants.js'; +import { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, EquirectangularRefractionMapping, CubeRefractionMapping, SphericalReflectionMapping, EquirectangularReflectionMapping, CubeUVRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFSoftShadowMap, PCFShadowMap, VSMShadowMap, ACESFilmicToneMapping, CineonToneMapping, Uncharted2ToneMapping, ReinhardToneMapping, LinearToneMapping, GammaEncoding, RGBDEncoding, RGBM16Encoding, RGBM7Encoding, RGBEEncoding, sRGBEncoding, LinearEncoding, LogLuvEncoding } from '../../constants.js'; var programIdCount = 0; @@ -262,6 +262,10 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters, shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; + } else if ( parameters.shadowMapType === VSMShadowMap ) { + + shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; + } var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; diff --git a/src/renderers/webgl/WebGLShadowMap.js b/src/renderers/webgl/WebGLShadowMap.js index 98ed1df695aeaa90e2a18d4cc3b19712b6dcc3ff..18ff93d19297d65a0e94bb14b31883fc93bcf6b2 100644 --- a/src/renderers/webgl/WebGLShadowMap.js +++ b/src/renderers/webgl/WebGLShadowMap.js @@ -3,10 +3,15 @@ * @author mrdoob / http://mrdoob.com/ */ -import { FrontSide, BackSide, DoubleSide, RGBAFormat, NearestFilter, PCFShadowMap, RGBADepthPacking, NoBlending } from '../../constants.js'; +import { FrontSide, BackSide, DoubleSide, RGBAFormat, NearestFilter, LinearFilter, PCFShadowMap, VSMShadowMap, RGBADepthPacking, NoBlending } from '../../constants.js'; import { WebGLRenderTarget } from '../WebGLRenderTarget.js'; import { MeshDepthMaterial } from '../../materials/MeshDepthMaterial.js'; import { MeshDistanceMaterial } from '../../materials/MeshDistanceMaterial.js'; +import { ShaderMaterial } from '../../materials/ShaderMaterial.js'; +import { BufferAttribute } from '../../core/BufferAttribute.js'; +import { BufferGeometry } from '../../core/BufferGeometry.js'; +import { Mesh } from '../../objects/Mesh.js'; +import { ShaderChunk } from '../shaders/ShaderChunk.js'; import { Vector4 } from '../../math/Vector4.js'; import { Vector2 } from '../../math/Vector2.js'; import { Frustum } from '../../math/Frustum.js'; @@ -32,6 +37,39 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { var shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; + var shadowMaterialVertical = new ShaderMaterial( { + + defines: { + SAMPLE_RATE: 2.0 / 8.0, + HALF_SAMPLE_RATE: 1.0 / 8.0 + }, + + uniforms: { + shadow_pass: { value: null }, + resolution: { value: new Vector2() }, + radius: { value: 4.0 } + }, + + vertexShader: ShaderChunk.vsm_vert, + + fragmentShader: ShaderChunk.vsm_frag + + } ); + + var shadowMaterialHorizonal = shadowMaterialVertical.clone(); + shadowMaterialHorizonal.defines.HORIZONAL_PASS = 1; + + var fullScreenTri = new BufferGeometry(); + fullScreenTri.addAttribute( + "position", + new BufferAttribute( + new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), + 3 + ) + ); + + var fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); + // init for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { @@ -133,6 +171,19 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { } + if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { + + var pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; + + shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); + shadow.map.texture.name = light.name + ".shadowMap"; + + shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); + + shadow.camera.updateProjectionMatrix(); + + } + if ( shadow.map === null ) { var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; @@ -166,7 +217,15 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { _frustum = shadow.getFrustum(); - renderObject( scene, camera, shadow.camera, light ); + renderObject( scene, camera, shadow.camera, light, this.type ); + + } + + // do blur pass for VSM + + if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { + + VSMPass( shadow, camera ); } @@ -178,7 +237,31 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { }; - function getDepthMaterial( object, material, light, shadowCameraNear, shadowCameraFar ) { + function VSMPass( shadow, camera ) { + + var geometry = _objects.update( fullScreenMesh ); + + // vertical pass + + shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; + shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; + shadowMaterialVertical.uniforms.radius.value = shadow.radius; + _renderer.setRenderTarget( shadow.mapPass ); + _renderer.clear(); + _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); + + // horizonal pass + + shadowMaterialHorizonal.uniforms.shadow_pass.value = shadow.mapPass.texture; + shadowMaterialHorizonal.uniforms.resolution.value = shadow.mapSize; + shadowMaterialHorizonal.uniforms.radius.value = shadow.radius; + _renderer.setRenderTarget( shadow.map ); + _renderer.clear(); + _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizonal, fullScreenMesh, null ); + + } + + function getDepthMaterial( object, material, light, shadowCameraNear, shadowCameraFar, type ) { var geometry = object.geometry; @@ -267,7 +350,15 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { result.visible = material.visible; result.wireframe = material.wireframe; - result.side = ( material.shadowSide != null ) ? material.shadowSide : shadowSide[ material.side ]; + if ( type === VSMShadowMap ) { + + result.side = ( material.shadowSide != null ) ? material.shadowSide : material.side; + + } else { + + result.side = ( material.shadowSide != null ) ? material.shadowSide : shadowSide[ material.side ]; + + } result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; @@ -288,7 +379,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { } - function renderObject( object, camera, shadowCamera, light ) { + function renderObject( object, camera, shadowCamera, light, type ) { if ( object.visible === false ) return; @@ -296,7 +387,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { - if ( object.castShadow && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { + if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); @@ -314,7 +405,8 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { if ( groupMaterial && groupMaterial.visible ) { - var depthMaterial = getDepthMaterial( object, groupMaterial, light, shadowCamera.near, shadowCamera.far ); + var depthMaterial = getDepthMaterial( object, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); } @@ -323,7 +415,8 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { } else if ( material.visible ) { - var depthMaterial = getDepthMaterial( object, material, light, shadowCamera.near, shadowCamera.far ); + var depthMaterial = getDepthMaterial( object, material, light, shadowCamera.near, shadowCamera.far, type ); + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); } @@ -336,7 +429,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { for ( var i = 0, l = children.length; i < l; i ++ ) { - renderObject( children[ i ], camera, shadowCamera, light ); + renderObject( children[ i ], camera, shadowCamera, light, type ); }