#ifndef SHADOWMAP_GLSL #define SHADOWMAP_GLSL #include "utils.glsl" uniform bool u_shadow_receiver; uniform float u_cascade_distances[MAX_LIGHTS * NUM_SHADOW_CASCADES]; uniform samplerCube shadowMap[MAX_LIGHTS]; uniform sampler2D shadowMap2D[MAX_LIGHTS * NUM_SHADOW_CASCADES]; // const float bias_modifier[NUM_SHADOW_CASCADES] = float[NUM_SHADOW_CASCADES](0.95, 0.35, 0.20, 0.1, 0.1, 0.1); const float bias_modifier[NUM_SHADOW_CASCADES] = float[NUM_SHADOW_CASCADES](0.95, 0.35, 0.20, 0.15); //// From http://fabiensanglard.net/shadowmappingVSM/index.php float shadow_vsm(float distance, vec3 dir, int light_index, float min_variance, float variance_transition) { distance = distance/200; // Define offsets for 3x3 PCF vec3 offsets[9] = vec3[9]( vec3(-1, -1, 0) * 0.01, vec3( 0, -1, 0) * 0.01, vec3( 1, -1, 0) * 0.01, vec3(-1, 0, 0) * 0.01, vec3( 0, 0, 0) * 0.01, vec3( 1, 0, 0) * 0.01, vec3(-1, 1, 0) * 0.01, vec3( 0, 1, 0) * 0.01, vec3( 1, 1, 0) * 0.01 ); float shadow = 0.0; // Perform 3x3 PCF for (int i = 0; i < 9; i++) { vec3 sampleDir = dir + offsets[i] * (rand(vec2(v_position_ws.x + offsets[i].x, v_position_ws.y + offsets[i].y))*1.75f + 1.25f); vec2 moments = texture(shadowMap[light_index], sampleDir).rg; // If the shadow map is sampled outside of its bounds, add 1.0 if (moments.x == 1.0 && moments.y == 1.0) { shadow += 1.0; continue; } // Surface is fully lit if the current fragment is before the light occluder if (distance <= moments.x) { shadow += 1.0; continue; } // Calculate VSM for this sample float p = step(distance, moments.x); float variance = max(moments.y - (moments.x * moments.x), min_variance); float d = distance - moments.x; float p_max = linstep(variance_transition, 1.0, variance / (variance + d*d)); shadow += min(max(p, p_max), 1.0); } // Average the results return shadow / 9.0; } float shadow_csm(float distance, vec3 lightDir, int light_index, float shadow_bias, float normal_bias) { // Determine which cascade to use int cascade_index = -1; int min_cascades_range = light_index * NUM_SHADOW_CASCADES; int max_cascades_range = min_cascades_range + NUM_SHADOW_CASCADES; for (int i = min_cascades_range; i < max_cascades_range; i++) { if (distance < u_cascade_distances[i]) { cascade_index = i; break; } } if (cascade_index == -1) { cascade_index = max_cascades_range - 1; } light_t light = u_lights[light_index]; int matrix_index = cascade_index - min_cascades_range; vec4 fragPosLightSpace = light.shadow_matrix[matrix_index] * vec4(v_position_ws, 1.0); // 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(normal_bias * (1.0 - dot(normal, lightDir)), shadow_bias); bias *= 1 / (u_cascade_distances[cascade_index] * bias_modifier[matrix_index]); // CSM float shadow = 0.0; vec2 texelSize = 1.0 / textureSize(shadowMap2D[cascade_index], 0); #if 1 for(int x = -3; x <= 3; ++x) { for(int y = -3; y <= 3; ++y) { float csmDepth = texture(shadowMap2D[cascade_index], projCoords.xy + vec2(x, y) * texelSize * (rand(vec2(projCoords.x + x, projCoords.y + y))*0.75f + 0.25f)).r; shadow += currentDepth - bias > csmDepth ? 1.0 : 0.0; } } shadow /= 36.0; #else for(int x = -1; x <= 1; ++x) { for(int y = -1; y <= 1; ++y) { float csmDepth = texture(shadowMap2D[cascade_index], projCoords.xy + vec2(x, y) * texelSize * (rand(vec2(projCoords.x + x, projCoords.y + y))*0.75f + 0.25f)).r; shadow += currentDepth - bias > csmDepth ? 1.0 : 0.0; } } shadow /= 9.0; #endif return 1.0 - shadow; } vec4 shadowmap(int idx, in vec4 peye, in vec4 neye) { vec3 fragment = vec3(peye); float shadowFactor = 1.0; light_t light = u_lights[idx]; if (light.processed_shadows) { if (light.type == LIGHT_DIRECTIONAL) { shadowFactor = shadow_csm(-peye.z, light.dir, idx, light.shadow_bias, light.normal_bias); } else if (light.type == LIGHT_POINT || light.type == LIGHT_SPOT) { vec3 light_pos = (view * vec4(light.pos, 1.0)).xyz; vec3 dir = light_pos - fragment; vec4 sc = inv_view * vec4(dir, 0.0); shadowFactor = shadow_vsm(length(dir), -sc.xyz, idx, light.min_variance, light.variance_transition); } } return vec4(vec3(shadowFactor), 1.0); } vec4 shadowing(int idx) { if (u_shadow_receiver) { return shadowmap(idx, vpeye, vneye); } else { return vec4(1.0); } } #endif