|
|
@@ -15,7 +15,16 @@ export default /* glsl */`
|
|
|
|
|
|
#if NUM_DIR_LIGHT_SHADOWS > 0
|
|
|
|
|
|
- uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];
|
|
|
+ #if defined( SHADOWMAP_TYPE_PCF )
|
|
|
+
|
|
|
+ uniform sampler2DShadow directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];
|
|
|
+
|
|
|
+ #else
|
|
|
+
|
|
|
+ uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];
|
|
|
+
|
|
|
+ #endif
|
|
|
+
|
|
|
varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];
|
|
|
|
|
|
struct DirectionalLightShadow {
|
|
|
@@ -32,7 +41,15 @@ export default /* glsl */`
|
|
|
|
|
|
#if NUM_SPOT_LIGHT_SHADOWS > 0
|
|
|
|
|
|
- uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];
|
|
|
+ #if defined( SHADOWMAP_TYPE_PCF )
|
|
|
+
|
|
|
+ uniform sampler2DShadow spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];
|
|
|
+
|
|
|
+ #else
|
|
|
+
|
|
|
+ uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];
|
|
|
+
|
|
|
+ #endif
|
|
|
|
|
|
struct SpotLightShadow {
|
|
|
float shadowIntensity;
|
|
|
@@ -48,7 +65,16 @@ export default /* glsl */`
|
|
|
|
|
|
#if NUM_POINT_LIGHT_SHADOWS > 0
|
|
|
|
|
|
- uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];
|
|
|
+ #if defined( SHADOWMAP_TYPE_PCF )
|
|
|
+
|
|
|
+ uniform samplerCubeShadow pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];
|
|
|
+
|
|
|
+ #elif defined( SHADOWMAP_TYPE_BASIC )
|
|
|
+
|
|
|
+ uniform samplerCube pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];
|
|
|
+
|
|
|
+ #endif
|
|
|
+
|
|
|
varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];
|
|
|
|
|
|
struct PointLightShadow {
|
|
|
@@ -65,272 +91,243 @@ export default /* glsl */`
|
|
|
|
|
|
#endif
|
|
|
|
|
|
- float texture2DCompare( sampler2D depths, vec2 uv, float compare ) {
|
|
|
+ #if defined( SHADOWMAP_TYPE_PCF )
|
|
|
|
|
|
- float depth = unpackRGBAToDepth( texture2D( depths, uv ) );
|
|
|
+ // Interleaved Gradient Noise for randomizing sampling patterns
|
|
|
+ float interleavedGradientNoise( vec2 position ) {
|
|
|
|
|
|
- #ifdef USE_REVERSED_DEPTH_BUFFER
|
|
|
+ return fract( 52.9829189 * fract( dot( position, vec2( 0.06711056, 0.00583715 ) ) ) );
|
|
|
|
|
|
- return step( depth, compare );
|
|
|
+ }
|
|
|
|
|
|
- #else
|
|
|
+ // Vogel disk sampling for uniform circular distribution
|
|
|
+ vec2 vogelDiskSample( int sampleIndex, int samplesCount, float phi ) {
|
|
|
|
|
|
- return step( compare, depth );
|
|
|
+ const float goldenAngle = 2.399963229728653;
|
|
|
+ float r = sqrt( float( sampleIndex ) + 0.5 ) / sqrt( float( samplesCount ) );
|
|
|
+ float theta = float( sampleIndex ) * goldenAngle + phi;
|
|
|
+ return vec2( cos( theta ), sin( theta ) ) * r;
|
|
|
|
|
|
- #endif
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ #endif
|
|
|
|
|
|
- vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {
|
|
|
+ #if defined( SHADOWMAP_TYPE_PCF )
|
|
|
|
|
|
- return unpackRGBATo2Half( texture2D( shadow, uv ) );
|
|
|
+ float getShadow( sampler2DShadow shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
|
|
|
|
|
|
- }
|
|
|
+ float shadow = 1.0;
|
|
|
|
|
|
- float VSMShadow( sampler2D shadow, vec2 uv, float compare ) {
|
|
|
+ shadowCoord.xyz /= shadowCoord.w;
|
|
|
+ shadowCoord.z += shadowBias;
|
|
|
|
|
|
- vec2 distribution = texture2DDistribution( shadow, uv );
|
|
|
+ bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;
|
|
|
+ bool frustumTest = inFrustum && shadowCoord.z <= 1.0;
|
|
|
|
|
|
- float mean = distribution.x;
|
|
|
- float variance = distribution.y * distribution.y;
|
|
|
+ if ( frustumTest ) {
|
|
|
|
|
|
- #ifdef USE_REVERSED_DEPTH_BUFFER
|
|
|
+ // Hardware PCF with LinearFilter gives us 4-tap filtering per sample
|
|
|
+ // 5 samples using Vogel disk + IGN = effectively 20 filtered taps with better distribution
|
|
|
+ vec2 texelSize = vec2( 1.0 ) / shadowMapSize;
|
|
|
+ float radius = shadowRadius * texelSize.x;
|
|
|
|
|
|
- float hard_shadow = step( mean, compare );
|
|
|
+ // Use IGN to rotate sampling pattern per pixel
|
|
|
+ float phi = interleavedGradientNoise( gl_FragCoord.xy ) * 6.28318530718; // 2*PI
|
|
|
|
|
|
- #else
|
|
|
+ shadow = (
|
|
|
+ texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 0, 5, phi ) * radius, shadowCoord.z ) ) +
|
|
|
+ texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 1, 5, phi ) * radius, shadowCoord.z ) ) +
|
|
|
+ texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 2, 5, phi ) * radius, shadowCoord.z ) ) +
|
|
|
+ texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 3, 5, phi ) * radius, shadowCoord.z ) ) +
|
|
|
+ texture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 4, 5, phi ) * radius, shadowCoord.z ) )
|
|
|
+ ) * 0.2;
|
|
|
|
|
|
- float hard_shadow = step( compare, mean );
|
|
|
+ }
|
|
|
|
|
|
- #endif
|
|
|
+ return mix( 1.0, shadow, shadowIntensity );
|
|
|
|
|
|
- // Early return if fully lit
|
|
|
- if ( hard_shadow == 1.0 ) return 1.0;
|
|
|
+ }
|
|
|
|
|
|
- // Variance must be non-zero to avoid division by zero
|
|
|
- variance = max( variance, 0.0000001 );
|
|
|
+ #elif defined( SHADOWMAP_TYPE_VSM )
|
|
|
|
|
|
- // Distance from mean
|
|
|
- float d = compare - mean;
|
|
|
+ float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
|
|
|
|
|
|
- // Chebyshev's inequality for upper bound on probability
|
|
|
- float p_max = variance / ( variance + d * d );
|
|
|
+ float shadow = 1.0;
|
|
|
|
|
|
- // Reduce light bleeding by remapping [amount, 1] to [0, 1]
|
|
|
- p_max = clamp( ( p_max - 0.3 ) / 0.65, 0.0, 1.0 );
|
|
|
+ shadowCoord.xyz /= shadowCoord.w;
|
|
|
+ shadowCoord.z += shadowBias;
|
|
|
|
|
|
- return max( hard_shadow, p_max );
|
|
|
+ bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;
|
|
|
+ bool frustumTest = inFrustum && shadowCoord.z <= 1.0;
|
|
|
|
|
|
- }
|
|
|
+ if ( frustumTest ) {
|
|
|
|
|
|
- float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
|
|
|
+ vec2 distribution = texture2D( shadowMap, shadowCoord.xy ).rg;
|
|
|
|
|
|
- float shadow = 1.0;
|
|
|
+ float mean = distribution.x;
|
|
|
+ float variance = distribution.y * distribution.y;
|
|
|
|
|
|
- shadowCoord.xyz /= shadowCoord.w;
|
|
|
- shadowCoord.z += shadowBias;
|
|
|
+ #ifdef USE_REVERSED_DEPTH_BUFFER
|
|
|
|
|
|
- bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;
|
|
|
- bool frustumTest = inFrustum && shadowCoord.z <= 1.0;
|
|
|
+ float hard_shadow = step( mean, shadowCoord.z );
|
|
|
|
|
|
- if ( frustumTest ) {
|
|
|
+ #else
|
|
|
|
|
|
- #if defined( SHADOWMAP_TYPE_PCF )
|
|
|
+ float hard_shadow = step( shadowCoord.z, mean );
|
|
|
|
|
|
- vec2 texelSize = vec2( 1.0 ) / shadowMapSize;
|
|
|
+ #endif
|
|
|
|
|
|
- float dx0 = - texelSize.x * shadowRadius;
|
|
|
- float dy0 = - texelSize.y * shadowRadius;
|
|
|
- float dx1 = + texelSize.x * shadowRadius;
|
|
|
- float dy1 = + texelSize.y * shadowRadius;
|
|
|
- float dx2 = dx0 / 2.0;
|
|
|
- float dy2 = dy0 / 2.0;
|
|
|
- float dx3 = dx1 / 2.0;
|
|
|
- float dy3 = dy1 / 2.0;
|
|
|
+ // Early return if fully lit
|
|
|
+ if ( hard_shadow == 1.0 ) {
|
|
|
|
|
|
- shadow = (
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )
|
|
|
- ) * ( 1.0 / 17.0 );
|
|
|
-
|
|
|
- #elif defined( SHADOWMAP_TYPE_PCF_SOFT )
|
|
|
-
|
|
|
- vec2 texelSize = vec2( 1.0 ) / shadowMapSize;
|
|
|
- float dx = texelSize.x;
|
|
|
- float dy = texelSize.y;
|
|
|
-
|
|
|
- vec2 uv = shadowCoord.xy;
|
|
|
- vec2 f = fract( uv * shadowMapSize + 0.5 );
|
|
|
- uv -= f * texelSize;
|
|
|
+ shadow = 1.0;
|
|
|
|
|
|
- shadow = (
|
|
|
- texture2DCompare( shadowMap, uv, shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +
|
|
|
- texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +
|
|
|
- mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ),
|
|
|
- texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),
|
|
|
- f.x ) +
|
|
|
- mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ),
|
|
|
- texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),
|
|
|
- f.x ) +
|
|
|
- mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ),
|
|
|
- texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),
|
|
|
- f.y ) +
|
|
|
- mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ),
|
|
|
- texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),
|
|
|
- f.y ) +
|
|
|
- mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ),
|
|
|
- texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),
|
|
|
- f.x ),
|
|
|
- mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ),
|
|
|
- texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),
|
|
|
- f.x ),
|
|
|
- f.y )
|
|
|
- ) * ( 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 );
|
|
|
+ } else {
|
|
|
|
|
|
- #endif
|
|
|
+ // Variance must be non-zero to avoid division by zero
|
|
|
+ variance = max( variance, 0.0000001 );
|
|
|
|
|
|
- }
|
|
|
+ // Distance from mean
|
|
|
+ float d = shadowCoord.z - mean;
|
|
|
|
|
|
- return mix( 1.0, shadow, shadowIntensity );
|
|
|
+ // Chebyshev's inequality for upper bound on probability
|
|
|
+ float p_max = variance / ( variance + d * d );
|
|
|
|
|
|
- }
|
|
|
+ // Reduce light bleeding by remapping [amount, 1] to [0, 1]
|
|
|
+ p_max = clamp( ( p_max - 0.3 ) / 0.65, 0.0, 1.0 );
|
|
|
+
|
|
|
+ shadow = max( hard_shadow, p_max );
|
|
|
|
|
|
- // cubeToUV() maps a 3D direction vector suitable for cube texture mapping to a 2D
|
|
|
- // vector suitable for 2D texture mapping. This code uses the following layout for the
|
|
|
- // 2D texture:
|
|
|
- //
|
|
|
- // xzXZ
|
|
|
- // y Y
|
|
|
- //
|
|
|
- // Y - Positive y direction
|
|
|
- // y - Negative y direction
|
|
|
- // X - Positive x direction
|
|
|
- // x - Negative x direction
|
|
|
- // Z - Positive z direction
|
|
|
- // z - Negative z direction
|
|
|
- //
|
|
|
- // Source and test bed:
|
|
|
- // https://gist.github.com/tschw/da10c43c467ce8afd0c4
|
|
|
+ }
|
|
|
|
|
|
- vec2 cubeToUV( vec3 v, float texelSizeY ) {
|
|
|
+ }
|
|
|
|
|
|
- // Number of texels to avoid at the edge of each square
|
|
|
+ return mix( 1.0, shadow, shadowIntensity );
|
|
|
|
|
|
- vec3 absV = abs( v );
|
|
|
+ }
|
|
|
|
|
|
- // Intersect unit cube
|
|
|
+ #else // SHADOWMAP_TYPE_BASIC
|
|
|
|
|
|
- float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );
|
|
|
- absV *= scaleToCube;
|
|
|
+ float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {
|
|
|
|
|
|
- // Apply scale to avoid seams
|
|
|
+ float shadow = 1.0;
|
|
|
|
|
|
- // two texels less per square (one texel will do for NEAREST)
|
|
|
- v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );
|
|
|
+ shadowCoord.xyz /= shadowCoord.w;
|
|
|
+ shadowCoord.z += shadowBias;
|
|
|
|
|
|
- // Unwrap
|
|
|
+ bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;
|
|
|
+ bool frustumTest = inFrustum && shadowCoord.z <= 1.0;
|
|
|
|
|
|
- // space: -1 ... 1 range for each square
|
|
|
- //
|
|
|
- // #X## dim := ( 4 , 2 )
|
|
|
- // # # center := ( 1 , 1 )
|
|
|
+ if ( frustumTest ) {
|
|
|
|
|
|
- vec2 planar = v.xy;
|
|
|
+ float depth = texture2D( shadowMap, shadowCoord.xy ).r;
|
|
|
|
|
|
- float almostATexel = 1.5 * texelSizeY;
|
|
|
- float almostOne = 1.0 - almostATexel;
|
|
|
+ #ifdef USE_REVERSED_DEPTH_BUFFER
|
|
|
|
|
|
- if ( absV.z >= almostOne ) {
|
|
|
+ shadow = step( depth, shadowCoord.z );
|
|
|
|
|
|
- if ( v.z > 0.0 )
|
|
|
- planar.x = 4.0 - v.x;
|
|
|
+ #else
|
|
|
|
|
|
- } else if ( absV.x >= almostOne ) {
|
|
|
+ shadow = step( shadowCoord.z, depth );
|
|
|
|
|
|
- float signX = sign( v.x );
|
|
|
- planar.x = v.z * signX + 2.0 * signX;
|
|
|
+ #endif
|
|
|
|
|
|
- } else if ( absV.y >= almostOne ) {
|
|
|
+ }
|
|
|
|
|
|
- float signY = sign( v.y );
|
|
|
- planar.x = v.x + 2.0 * signY + 2.0;
|
|
|
- planar.y = v.z * signY - 2.0;
|
|
|
+ return mix( 1.0, shadow, shadowIntensity );
|
|
|
|
|
|
}
|
|
|
|
|
|
- // Transform to UV space
|
|
|
+ #endif
|
|
|
|
|
|
- // scale := 0.5 / dim
|
|
|
- // translate := ( center + 0.5 ) / dim
|
|
|
- return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );
|
|
|
+ #if NUM_POINT_LIGHT_SHADOWS > 0
|
|
|
|
|
|
- }
|
|
|
+ #if defined( SHADOWMAP_TYPE_PCF )
|
|
|
|
|
|
- float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {
|
|
|
+ float getPointShadow( samplerCubeShadow shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {
|
|
|
|
|
|
float shadow = 1.0;
|
|
|
|
|
|
// for point lights, the uniform @vShadowCoord is re-purposed to hold
|
|
|
// the vector from the light to the world-space position of the fragment.
|
|
|
vec3 lightToPosition = shadowCoord.xyz;
|
|
|
-
|
|
|
- float lightToPositionLength = length( lightToPosition );
|
|
|
|
|
|
- if ( lightToPositionLength - shadowCameraFar <= 0.0 && lightToPositionLength - shadowCameraNear >= 0.0 ) {
|
|
|
+ // Direction from light to fragment
|
|
|
+ vec3 bd3D = normalize( lightToPosition );
|
|
|
+
|
|
|
+ // For cube shadow maps, depth is stored as distance along each face's view axis, not radial distance
|
|
|
+ // The view-space depth is the maximum component of the direction vector (which face is sampled)
|
|
|
+ vec3 absVec = abs( lightToPosition );
|
|
|
+ float viewSpaceZ = max( max( absVec.x, absVec.y ), absVec.z );
|
|
|
|
|
|
- // dp = normalized distance from light to fragment position
|
|
|
- float dp = ( lightToPositionLength - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); // need to clamp?
|
|
|
+ if ( viewSpaceZ - shadowCameraFar <= 0.0 && viewSpaceZ - shadowCameraNear >= 0.0 ) {
|
|
|
+
|
|
|
+ // Calculate perspective depth for cube shadow map
|
|
|
+ // Standard perspective depth formula: depth = (far * (z - near)) / (z * (far - near))
|
|
|
+ float dp = ( shadowCameraFar * ( viewSpaceZ - shadowCameraNear ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) );
|
|
|
dp += shadowBias;
|
|
|
|
|
|
- // bd3D = base direction 3D
|
|
|
- vec3 bd3D = normalize( lightToPosition );
|
|
|
+ // Hardware PCF with LinearFilter gives us 4-tap filtering per sample
|
|
|
+ // Use Vogel disk + IGN sampling for better quality
|
|
|
+ float texelSize = shadowRadius / shadowMapSize.x;
|
|
|
|
|
|
- vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );
|
|
|
+ // Build a tangent-space coordinate system for applying offsets
|
|
|
+ vec3 absDir = abs( bd3D );
|
|
|
+ vec3 tangent = absDir.x > absDir.z ? vec3( 0.0, 1.0, 0.0 ) : vec3( 1.0, 0.0, 0.0 );
|
|
|
+ tangent = normalize( cross( bd3D, tangent ) );
|
|
|
+ vec3 bitangent = cross( bd3D, tangent );
|
|
|
|
|
|
- #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )
|
|
|
+ // Use IGN to rotate sampling pattern per pixel
|
|
|
+ float phi = interleavedGradientNoise( gl_FragCoord.xy ) * 6.28318530718;
|
|
|
|
|
|
- vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;
|
|
|
+ shadow = (
|
|
|
+ texture( shadowMap, vec4( bd3D + ( tangent * vogelDiskSample( 0, 5, phi ).x + bitangent * vogelDiskSample( 0, 5, phi ).y ) * texelSize, dp ) ) +
|
|
|
+ texture( shadowMap, vec4( bd3D + ( tangent * vogelDiskSample( 1, 5, phi ).x + bitangent * vogelDiskSample( 1, 5, phi ).y ) * texelSize, dp ) ) +
|
|
|
+ texture( shadowMap, vec4( bd3D + ( tangent * vogelDiskSample( 2, 5, phi ).x + bitangent * vogelDiskSample( 2, 5, phi ).y ) * texelSize, dp ) ) +
|
|
|
+ texture( shadowMap, vec4( bd3D + ( tangent * vogelDiskSample( 3, 5, phi ).x + bitangent * vogelDiskSample( 3, 5, phi ).y ) * texelSize, dp ) ) +
|
|
|
+ texture( shadowMap, vec4( bd3D + ( tangent * vogelDiskSample( 4, 5, phi ).x + bitangent * vogelDiskSample( 4, 5, phi ).y ) * texelSize, dp ) )
|
|
|
+ ) * 0.2;
|
|
|
|
|
|
- shadow = (
|
|
|
- texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +
|
|
|
- texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +
|
|
|
- texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +
|
|
|
- texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +
|
|
|
- texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +
|
|
|
- texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +
|
|
|
- texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +
|
|
|
- texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +
|
|
|
- texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )
|
|
|
- ) * ( 1.0 / 9.0 );
|
|
|
+ }
|
|
|
+
|
|
|
+ return mix( 1.0, shadow, shadowIntensity );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ #elif defined( SHADOWMAP_TYPE_BASIC )
|
|
|
|
|
|
- #else // no percentage-closer filtering
|
|
|
+ float getPointShadow( samplerCube shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {
|
|
|
|
|
|
- shadow = texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );
|
|
|
+ float shadow = 1.0;
|
|
|
+
|
|
|
+ // for point lights, the uniform @vShadowCoord is re-purposed to hold
|
|
|
+ // the vector from the light to the world-space position of the fragment.
|
|
|
+ vec3 lightToPosition = shadowCoord.xyz;
|
|
|
+
|
|
|
+ // Direction from light to fragment
|
|
|
+ vec3 bd3D = normalize( lightToPosition );
|
|
|
+
|
|
|
+ // For cube shadow maps, depth is stored as distance along each face's view axis, not radial distance
|
|
|
+ // The view-space depth is the maximum component of the direction vector (which face is sampled)
|
|
|
+ vec3 absVec = abs( lightToPosition );
|
|
|
+ float viewSpaceZ = max( max( absVec.x, absVec.y ), absVec.z );
|
|
|
+
|
|
|
+ if ( viewSpaceZ - shadowCameraFar <= 0.0 && viewSpaceZ - shadowCameraNear >= 0.0 ) {
|
|
|
+
|
|
|
+ // Calculate perspective depth for cube shadow map
|
|
|
+ // Standard perspective depth formula: depth = (far * (z - near)) / (z * (far - near))
|
|
|
+ float dp = ( shadowCameraFar * ( viewSpaceZ - shadowCameraNear ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) );
|
|
|
+ dp += shadowBias;
|
|
|
+
|
|
|
+ float depth = textureCube( shadowMap, bd3D ).r;
|
|
|
+
|
|
|
+ #ifdef USE_REVERSED_DEPTH_BUFFER
|
|
|
+
|
|
|
+ shadow = step( depth, dp );
|
|
|
+
|
|
|
+ #else
|
|
|
+
|
|
|
+ shadow = step( dp, depth );
|
|
|
|
|
|
#endif
|
|
|
|
|
|
@@ -340,5 +337,9 @@ export default /* glsl */`
|
|
|
|
|
|
}
|
|
|
|
|
|
+ #endif
|
|
|
+
|
|
|
+ #endif
|
|
|
+
|
|
|
#endif
|
|
|
`;
|