<@if not SHADOW_SLH@> <@def SHADOW_SLH@> // the shadow texture uniform sampler2DShadow shadowMap; struct ShadowTransform { mat4 projection; mat4 viewInverse; float bias; float scale; }; uniform shadowTransformBuffer { ShadowTransform _shadowTransform; }; mat4 getShadowViewInverse() { return _shadowTransform.viewInverse; } mat4 getShadowProjection() { return _shadowTransform.projection; } float getShadowScale() { return _shadowTransform.scale; } float getShadowBias() { return _shadowTransform.bias; } // Compute the texture coordinates from world coordinates vec4 evalShadowTexcoord(vec4 position) { mat4 biasMatrix = mat4( 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0); float bias = -getShadowBias(); vec4 shadowCoord = biasMatrix * getShadowProjection() * getShadowViewInverse() * position; return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0); } // Sample the shadowMap with PCF (built-in) float fetchShadow(vec3 shadowTexcoord) { return texture(shadowMap, shadowTexcoord); } vec2 PCFkernel[4] = vec2[4]( vec2(-1.5, 0.5), vec2(0.5, 0.5), vec2(-1.5, -1.5), vec2(0.5, -1.5) ); float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { float pcfRadius = 3.0; float shadowScale = getShadowScale(); // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html vec2 offset = pcfRadius * step(fract(position.xy), vec2(0.5, 0.5)); float shadowAttenuation = (0.25 * ( fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[1], 0.0)) + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0)) )); return shadowAttenuation; } float evalShadowAttenuation(vec4 position) { vec4 shadowTexcoord = evalShadowTexcoord(position); if (shadowTexcoord.x < 0.0 || shadowTexcoord.x > 1.0 || shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0 || shadowTexcoord.z < 0.0 || shadowTexcoord.z > 1.0) { // If a point is not in the map, do not attenuate return 1.0; } return evalShadowAttenuationPCF(position, shadowTexcoord); } <@endif@>