diff --git a/examples/webgl_shadowmap_omnidirectional.html b/examples/webgl_shadowmap_omnidirectional.html index bcfcc3977e8a211052579fd26844a16f37040b3a..dc808cdf28cc97b95dfdb7127b750201936737c9 100644 --- a/examples/webgl_shadowmap_omnidirectional.html +++ b/examples/webgl_shadowmap_omnidirectional.html @@ -75,12 +75,12 @@ pointLight.shadowCameraFar = 30; pointLight.shadowDarkness = 0.5; pointLight.shadowCameraVisible = true; - pointLight.shadowMapWidth = 1024; + pointLight.shadowMapWidth = 2048; pointLight.shadowMapHeight = 1024; pointLight.name = 'Point Light'; scene.add( pointLight ); - dirLight = new THREE.DirectionalLight( 0xffffff, 1 ); + /*dirLight = new THREE.DirectionalLight( 0xffffff, 1 ); dirLight.position.set( 0, 50, 0 ); dirLight.castShadow = true; dirLight.shadowCameraNear = 0.01; @@ -94,7 +94,7 @@ dirLight.shadowMapWidth = 1024; dirLight.shadowMapHeight = 1024; dirLight.name = 'Dir. Light'; - //scene.add( dirLight ); + scene.add( dirLight );*/ cubeMaterial = new THREE.MeshPhongMaterial( { color: 0xff0000, @@ -128,7 +128,7 @@ var torusGeometry = new THREE.TorusKnotGeometry( 25, 8, 75, 20 ); torusKnot = new THREE.Mesh( torusGeometry, cubeMaterial ); torusKnot.scale.multiplyScalar( 1 / 18 ); - torusKnot.position.y = 3; + torusKnot.position.set( -1, 3, -4 ); torusKnot.castShadow = true; torusKnot.receiveShadow = true; scene.add( torusKnot ); @@ -185,6 +185,16 @@ wall.rotation.y = Math.PI / 2; wall.rotation.z = Math.PI / 2; + /*wall = new THREE.Mesh( wallGeometry, wallMaterial ); + wall.name = "front wall"; + wall.scale.multiplyScalar( 3 ); + wall.castShadow = false; + wall.receiveShadow = true; + scene.add( wall ); + wall.position.set( 0, 10, 14 ); + wall.rotation.y = Math.PI / 2; + wall.rotation.z = Math.PI / 2;*/ + var sphereGeometry = new THREE.SphereGeometry( 1, 32, 32 ); var material = new THREE.MeshBasicMaterial( { color: 0xffffff } ); var sphere = new THREE.Mesh( sphereGeometry, material ); @@ -206,7 +216,7 @@ renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setClearColor( 0x000000 ); renderer.shadowMap.enabled = true; - renderer.shadowMap.type = THREE.BasicShadowMap; + renderer.shadowMap.type = THREE.PCFShadowMap; // Mouse control controls = new THREE.OrbitControls( camera, renderer.domElement ); diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 7157192038aa02434190d49aaebfbfa1c014b5f8..c8dfe4aef75e48560bceb39f267e8006c27cae61 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -374,6 +374,16 @@ THREE.WebGLRenderer = function ( parameters ) { }; + this.getViewport = function ( dimensions ) { + + dimensions.x = _viewportX; + dimensions.y = _viewportY; + + dimensions.z = _viewportWidth; + dimensions.w = _viewportHeight; + + }; + this.setScissor = function ( x, y, width, height ) { _gl.scissor( @@ -1965,30 +1975,26 @@ THREE.WebGLRenderer = function ( parameters ) { if ( ! light.castShadow ) continue; - if ( light instanceof THREE.PointLight || light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) { + if ( light instanceof THREE.PointLight || light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) { - if( light instanceof THREE.PointLight ){ - - uniforms.shadowCube.value[ j ] = light.shadowMap; - uniforms.shadowMap.value[ j ] = null; + if ( light instanceof THREE.PointLight ) { // for point lights we set the sign of the shadowDarkness uniform to be negative - uniforms.shadowDarkness.value[ j ] = -light.shadowDarkness; + uniforms.shadowDarkness.value[ j ] = - light.shadowDarkness; // when we have a point light, the 'shadowMatrix' uniform is used to store - // the inverse of the view matrix (camera.matrixWorld), so that we can get the + // the inverse of the view matrix (camera.matrixWorld), so that we can get the // world-space position of the light in the shader. uniforms.shadowMatrix.value[ j ] = camera.matrixWorld; } else { - uniforms.shadowMap.value[ j ] = light.shadowMap; - uniforms.shadowCube.value[ j ] = null; uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; - } + } + uniforms.shadowMap.value[ j ] = light.shadowMap; uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; uniforms.shadowBias.value[ j ] = light.shadowBias; @@ -2347,9 +2353,11 @@ THREE.WebGLRenderer = function ( parameters ) { textureUnit = uniform._array[ i ]; if ( ! texture ) continue; - + if ( texture instanceof THREE.CubeTexture || - ( texture.image instanceof Array && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/ + ( texture.image instanceof Array && texture.image.length === 6 ) ) { + + // CompressedTexture can have Array in image :/ setCubeTexture( texture, textureUnit ); @@ -3153,10 +3161,10 @@ THREE.WebGLRenderer = function ( parameters ) { } - if( isCube ){ + if ( isCube ) { var renderTargetProperties = properties.get( renderTarget ); - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, renderTargetProperties.__webglTexture , 0 ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, renderTargetProperties.__webglTexture, 0 ); } diff --git a/src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl b/src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl index 54beb8faabc1844cd81d03356eb4aa8acd9dca62..9c0034a0ea74a382d9a29a8f3d6b5ce79b6dff91 100644 --- a/src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl +++ b/src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl @@ -24,7 +24,9 @@ // for point lights, the uniform @vShadowCoord is re-purposed to hold // the distance from the light to the world-space position of the fragment. vec3 lightToPosition = vShadowCoord[ i ].xyz; - float cubeTexelSize = 1.0 / shadowMapSize[ i ].x; + + float texelSizeX = 1.0 / shadowMapSize[ i ].x; + float texelSizeY = 1.0 / shadowMapSize[ i ].y; vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w; float shadow = 0.0; @@ -49,16 +51,20 @@ if( isPointLight ) { + float cubeTexelSize = 1.0 / ( shadowMapSize[ i ].x * 0.25 ); + vec3 baseDirection3D = normalize( lightToPosition ); + vec2 baseDirection2D = cubeToUV( baseDirection3D, texelSizeX, texelSizeY ); + initGridSamplingDisk(); - float diskRadius = 1.5; + float diskRadius = 1.25; float numSamples = 1.0; shadow = 0.0; vec3 baseDirection = normalize( lightToPosition ); float curDistance = length( lightToPosition ); - float dist = unpack1K( textureCube( shadowCube[ i ], baseDirection ) ); + float dist = unpack1K( texture2D( shadowMap[ i ], baseDirection2D ) ) + 0.1; if ( curDistance >= dist ) shadow += 1.0; @@ -66,15 +72,17 @@ for( int s = 0; s < 20; s++ ) { vec3 offset = gridSamplingDisk[ s ] * diskRadius * cubeTexelSize; - dist = unpack1K( textureCube( shadowCube[ i ], vec3( baseDirection + offset ) ) ); + vec3 adjustedBaseDirection3D = baseDirection3D + offset; + vec2 adjustedBaseDirection2D = cubeToUV( adjustedBaseDirection3D, texelSizeX, texelSizeY ); + dist = unpack1K( texture2D( shadowMap[ i ], adjustedBaseDirection2D ) ) + 0.1; if ( curDistance >= dist ) shadow += 1.0; numSamples += 1.0; - + } shadow /= numSamples; - + } else { #endif @@ -86,30 +94,22 @@ /* // nested loops breaks shader compiler / validator on some ATI cards when using OpenGL // must enroll loop manually - for ( float y = -1.25; y <= 1.25; y += 1.25 ) for ( float x = -1.25; x <= 1.25; x += 1.25 ) { - vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ); - // doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup //vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) ); - float fDepth = unpackDepth( rgbaDepth ); - if ( fDepth < shadowCoord.z ) shadow += 1.0; - } - shadow /= 9.0; - */ const float shadowDelta = 1.0 / 9.0; - float xPixelOffset = 1.0 / shadowMapSize[ i ].x; - float yPixelOffset = 1.0 / shadowMapSize[ i ].y; + float xPixelOffset = texelSizeX; + float yPixelOffset = texelSizeY; float dx0 = -1.25 * xPixelOffset; float dy0 = -1.25 * yPixelOffset; @@ -150,40 +150,46 @@ #endif shadowColor = shadowColor * vec3( ( 1.0 - realShadowDarkness * shadow ) ); - + #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) - - #if defined(POINT_LIGHT_SHADOWS) + + #if defined(POINT_LIGHT_SHADOWS) if( isPointLight ) { + float cubeTexelSize = 1.0 / ( shadowMapSize[ i ].x * 0.25 ); + vec3 baseDirection3D = normalize( lightToPosition ); + vec2 baseDirection2D = cubeToUV( baseDirection3D, texelSizeX, texelSizeY ); + initGridSamplingDisk(); - float diskRadius = 2.5; + float diskRadius = 2.25; float numSamples = 1.0; shadow = 0.0; vec3 baseDirection = normalize( lightToPosition ); float curDistance = length( lightToPosition ); - float dist = unpack1K( textureCube( shadowCube[ i ], baseDirection ) ); + float dist = unpack1K( texture2D( shadowMap[ i ], baseDirection2D ) ) + 0.1; if ( curDistance >= dist ) shadow += 1.0; - + // evaluate each sampling direction for( int s = 0; s < 20; s++ ) { - + vec3 offset = gridSamplingDisk[ s ] * diskRadius * cubeTexelSize; - dist = unpack1K( textureCube( shadowCube[ i ], vec3( baseDirection + offset ) ) ); + vec3 adjustedBaseDirection3D = baseDirection3D + offset; + vec2 adjustedBaseDirection2D = cubeToUV( adjustedBaseDirection3D, texelSizeX, texelSizeY ); + dist = unpack1K( texture2D( shadowMap[ i ], adjustedBaseDirection2D ) ) + 0.1; if ( curDistance >= dist ) shadow += 1.0; numSamples += 1.0; - + } shadow /= numSamples; - } else { + } else { #endif @@ -191,8 +197,8 @@ // (9 pixel kernel) // http://fabiensanglard.net/shadowmappingPCF/ - float xPixelOffset = 1.0 / shadowMapSize[ i ].x; - float yPixelOffset = 1.0 / shadowMapSize[ i ].y; + float xPixelOffset = texelSizeX; + float yPixelOffset = texelSizeY; float dx0 = -1.0 * xPixelOffset; float dy0 = -1.0 * yPixelOffset; @@ -247,17 +253,19 @@ #if defined(POINT_LIGHT_SHADOWS) - if( isPointLight ) { + if( isPointLight ) { + + vec3 baseDirection3D = normalize( lightToPosition ); + vec2 baseDirection2D = cubeToUV( baseDirection3D, texelSizeX, texelSizeY ); + vec4 data = texture2D( shadowMap[ i ], baseDirection2D ); + float dist = unpack1K( data ) + 0.1; + if ( length( lightToPosition ) >= dist) + shadowColor = shadowColor * vec3( 1.0 - realShadowDarkness ); - vec4 data = textureCube( shadowCube[ i ], normalize( lightToPosition ) ); - float dist = unpack1K( data ); - if ( length( lightToPosition ) >= dist) - shadowColor = shadowColor * vec3( 1.0 - realShadowDarkness ); - } else { #endif - + vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy ); float fDepth = unpackDepth( rgbaDepth ); diff --git a/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl b/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl index 00c35c2a20b510749e64e110a0b5b59bad3dc4f2..e2f70e8e92aa0c0a82dae446922f926e28756c4b 100644 --- a/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl +++ b/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl @@ -18,19 +18,6 @@ #if defined(POINT_LIGHT_SHADOWS) - uniform samplerCube shadowCube[ MAX_SHADOWS ]; - - vec4 pack1K ( float depth ) { - - depth /= 1000.0; - const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 ); - const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 ); - vec4 res = fract( depth * bitSh ); - res -= res.xxyz * bitMsk; - return res; - - } - float unpack1K ( vec4 color ) { const vec4 bitSh = vec4( 1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0 ); @@ -38,6 +25,72 @@ } + vec2 cubeToUV( vec3 v, float texelSizeX, float texelSizeY ) { + + // Horizontal cross layout: + const vec2 Squares = vec2( 4.0, 2.0 ); + const vec2 Center = vec2( 1.0, 0.0 ); + + // Size of a square in UV space: + const vec2 TexSquareSize = 1.0 / Squares; + + // UV space offset of the center of the center square of the cross: + const vec2 TexCoordOffs = TexSquareSize * ( 0.5 + Center ); + + // Factors to scale square space (-1..+1 per square) to UV space: + const vec2 TexSquareScale = TexSquareSize * 0.5; + + // Just less than a texel in square space when divided by resolution: + const float TexEps = 1.5; // = min(Squares.x, Squares.y) - 0.5; + + vec3 absV = abs( v ); + vec3 sgnV = sign( v ); + + // Intersect unit cube + + float scale = 1.0 / max( absV.x, max( absV.y, absV.z ) ); + + v *= scale; + absV *= scale; + + // Determine gate factors + + // gate.? is one when on left / right, bottom / top, back + float eps = TexEps * texelSizeY; + vec3 gate = step( 1.0 - eps, vec3( absV.xy, v.z ) ); + + // prefer any square over bottom / top + float notX = 1. - gate.x; + float notZ = 1. - gate.z; + gate.y *= notX * notZ; + // prefer back over side + gate.x *= notZ; + + // Unwrap + + // start with xy coordinates + vec2 planar = v.xy; + + // stop at the last texel (can use a factor of 1.0 for NEAREST) + float yTexelSize = 2.0 * Squares.y * texelSizeY; + float yAdjusted = planar.y * ( 1.0 - yTexelSize ); + planar.y = yAdjusted; + planar.y -= gate.y * yAdjusted; + + // unwrap left / right, top / bottom + planar.x += gate.x * ( sgnV.x + v.z * sgnV.x ); + + planar.x += gate.y * ( -sgnV.y * 2.0 ); + planar.y += gate.y * ( 2.0 + ( v.z * sgnV.y ) ); + + // unwrap back + planar.x += gate.z * ( 4.0 - 2.0 * planar.x ); + + // adjust to UV space + return TexCoordOffs + planar * TexSquareScale; + + } + vec3 gridSamplingDisk[ 20 ]; bool gridSamplingInitialized = false; @@ -46,7 +99,7 @@ if( gridSamplingInitialized ){ return; - + } gridSamplingDisk[0] = vec3(1, 1, 1); diff --git a/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl b/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl index 619c2462c79f6c5d8e49be0cc0e9491adaeb648e..a94adb0727494440b939469c646f13cdeefc5e85 100644 --- a/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl +++ b/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl @@ -1 +1,7 @@ -#ifdef USE_SHADOWMAP uniform float shadowDarkness[ MAX_SHADOWS ]; uniform mat4 shadowMatrix[ MAX_SHADOWS ]; varying vec4 vShadowCoord[ MAX_SHADOWS ]; #endif \ No newline at end of file +#ifdef USE_SHADOWMAP + + uniform float shadowDarkness[ MAX_SHADOWS ]; + uniform mat4 shadowMatrix[ MAX_SHADOWS ]; + varying vec4 vShadowCoord[ MAX_SHADOWS ]; + +#endif \ No newline at end of file diff --git a/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl b/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl index f0adca0f19b8feeeabc82a4dfc69dd4b147e2c28..e1e5308ae2c17524b10c51fb5e479c7aeab0ec87 100644 --- a/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl +++ b/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl @@ -1 +1,38 @@ -#ifdef USE_SHADOWMAP for( int i = 0; i < MAX_SHADOWS; i ++ ) { #if defined(POINT_LIGHT_SHADOWS) // if shadowDarkness[ i ] < 0.0, that means we have a point light with a cube // shadow map if( shadowDarkness[ i ] < 0.0 ){ // When we have a point light, the @shadowMatrix uniform is used to store // the inverse of the view matrix, so that we can get the world-space // position of the light. vec4 lightPositionWorld = ( shadowMatrix[ i ] * vec4( pointLightPosition[ i ], 1.0 )); vec4 distanceToLight = worldPosition - lightPositionWorld; distanceToLight.w = 1.0; // We also repurpose vShadowCoord to hold the distance in world space from the // light to the vertex. This value will be interpolated correctly in the fragment shader. vShadowCoord[ i ] = distanceToLight; } else { vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition; } #else vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition; #endif } #endif \ No newline at end of file +#ifdef USE_SHADOWMAP + + for( int i = 0; i < MAX_SHADOWS; i ++ ) { + + #if defined(POINT_LIGHT_SHADOWS) + + // if shadowDarkness[ i ] < 0.0, that means we have a point light with a cube + // shadow map + if( shadowDarkness[ i ] < 0.0 ) { + + // When we have a point light, the @shadowMatrix uniform is used to store + // the inverse of the view matrix, so that we can get the world-space + // position of the light. + + vec4 lightPositionWorld = ( shadowMatrix[ i ] * vec4( pointLightPosition[ i ], 1.0 ) ); + vec4 distanceToLight = worldPosition - lightPositionWorld; + distanceToLight.w = 1.0; + + // We also repurpose vShadowCoord to hold the distance in world space from the + // light to the vertex. This value will be interpolated correctly in the fragment shader. + + vShadowCoord[ i ] = distanceToLight; + + } else { + + vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition; + + } + + #else + + vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition; + + #endif + + } + +#endif \ No newline at end of file diff --git a/src/renderers/shaders/ShaderLib.js b/src/renderers/shaders/ShaderLib.js index 0c75fbe217f9c81bce6eb4e050b6a705d168cf9a..fadac0e4dc777f163789785f3f94e476b0ddf973 100644 --- a/src/renderers/shaders/ShaderLib.js +++ b/src/renderers/shaders/ShaderLib.js @@ -842,7 +842,7 @@ THREE.ShaderLib = { uniforms: { - "lightPos": { type: "v3", value: new THREE.Vector3(0,0,0) } + "lightPos": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) } }, @@ -867,7 +867,7 @@ THREE.ShaderLib = { "}" - ].join("\n"), + ].join( "\n" ), fragmentShader: [ @@ -885,8 +885,8 @@ THREE.ShaderLib = { " res -= res.xxyz * bitMsk;", " return res; ", - "}", - + "}", + "float unpack1K ( vec4 color ) {", " const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );", @@ -900,8 +900,9 @@ THREE.ShaderLib = { "}" - ].join("\n") + ].join( "\n" ) } }; + diff --git a/src/renderers/shaders/UniformsLib.js b/src/renderers/shaders/UniformsLib.js index bdc7a9fb9d4127e26bc0a3f5ef9c50760925e1db..aae6c21f471738bb4474c2fd3c159ad03be88ca6 100644 --- a/src/renderers/shaders/UniformsLib.js +++ b/src/renderers/shaders/UniformsLib.js @@ -117,15 +117,13 @@ THREE.UniformsLib = { shadowmap: { - "shadowCube": { type: "tv", value: [] }, "shadowMap": { type: "tv", value: [] }, - "shadowMapSize": { type: "v2v", value: [] }, "shadowBias" : { type: "fv1", value: [] }, "shadowDarkness": { type: "fv1", value: [] }, - "shadowMatrix" : { type: "m4v", value: [] }, + "shadowMatrix" : { type: "m4v", value: [] } } diff --git a/src/renderers/webgl/WebGLShadowMap.js b/src/renderers/webgl/WebGLShadowMap.js index 6c7f09c448fc2e0cbf2a288184002b516ced7ec2..821a5e904e026d30d4c2716617359d1f0d29455e 100644 --- a/src/renderers/webgl/WebGLShadowMap.js +++ b/src/renderers/webgl/WebGLShadowMap.js @@ -21,13 +21,16 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { var _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, _distanceMaterial, _distanceMaterialMorph, _distanceMaterialSkin, _distanceMaterialMorphSkin; - var cubeDirections = [new THREE.Vector3(1,0,0), new THREE.Vector3(-1,0,0), new THREE.Vector3(0,1,0), - new THREE.Vector3(0,-1,0), new THREE.Vector3(0,0,1), new THREE.Vector3(0,0,-1)]; + var cubeDirections = [ new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( - 1, 0, 0 ), new THREE.Vector3( 0, 0, 1 ), + new THREE.Vector3( 0, 0, - 1 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, - 1, 0 ) ]; - var cubeUps = [new THREE.Vector3(0,-1,0), new THREE.Vector3(0,-1,0), new THREE.Vector3(0,0,1), - new THREE.Vector3(0,0,-1), new THREE.Vector3(0,-1,0), new THREE.Vector3(0,-1,0)]; + var cubeUps = [ new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), + new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, - 1 ) ]; - var _vector4 = new THREE.Vector4(); + var cube2DViewPorts = [ new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(), + new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4() ]; + + var _vector4 = new THREE.Vector4(); // init @@ -118,7 +121,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { this.render = function ( scene ) { - var faceCount, isCube; + var faceCount, isPointLight; if ( scope.enabled === false ) return; if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; @@ -152,12 +155,28 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { if ( light instanceof THREE.PointLight ) { faceCount = 6; - isCube = true; + isPointLight = true; + + var vpWidth = light.shadowMapWidth / 4.0; + var vpHeight = light.shadowMapHeight / 2.0; + + // positive X + cube2DViewPorts[ 0 ].set( vpWidth * 2, 0, vpWidth, vpHeight ); + // negative X + cube2DViewPorts[ 1 ].set( 0, 0, vpWidth, vpHeight ); + // positive Z + cube2DViewPorts[ 2 ].set( vpWidth * 3, 0, vpWidth, vpHeight ); + // negative Z + cube2DViewPorts[ 3 ].set( vpWidth, 0, vpWidth, vpHeight ); + // positive Y + cube2DViewPorts[ 4 ].set( 0, vpHeight, vpWidth, vpHeight ); + // negative Y + cube2DViewPorts[ 5 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight ); } else { faceCount = 1; - isCube = false; + isPointLight = false; } @@ -167,7 +186,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { var shadowFilter = THREE.LinearFilter; - if ( scope.type === THREE.PCFSoftShadowMap ) { + if ( scope.type === THREE.PCFSoftShadowMap || light instanceof THREE.PointLight ) { shadowFilter = THREE.NearestFilter; @@ -175,17 +194,8 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; - if ( isCube ) { - - light.shadowMap = new THREE.WebGLRenderTargetCube( light.shadowMapWidth, light.shadowMapWidth, pars ); - light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapWidth ); - - } else { - - light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); - light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); - - } + light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); + light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); light.shadowMatrix = new THREE.Matrix4(); @@ -203,7 +213,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { } else { - light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); + light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, 1.0, light.shadowCameraNear, light.shadowCameraFar ); } @@ -225,27 +235,34 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { var shadowCamera = light.shadowCamera; _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( _lightPositionWorld); + shadowCamera.position.copy( _lightPositionWorld ); + + // save the existing viewport so it can be restored later + _renderer.getViewport( _vector4 ); + + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); // render shadow map for each cube face (if omni-directional) or // run a single pass if not - for ( var face = 0; face < faceCount; face++ ){ - - if( isCube ){ + for ( var face = 0; face < faceCount; face ++ ) { + + if ( isPointLight ) { _lookTarget.copy( shadowCamera.position ); - _lookTarget.add( cubeDirections[face] ); - shadowCamera.up.copy( cubeUps[face] ); - shadowCamera.lookAt( _lookTarget ); - shadowMap.activeCubeFace = face; + _lookTarget.add( cubeDirections[ face ] ); + shadowCamera.up.copy( cubeUps[ face ] ); + shadowCamera.lookAt( _lookTarget ); + var vpDimensions = cube2DViewPorts[ face ]; + _renderer.setViewport( vpDimensions.x, vpDimensions.y, vpDimensions.z, vpDimensions.w ); } else { _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); shadowCamera.lookAt( _lookTarget ); - } + } shadowCamera.updateMatrixWorld(); shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); @@ -270,17 +287,13 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); - // render shadow map - - _renderer.setRenderTarget( shadowMap ); - _renderer.clear(); - // set object matrices & frustum culling _renderList.length = 0; projectObject( scene, shadowCamera ); + // render shadow map // render regular objects for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) { @@ -301,8 +314,8 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { if ( groupMaterial.visible === true ) { - var depthMaterial = getDepthMaterial( object, groupMaterial, isCube, _lightPositionWorld ); - _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial , object, group ); + var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld ); + _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, group ); } @@ -310,7 +323,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { } else { - var depthMaterial = getDepthMaterial( object, material, isCube, _lightPosition); + var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld ); _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, null ); } @@ -318,6 +331,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { } } + } // restore GL state @@ -334,13 +348,15 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { } + _renderer.setViewport( _vector4.x, _vector4.y, _vector4.z, _vector4.w ); + _renderer.resetGLState(); scope.needsUpdate = false; }; - function getDepthMaterial( object, material, isCube, lightPositionWorld) { + function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) { var geometry = object.geometry; @@ -350,27 +366,29 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { var newMaterial; var depthMaterial = _depthMaterial; - var depthMaterialMorph = _depthMaterialMorph; - var depthMaterialSkin = _depthMaterialSkin; + var depthMaterialMorph = _depthMaterialMorph; + var depthMaterialSkin = _depthMaterialSkin; var depthMaterialMorphSkin = _depthMaterialMorphSkin; - if ( isCube ){ + if ( isPointLight ) { depthMaterial = _distanceMaterial; - depthMaterialMorph = _distanceMaterialMorph; - depthMaterialSkin = _distanceMaterialSkin; + depthMaterialMorph = _distanceMaterialMorph; + depthMaterialSkin = _distanceMaterialSkin; depthMaterialMorphSkin = _distanceMaterialMorphSkin; + } if ( object.customDepthMaterial || object.customDistanceMaterial ) { - if ( isCube ){ + if ( isPointLight ) { newMaterial = object.customDistanceMaterial; } else { newMaterial = object.customDepthMaterial; + } } else if ( useSkinning ) { @@ -391,12 +409,14 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { newMaterial.wireframe = material.wireframe; newMaterial.wireframeLinewidth = material.wireframeLinewidth; - if ( isCube ){ + if ( isPointLight ) { - if( newMaterial.uniforms.lightPos ){ + if ( newMaterial.uniforms.lightPos ) { newMaterial.uniforms.lightPos.value.copy( lightPositionWorld ); + } + } return newMaterial; @@ -414,6 +434,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { var material = object.material; if ( material.visible === true ) { + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); _renderList.push( object );