in vec4 vpeye; in vec4 vneye; uniform bool u_shadow_receiver; uniform float u_cascade_splits[NUM_SHADOW_CASCADES]; uniform float u_cascade_distances[NUM_SHADOW_CASCADES]; uniform samplerCube shadowMap[MAX_LIGHTS]; uniform sampler2D shadowMap2D[MAX_LIGHTS * NUM_SHADOW_CASCADES]; //// From http://fabiensanglard.net/shadowmappingVSM/index.php float shadow_vsm(float distance, vec3 dir, int light_index) { distance = distance/20; vec2 moments = texture(shadowMap[light_index], dir).rg; // Surface is fully lit. as the current fragment is before the light occluder if (distance <= moments.x) { return 1.0; } // The fragment is either in shadow or penumbra. We now use chebyshev's upperBound to check // How likely this pixel is to be lit (p_max) float variance = moments.y - (moments.x*moments.x); //variance = max(variance, 0.000002); variance = max(variance, 0.00002); float d = distance - moments.x; float p_max = variance / (variance + d*d); return p_max; } float shadow_pcf(float distance, vec3 lightDir, int light_index) { // Determine which cascade to use int cascade_index = -1; for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { if (distance < u_cascade_distances[i]) { cascade_index = i; break; } } if (cascade_index == -1) { cascade_index = NUM_SHADOW_CASCADES - 1; } vec4 fragPosLightSpace = u_lights[light_index].shadow_matrix[cascade_index] * vec4(v_position_ws, 1.0); int index = light_index * NUM_SHADOW_CASCADES + cascade_index; // Perform perspective divide vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; // Transform to [0,1] range projCoords = projCoords * 0.5 + 0.5; float currentDepth = projCoords.z; if (currentDepth > 1.0) { return 1.0; } // Calculate bias vec3 normal = normalize(vneye.xyz); float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); const float bias_modifier = 0.45; if (cascade_index == NUM_SHADOW_CASCADES-1) { bias *= 1 / (u_cascade_distances[NUM_SHADOW_CASCADES-1] * bias_modifier); } else { bias *= 1 / (u_cascade_distances[cascade_index] * bias_modifier); } // PCF float shadow = 0.0; vec2 texelSize = 1.0 / textureSize(shadowMap2D[index], 0); for(int x = -3; x <= 3; ++x) { for(int y = -3; y <= 3; ++y) { float pcfDepth = texture(shadowMap2D[index], projCoords.xy + vec2(x, y) * texelSize).r; shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; } } shadow /= 36.0; return 1.0 - shadow; } vec4 shadowmap(in vec4 peye, in vec4 neye) { float shadowFactor = 0.0; vec3 fragment = vec3(peye); int total_casters = 0; for (int i = 0; i < u_num_lights; i++) { light_t light = u_lights[i]; float factor = 0.0; if (light.type == LIGHT_DIRECTIONAL) { total_casters++; factor += shadow_pcf(-peye.z, light.dir, i); } else if (light.type == LIGHT_POINT || light.type == LIGHT_SPOT) { total_casters++; vec3 light_pos = (view * vec4(light.pos, 1.0)).xyz; vec3 dir = light_pos - fragment; vec4 sc = inv_view * vec4(dir, 0.0); factor += shadow_vsm(length(dir), -sc.xyz, i); } shadowFactor += factor; } if (total_casters == 0) { shadowFactor = 1.0; } else { shadowFactor /= total_casters; } return vec4(vec3(shadowFactor), 1.0); } vec4 shadowing() { if (u_shadow_receiver) { return shadowmap(vpeye, vneye); } else { return vec4(1.0); } }