From 5964cb9b00f80a71d359bf9898422bbe626d68dd Mon Sep 17 00:00:00 2001 From: Mark Kellogg Date: Tue, 15 Sep 2015 15:44:08 -0700 Subject: [PATCH] Updated point-light shadow mapping code to be more efficient with uniforms and varyings. --- src/renderers/WebGLRenderer.js | 25 ++++---- .../ShaderChunk/lights_phong_pars_vertex.glsl | 6 ++ .../ShaderChunk/shadowmap_fragment.glsl | 46 ++++++++------ .../ShaderChunk/shadowmap_pars_fragment.glsl | 63 ++++++++++++------- .../ShaderChunk/shadowmap_pars_vertex.glsl | 2 +- .../shaders/ShaderChunk/shadowmap_vertex.glsl | 2 +- src/renderers/shaders/ShaderLib.js | 13 +--- src/renderers/shaders/UniformsLib.js | 2 - src/renderers/webgl/WebGLShadowMap.js | 12 ++-- 9 files changed, 95 insertions(+), 76 deletions(-) diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 214d14841c..7157192038 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -1701,7 +1701,7 @@ THREE.WebGLRenderer = function ( parameters ) { if ( object.receiveShadow && ! material._shadowPass ) { - refreshUniformsShadow( m_uniforms, lights ); + refreshUniformsShadow( m_uniforms, lights, camera ); } @@ -1953,7 +1953,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - function refreshUniformsShadow ( uniforms, lights ) { + function refreshUniformsShadow ( uniforms, lights, camera ) { if ( uniforms.shadowMatrix ) { @@ -1971,28 +1971,27 @@ THREE.WebGLRenderer = function ( parameters ) { uniforms.shadowCube.value[ j ] = light.shadowMap; uniforms.shadowMap.value[ j ] = null; - uniforms.isShadowCube.value[ j ] = 1; + + // for point lights we set the sign of the shadowDarkness uniform to be negative + 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 + // world-space position of the light in the shader. + uniforms.shadowMatrix.value[ j ] = camera.matrixWorld; } else { uniforms.shadowMap.value[ j ] = light.shadowMap; - uniforms.isShadowCube.value[ j ] = 0; uniforms.shadowCube.value[ j ] = null; + uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; + uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; } uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; - - uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; - - uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; uniforms.shadowBias.value[ j ] = light.shadowBias; - _vector3.setFromMatrixPosition( light.matrixWorld ); - uniforms.shadowLightPosition.value[ j * 3 ] = _vector3.x; - uniforms.shadowLightPosition.value[ j * 3 + 1 ] = _vector3.y; - uniforms.shadowLightPosition.value[ j * 3 + 2 ] = _vector3.z; - j ++; } diff --git a/src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl b/src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl index 44393f85d9..5bf7abd432 100644 --- a/src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl +++ b/src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl @@ -3,3 +3,9 @@ varying vec3 vWorldPosition; #endif + +#if MAX_POINT_LIGHTS > 0 + + uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ]; + +#endif diff --git a/src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl b/src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl index 4e4574eaf9..818de7f756 100644 --- a/src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl +++ b/src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl @@ -13,16 +13,24 @@ vec3 shadowColor = vec3( 1.0 ); for( int i = 0; i < MAX_SHADOWS; i ++ ) { - vec3 lightToPosition = vWPosition[ i ].xyz - shadowLightPosition[ i ]; - vec3 lightToFragment = normalize( lightToPosition ); - float distanceToLight = length( lightToPosition ); - int currentIsCube = isShadowCube[ i ]; + + // to save on uniform space, we use the sign of @shadowDarkness[ i ] to determine + // whether or not this light is a point light ( shadowDarkness[ i ] < 0 == point light) + bool isPointLight = shadowDarkness[ i ] < 0.0; + + // get the real shadow darkness + float realShadowDarkness = abs( shadowDarkness[ i ] ); + + // 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; + vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w; float shadow = 0.0; - // if ( something && something ) breaks ATI OpenGL shader compiler - // if ( all( something, something ) ) using this instead + // if ( something && something ) breaks ATI OpenGL shader compiler + // if ( all( something, something ) ) using this instead bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 ); bool inFrustum = all( inFrustumVec ); @@ -31,14 +39,14 @@ bool frustumTest = all( frustumTestVec ); - if ( frustumTest || currentIsCube == 1) { + if ( frustumTest || isPointLight ) { shadowCoord.z += shadowBias[ i ]; #if defined( SHADOWMAP_TYPE_PCF ) - if( currentIsCube == 1 ){ + if( isPointLight ){ - shadow = sampleCubeShadowMapPCF( shadowCube[ i ], lightToFragment, distanceToLight, cubeTexelSize, 1.5); + shadow = sampleCubeShadowMapPCF( i, normalize( lightToPosition ), length( lightToPosition ), cubeTexelSize, 1.5); } else { // Percentage-close filtering @@ -106,13 +114,13 @@ if ( fDepth < shadowCoord.z ) shadow += shadowDelta; } - shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) ); + shadowColor = shadowColor * vec3( ( 1.0 - realShadowDarkness * shadow ) ); #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) - if( currentIsCube == 1 ){ + if( isPointLight ){ - shadow = sampleCubeShadowMapPCF( shadowCube[ i ], lightToFragment, distanceToLight, cubeTexelSize, 2.5 ); + shadow = sampleCubeShadowMapPCF( i, normalize( lightToPosition ), length( lightToPosition ), cubeTexelSize, 2.5 ); } else { @@ -165,15 +173,15 @@ shadow = dot( shadowValues, vec4( 1.0 ) ); } - shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) ); + shadowColor = shadowColor * vec3( ( 1.0 - realShadowDarkness * shadow ) ); #else - if( currentIsCube == 1 ){ + if( isPointLight ){ - float dist = getCubeMapFloat( shadowCube[ i ], lightToFragment ); - if ( distanceToLight >= dist) - shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] ); + float dist = getCubeShadowMapFloat( i, normalize( lightToPosition ) ); + if ( length( lightToPosition ) >= dist) + shadowColor = shadowColor * vec3( 1.0 - realShadowDarkness ); } else { @@ -184,11 +192,11 @@ // spot with multiple shadows is darker - shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] ); + shadowColor = shadowColor * vec3( 1.0 - realShadowDarkness ); // spot with multiple shadows has the same color as single shadow spot - // shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) ); + // shadowColor = min( shadowColor, vec3( realShadowDarkness ) ); } #endif diff --git a/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl b/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl index cf5d17a2c9..6a9b1e6592 100644 --- a/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl +++ b/src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl @@ -1,6 +1,5 @@ #ifdef USE_SHADOWMAP - uniform int isShadowCube[ MAX_SHADOWS ]; uniform samplerCube shadowCube[ MAX_SHADOWS ]; uniform sampler2D shadowMap[ MAX_SHADOWS ]; uniform vec2 shadowMapSize[ MAX_SHADOWS ]; @@ -8,10 +7,7 @@ uniform float shadowDarkness[ MAX_SHADOWS ]; uniform float shadowBias[ MAX_SHADOWS ]; - uniform vec3 shadowLightPosition[ MAX_SHADOWS ]; - varying vec4 vShadowCoord[ MAX_SHADOWS ]; - varying vec4 vWPosition[ MAX_SHADOWS ]; float unpackDepth( const in vec4 rgba_depth ) { @@ -21,25 +17,25 @@ } - vec4 pack1K (float depth) { + 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); + 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) { + 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); - return dot(color, bitSh) * 1000.0; + const vec4 bitSh = vec4( 1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0 ); + return dot( color, bitSh ) * 1000.0; } - vec3 gridSamplingDisk[20]; + vec3 gridSamplingDisk[ 20 ]; int gridSamplingInitialized = 0; void initGridSamplingDisk(){ @@ -67,17 +63,35 @@ } - float getCubeMapFloat(in samplerCube cube, in vec3 baseDirection){ - - vec4 data = textureCube(cube, baseDirection); + float getCubeShadowMapFloat( const in int cubeIndex, in vec3 baseDirection ){ + + vec4 data = vec4( 0, 0, 0, 0 ); + + // This loop may seem silly and unnecessary, but we can't use @cubeIndex to access @shadowCube + // directly, since its bounds are unknown to the compiler. The compiler + // knows the bounds of the loop variable 'i' so we CAN use that to index + // into @shadowCube. The alternative to this is to send the samplerCube directly + // to this function as a parameter, but come drivers don't allow that. + + for( int i = 0; i < MAX_SHADOWS; i++ ) { + + if( i == cubeIndex ){ + + data = textureCube(shadowCube[ i ], baseDirection); + break; + + } + + } + float dist = unpack1K( data ); return dist; } - float sampleCubeShadowMapPCF(in samplerCube cube, in vec3 baseDirection, in float curDistance, in float texSize, float softness){ + float sampleCubeShadowMapPCF( const in int cubeIndex, in vec3 baseDirection, in float curDistance, in float texSize, float softness ){ - if( gridSamplingInitialized == 0){ + if( gridSamplingInitialized == 0 ){ initGridSamplingDisk(); gridSamplingInitialized = 1; @@ -89,17 +103,17 @@ float numSamples = 0.0; float shadowFactor = 0.0; - float dist = getCubeMapFloat(cube, baseDirection); - if ( curDistance >= dist) + float dist = getCubeShadowMapFloat( cubeIndex, baseDirection ); + if ( curDistance >= dist ) shadowFactor += 1.0; numSamples += 1.0; // evaluate each sampling direction - for(int i=0; i<20; i++){ - - vec3 offset = gridSamplingDisk[i] * diskRadius * texSize; - dist = getCubeMapFloat(cube, vec3(baseDirection + offset)); - if ( curDistance >= dist) + for( int i = 0; i < 20; i++ ){ + + vec3 offset = gridSamplingDisk[ i ] * diskRadius * texSize; + dist = getCubeShadowMapFloat( cubeIndex, vec3( baseDirection + offset ) ); + if ( curDistance >= dist ) shadowFactor += 1.0; numSamples += 1.0; @@ -107,6 +121,7 @@ shadowFactor /= numSamples; return shadowFactor; + } #endif \ No newline at end of file diff --git a/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl b/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl index 3b376e54f9..619c2462c7 100644 --- a/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl +++ b/src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl @@ -1 +1 @@ -#ifdef USE_SHADOWMAP varying vec4 vShadowCoord[ MAX_SHADOWS ]; varying vec4 vWPosition[ MAX_SHADOWS ]; uniform mat4 shadowMatrix[ MAX_SHADOWS ]; uniform vec3 shadowLightPosition[ 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 fa943ad866..c99bb40d7a 100644 --- a/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl +++ b/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl @@ -1 +1 @@ -#ifdef USE_SHADOWMAP for( int i = 0; i < MAX_SHADOWS; i ++ ) { vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition; vWPosition[ i ] = worldPosition; } #endif \ No newline at end of file +#ifdef USE_SHADOWMAP for( int i = 0; i < MAX_SHADOWS; i ++ ) { #if MAX_POINT_LIGHTS > 0 // 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 4b6d51b97a..0c75fbe217 100644 --- a/src/renderers/shaders/ShaderLib.js +++ b/src/renderers/shaders/ShaderLib.js @@ -856,21 +856,14 @@ THREE.ShaderLib = { "void main() {", - "#ifdef USE_SKINNING", - - " vWorldPosition = modelMatrix * skinned;", - - "#else", - - " vWorldPosition = modelMatrix * vec4( position, 1.0 );", - - "#endif", - THREE.ShaderChunk[ "skinbase_vertex" ], THREE.ShaderChunk[ "begin_vertex" ], THREE.ShaderChunk[ "morphtarget_vertex" ], THREE.ShaderChunk[ "skinning_vertex" ], THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "worldpos_vertex" ], + + "vWorldPosition = worldPosition;", "}" diff --git a/src/renderers/shaders/UniformsLib.js b/src/renderers/shaders/UniformsLib.js index ce6ed5eb93..bdc7a9fb9d 100644 --- a/src/renderers/shaders/UniformsLib.js +++ b/src/renderers/shaders/UniformsLib.js @@ -117,7 +117,6 @@ THREE.UniformsLib = { shadowmap: { - "isShadowCube": { type: "iv1", value: [] }, "shadowCube": { type: "tv", value: [] }, "shadowMap": { type: "tv", value: [] }, @@ -127,7 +126,6 @@ THREE.UniformsLib = { "shadowDarkness": { type: "fv1", value: [] }, "shadowMatrix" : { type: "m4v", value: [] }, - "shadowLightPosition" : { type: "fv", value: [] } } diff --git a/src/renderers/webgl/WebGLShadowMap.js b/src/renderers/webgl/WebGLShadowMap.js index 6bf727c152..79563b8ad1 100644 --- a/src/renderers/webgl/WebGLShadowMap.js +++ b/src/renderers/webgl/WebGLShadowMap.js @@ -14,7 +14,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { _max = new THREE.Vector3(), _lookTarget = new THREE.Vector3(), - _lightPosition = new THREE.Vector3(), + _lightPositionWorld = new THREE.Vector3(), _renderList = []; @@ -224,8 +224,8 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { var shadowMatrix = light.shadowMatrix; var shadowCamera = light.shadowCamera; - _lightPosition.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( _lightPosition); + _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); + shadowCamera.position.copy( _lightPositionWorld); // render shadow map for each cube face (if omni-directional) or // run a single pass if not @@ -306,7 +306,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { if ( groupMaterial.visible === true ) { - var depthMaterial = getDepthMaterial( object, groupMaterial, isCube, _lightPosition ); + var depthMaterial = getDepthMaterial( object, groupMaterial, isCube, _lightPositionWorld ); _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial , object, group ); } @@ -345,7 +345,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { }; - function getDepthMaterial( object, material, isCube, lightPosition) { + function getDepthMaterial( object, material, isCube, lightPositionWorld) { var geometry = object.geometry; @@ -400,7 +400,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { if( newMaterial.uniforms.lightPos ){ - newMaterial.uniforms.lightPos.value.copy( lightPosition ); + newMaterial.uniforms.lightPos.value.copy( lightPositionWorld ); } } -- GitLab