2024-08-29 15:32:34 +00:00
|
|
|
in vec4 vpeye;
|
|
|
|
in vec4 vneye;
|
|
|
|
uniform bool u_shadow_receiver;
|
2024-08-30 01:46:46 +00:00
|
|
|
uniform float u_cascade_splits[NUM_SHADOW_CASCADES];
|
|
|
|
uniform float u_cascade_distances[NUM_SHADOW_CASCADES];
|
2024-08-29 15:32:34 +00:00
|
|
|
uniform samplerCube shadowMap[MAX_LIGHTS];
|
2024-08-30 01:46:46 +00:00
|
|
|
uniform sampler2D shadowMap2D[MAX_LIGHTS * NUM_SHADOW_CASCADES];
|
2024-08-24 17:32:25 +00:00
|
|
|
|
2024-08-29 15:32:34 +00:00
|
|
|
//// From http://fabiensanglard.net/shadowmappingVSM/index.php
|
2024-08-29 18:46:30 +00:00
|
|
|
float shadow_vsm(float distance, vec3 dir, int light_index) {
|
2024-08-29 15:32:34 +00:00
|
|
|
distance = distance/20;
|
|
|
|
vec2 moments = texture(shadowMap[light_index], dir).rg;
|
2024-08-24 13:24:44 +00:00
|
|
|
|
2024-08-29 15:32:34 +00:00
|
|
|
// Surface is fully lit. as the current fragment is before the light occluder
|
|
|
|
if (distance <= moments.x) {
|
|
|
|
return 1.0;
|
|
|
|
}
|
2024-08-24 13:24:44 +00:00
|
|
|
|
2024-08-29 15:32:34 +00:00
|
|
|
// 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);
|
2024-08-30 01:46:46 +00:00
|
|
|
|
2024-08-29 15:32:34 +00:00
|
|
|
return p_max;
|
|
|
|
}
|
|
|
|
|
2024-08-30 01:46:46 +00:00
|
|
|
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;
|
|
|
|
|
2024-08-29 18:46:30 +00:00
|
|
|
// Perform perspective divide
|
|
|
|
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
|
|
|
|
|
|
|
// Transform to [0,1] range
|
|
|
|
projCoords = projCoords * 0.5 + 0.5;
|
2024-08-30 01:46:46 +00:00
|
|
|
|
2024-08-29 18:46:30 +00:00
|
|
|
float currentDepth = projCoords.z;
|
2024-08-30 01:46:46 +00:00
|
|
|
|
|
|
|
if (currentDepth > 1.0) {
|
|
|
|
return 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate bias
|
2024-08-29 18:46:30 +00:00
|
|
|
vec3 normal = normalize(vneye.xyz);
|
2024-08-30 01:46:46 +00:00
|
|
|
float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
|
2024-08-30 08:19:57 +00:00
|
|
|
const float bias_modifier = 0.45;
|
2024-08-30 01:46:46 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-08-29 18:46:30 +00:00
|
|
|
// PCF
|
|
|
|
float shadow = 0.0;
|
2024-08-30 01:46:46 +00:00
|
|
|
vec2 texelSize = 1.0 / textureSize(shadowMap2D[index], 0);
|
2024-08-30 08:19:57 +00:00
|
|
|
for(int x = -3; x <= 3; ++x)
|
2024-08-29 18:46:30 +00:00
|
|
|
{
|
2024-08-30 08:19:57 +00:00
|
|
|
for(int y = -3; y <= 3; ++y)
|
2024-08-29 18:46:30 +00:00
|
|
|
{
|
2024-08-30 01:46:46 +00:00
|
|
|
float pcfDepth = texture(shadowMap2D[index], projCoords.xy + vec2(x, y) * texelSize).r;
|
2024-08-30 08:19:57 +00:00
|
|
|
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
2024-08-29 18:46:30 +00:00
|
|
|
}
|
|
|
|
}
|
2024-08-30 08:19:57 +00:00
|
|
|
shadow /= 36.0;
|
2024-08-30 01:46:46 +00:00
|
|
|
|
2024-08-29 18:46:30 +00:00
|
|
|
return 1.0 - shadow;
|
|
|
|
}
|
|
|
|
|
2024-08-29 15:32:34 +00:00
|
|
|
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) {
|
2024-08-29 18:46:30 +00:00
|
|
|
total_casters++;
|
2024-08-30 01:46:46 +00:00
|
|
|
factor += shadow_pcf(-peye.z, light.dir, i);
|
|
|
|
} else if (light.type == LIGHT_POINT || light.type == LIGHT_SPOT) {
|
2024-08-29 18:46:30 +00:00
|
|
|
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);
|
2024-08-24 17:32:25 +00:00
|
|
|
}
|
2024-08-29 15:32:34 +00:00
|
|
|
shadowFactor += factor;
|
|
|
|
}
|
|
|
|
|
2024-08-29 18:46:30 +00:00
|
|
|
if (total_casters == 0) {
|
2024-08-29 15:32:34 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|