const vec2 poissonDisk[16] = vec2[]( vec2( -0.94201624, -0.39906216 ), vec2( 0.94558609, -0.76890725 ), vec2( -0.094184101, -0.92938870 ), vec2( 0.34495938, 0.29387760 ), vec2( -0.91588581, 0.45771432 ), vec2( -0.81544232, -0.87912464 ), vec2( -0.38277543, 0.27676845 ), vec2( 0.97484398, 0.75648379 ), vec2( 0.44323325, -0.97511554 ), vec2( 0.53742981, -0.47373420 ), vec2( -0.26496911, -0.41893023 ), vec2( 0.79197514, 0.19090188 ), vec2( -0.24188840, 0.99706507 ), vec2( -0.81409955, 0.91437590 ), vec2( 0.19984126, 0.78641367 ), vec2( 0.14383161, -0.14100790 ) ); #ifndef SHADOW_SAMPLES #define SHADOW_SAMPLES ubo.settings.lighting.shadowSamples #endif #if VXGI float shadowFactorVXGI( const Light, float def ); #endif #if RT float shadowFactorRT( const Light, float def ); #endif #if CUBEMAPS float omniShadowMap( const Light light, float def ) { return 1.0; } #else float omniShadowMap( const Light light, float def ) { float factor = 1.0; const mat4 views[6] = { mat4( 0, 0, 1, 0, 0, 1, 0, 0,-1, 0, 0, 0, 0, 0, 0, 1 ), mat4( 0, 0,-1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 ), mat4( 1, 0, 0, 0, 0, 0, 1, 0, 0,-1, 0, 0, 0, 0, 0, 1 ), mat4( 1, 0, 0, 0, 0, 0,-1, 0, 0, 1, 0, 0, 0, 0, 0, 1 ), mat4( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ), mat4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0,-1, 0, 0, 0, 0, 1 ), }; const vec3 D = normalize(surface.position.world - light.position); const vec3 N = abs(D); uint A = N.y > N.x ? 1 : 0; A = N.z > N[A] ? 2 : A; uint index = A * 2; if ( D[A] < 0.0 ) ++index; vec4 positionClip = light.projection * views[index] * vec4(surface.position.world - light.position, 1.0); positionClip.xy /= positionClip.w; if ( positionClip.x < -1 || positionClip.x >= 1 ) return 1.0f; if ( positionClip.y < -1 || positionClip.y >= 1 ) return 1.0f; if ( positionClip.z <= 0 || positionClip.z >= 1 ) return 1.0f; const float bias = light.depthBias; const float eyeDepth = abs(positionClip.z / positionClip.w); const vec3 sampleOffsetDirections[20] = { vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1), vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1) }; float sampled = 0; const int samples = int(SHADOW_SAMPLES); // cubemap point light if ( light.typeMap == 1 ) { if ( samples < 1 ) { sampled = texture(samplerCubemaps[nonuniformEXT(light.indexMap)], D).r ; } else { for ( int i = 0; i < samples; ++i ) { const int idx = int( float(samples) * interleavedGradientNoise(surface.fragCoord.xy + i)) % samples; vec2 poisson = poissonDisk[idx] / 700.0; vec3 P = vec3( poisson.xy, (poisson.x + poisson.y) * 0.5 ); sampled = texture(samplerCubemaps[nonuniformEXT(light.indexMap)], D + P ).r ; if ( eyeDepth < sampled - bias ) factor -= 1.0 / samples; } return factor; } // separated point lights } else if ( light.typeMap == 2 ) { const vec2 uv = positionClip.xy * 0.5 + 0.5; if ( samples < 1 ) { sampled = texture(samplerTextures[nonuniformEXT(light.indexMap + index)], uv).r ; } else { for ( int i = 0; i < samples; ++i ) { const int idx = int( float(samples) * interleavedGradientNoise(surface.fragCoord.xy + i)) % samples; sampled = texture(samplerTextures[nonuniformEXT(light.indexMap + index)], uv + poissonDisk[idx] / 700.0 ).r ; if ( eyeDepth < sampled - bias ) factor -= 1.0 / samples; } return factor; } } return eyeDepth < sampled - bias ? 0.0 : factor; } #endif float shadowFactor( const Light light, float def ) { #if RT return shadowFactorRT( light, def ); #endif if ( light.typeMap != 0 ) return omniShadowMap( light, def ); if ( !validTextureIndex(light.indexMap) ) #if VXGI return shadowFactorVXGI( light, def ); #else return 1.0; #endif vec4 positionClip = light.projection * light.view * vec4(surface.position.world, 1.0); positionClip.xyz /= positionClip.w; if ( positionClip.x < -1 || positionClip.x >= 1 ) return def; if ( positionClip.y < -1 || positionClip.y >= 1 ) return def; if ( positionClip.z <= 0 || positionClip.z >= 1 ) return def; float factor = 1.0; // spot light if ( abs(light.type) == 2 || abs(light.type) == 3 ) { const float dist = length( positionClip.xy ); if ( dist > 0.5 ) return def; // spot light with attenuation if ( abs(light.type) == 3 ) { factor = 1.0 - (pow(dist * 2,2.0)); } } const vec2 uv = positionClip.xy * 0.5 + 0.5; const float bias = light.depthBias; const float eyeDepth = positionClip.z; const int samples = int(SHADOW_SAMPLES); const float invResolution = 1.0 / textureSize( samplerTextures[nonuniformEXT(light.indexMap)], 0 ).x; if ( samples < 1 ) return eyeDepth < texture(samplerTextures[nonuniformEXT(light.indexMap)], uv).r - bias ? 0.0 : factor; for ( int i = 0; i < samples; ++i ) { const int index = int( float(samples) * interleavedGradientNoise(surface.fragCoord.xy + i)) % samples; const float lightDepth = texture(samplerTextures[nonuniformEXT(light.indexMap)], uv + poissonDisk[index] * invResolution ).r; if ( eyeDepth < lightDepth - bias ) factor -= 1.0 / samples; } return factor; }