2024-08-30 14:01:46 +00:00
|
|
|
#ifndef SHADOWMAP_GLSL
|
|
|
|
#define SHADOWMAP_GLSL
|
|
|
|
|
2024-08-30 10:19:50 +00:00
|
|
|
#include "utils.glsl"
|
|
|
|
|
2024-08-29 15:32:34 +00:00
|
|
|
uniform bool u_shadow_receiver;
|
2024-08-30 12:25:47 +00:00
|
|
|
uniform float u_cascade_distances[MAX_LIGHTS * 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-09-02 11:10:35 +00:00
|
|
|
uniform sampler3D shadow_offsets;
|
|
|
|
uniform int shadow_filter_size;
|
|
|
|
uniform int shadow_window_size;
|
|
|
|
uniform float shadow_offset_radius;
|
2024-08-24 17:32:25 +00:00
|
|
|
|
2024-08-30 21:23:07 +00:00
|
|
|
// const float bias_modifier[NUM_SHADOW_CASCADES] = float[NUM_SHADOW_CASCADES](0.95, 0.35, 0.20, 0.1, 0.1, 0.1);
|
2024-09-02 11:10:35 +00:00
|
|
|
const float bias_modifier[NUM_SHADOW_CASCADES] = float[NUM_SHADOW_CASCADES](1.0, 6.0, 9.0, 16.0);
|
|
|
|
// const float bias_modifier[NUM_SHADOW_CASCADES] = float[NUM_SHADOW_CASCADES](0.95, 0.35, 0.20, 0.15);
|
2024-08-30 10:19:50 +00:00
|
|
|
|
2024-08-29 15:32:34 +00:00
|
|
|
//// From http://fabiensanglard.net/shadowmappingVSM/index.php
|
2024-08-31 10:17:59 +00:00
|
|
|
float shadow_vsm(float distance, vec3 dir, int light_index, float min_variance, float variance_transition) {
|
2024-08-30 14:01:46 +00:00
|
|
|
distance = distance/200;
|
2024-08-24 13:24:44 +00:00
|
|
|
|
2024-08-30 21:23:07 +00:00
|
|
|
// Define offsets for 3x3 PCF
|
|
|
|
vec3 offsets[9] = vec3[9](
|
2024-08-30 19:00:04 +00:00
|
|
|
vec3(-1, -1, 0) * 0.01,
|
2024-08-30 21:23:07 +00:00
|
|
|
vec3( 0, -1, 0) * 0.01,
|
2024-08-30 19:00:04 +00:00
|
|
|
vec3( 1, -1, 0) * 0.01,
|
2024-08-30 21:23:07 +00:00
|
|
|
vec3(-1, 0, 0) * 0.01,
|
|
|
|
vec3( 0, 0, 0) * 0.01,
|
|
|
|
vec3( 1, 0, 0) * 0.01,
|
2024-08-30 19:00:04 +00:00
|
|
|
vec3(-1, 1, 0) * 0.01,
|
2024-08-30 21:23:07 +00:00
|
|
|
vec3( 0, 1, 0) * 0.01,
|
2024-08-30 19:00:04 +00:00
|
|
|
vec3( 1, 1, 0) * 0.01
|
|
|
|
);
|
2024-08-24 13:24:44 +00:00
|
|
|
|
2024-08-30 19:00:04 +00:00
|
|
|
float shadow = 0.0;
|
2024-09-02 11:10:35 +00:00
|
|
|
|
2024-08-30 21:23:07 +00:00
|
|
|
// Perform 3x3 PCF
|
|
|
|
for (int i = 0; i < 9; i++) {
|
2024-08-30 19:00:04 +00:00
|
|
|
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
|
2024-08-30 21:23:07 +00:00
|
|
|
float p = step(distance, moments.x);
|
2024-08-31 10:17:59 +00:00
|
|
|
float variance = max(moments.y - (moments.x * moments.x), min_variance);
|
2024-08-30 19:00:04 +00:00
|
|
|
float d = distance - moments.x;
|
2024-08-31 10:17:59 +00:00
|
|
|
float p_max = linstep(variance_transition, 1.0, variance / (variance + d*d));
|
2024-08-30 19:00:04 +00:00
|
|
|
|
2024-08-30 21:23:07 +00:00
|
|
|
shadow += min(max(p, p_max), 1.0);
|
2024-08-30 19:00:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Average the results
|
2024-08-30 21:23:07 +00:00
|
|
|
return shadow / 9.0;
|
2024-08-29 15:32:34 +00:00
|
|
|
}
|
|
|
|
|
2024-08-31 10:17:59 +00:00
|
|
|
float shadow_csm(float distance, vec3 lightDir, int light_index, float shadow_bias, float normal_bias) {
|
2024-08-30 01:46:46 +00:00
|
|
|
// Determine which cascade to use
|
|
|
|
int cascade_index = -1;
|
2024-08-30 12:25:47 +00:00
|
|
|
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++) {
|
2024-08-30 01:46:46 +00:00
|
|
|
if (distance < u_cascade_distances[i]) {
|
|
|
|
cascade_index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cascade_index == -1) {
|
2024-08-30 12:25:47 +00:00
|
|
|
cascade_index = max_cascades_range - 1;
|
2024-08-30 01:46:46 +00:00
|
|
|
}
|
|
|
|
|
2024-08-30 14:01:46 +00:00
|
|
|
light_t light = u_lights[light_index];
|
2024-08-30 12:25:47 +00:00
|
|
|
int matrix_index = cascade_index - min_cascades_range;
|
|
|
|
|
2024-08-30 14:01:46 +00:00
|
|
|
vec4 fragPosLightSpace = light.shadow_matrix[matrix_index] * vec4(v_position_ws, 1.0);
|
2024-08-30 01:46:46 +00:00
|
|
|
|
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-09-02 11:10:35 +00:00
|
|
|
vec4 sc = vec4(projCoords, 1.0);
|
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-09-02 11:10:35 +00:00
|
|
|
float bias = max(normal_bias * bias_modifier[matrix_index] * (1.0 - dot(normal, lightDir)), shadow_bias);
|
|
|
|
bias *= 1 / (u_cascade_distances[cascade_index]);
|
2024-08-30 10:19:50 +00:00
|
|
|
|
2024-08-30 19:00:04 +00:00
|
|
|
// CSM
|
2024-08-29 18:46:30 +00:00
|
|
|
float shadow = 0.0;
|
2024-08-30 12:25:47 +00:00
|
|
|
vec2 texelSize = 1.0 / textureSize(shadowMap2D[cascade_index], 0);
|
2024-08-30 10:19:50 +00:00
|
|
|
|
2024-09-02 11:10:35 +00:00
|
|
|
// Get the offset coordinates
|
|
|
|
ivec3 ofs_coord = ivec3(0);
|
|
|
|
vec2 ofs = mod(gl_FragCoord.xy, vec2(shadow_window_size));
|
|
|
|
ofs_coord.yz = ivec2(ofs);
|
|
|
|
float ofs_sum = 0.0;
|
|
|
|
int samples_div2 = int(shadow_filter_size * shadow_filter_size / 2.0);
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
ofs_coord.x = i;
|
|
|
|
vec4 offsets = texelFetch(shadow_offsets, ofs_coord, 0) * shadow_offset_radius;
|
|
|
|
sc.xy = projCoords.xy + offsets.rg * texelSize;
|
|
|
|
float csmDepth = texture(shadowMap2D[cascade_index], sc.xy).r;
|
|
|
|
ofs_sum += currentDepth - bias > csmDepth ? 1.0 : 0.0;
|
|
|
|
|
|
|
|
sc.xy = projCoords.xy + offsets.ba * texelSize;
|
|
|
|
csmDepth = texture(shadowMap2D[cascade_index], sc.xy).r;
|
|
|
|
ofs_sum += currentDepth - bias > csmDepth ? 1.0 : 0.0;
|
2024-08-29 18:46:30 +00:00
|
|
|
}
|
2024-09-02 11:10:35 +00:00
|
|
|
|
|
|
|
float shadow_sum = ofs_sum / 8.0;
|
|
|
|
|
|
|
|
if (shadow_sum != 0.0 && shadow_sum != 1.0) {
|
|
|
|
for (int i = 4; i < samples_div2; i++) {
|
|
|
|
ofs_coord.x = i;
|
|
|
|
vec4 offsets = texelFetch(shadow_offsets, ofs_coord, 0) * shadow_offset_radius;
|
|
|
|
sc.xy = projCoords.xy + offsets.rg * texelSize;
|
|
|
|
float csmDepth = texture(shadowMap2D[cascade_index], sc.xy).r;
|
|
|
|
ofs_sum += currentDepth - bias > csmDepth ? 1.0 : 0.0;
|
|
|
|
|
|
|
|
sc.xy = projCoords.xy + offsets.ba * texelSize;
|
|
|
|
csmDepth = texture(shadowMap2D[cascade_index], sc.xy).r;
|
|
|
|
ofs_sum += currentDepth - bias > csmDepth ? 1.0 : 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
shadow_sum = ofs_sum / (samples_div2 * 2.0);
|
2024-08-30 10:19:50 +00:00
|
|
|
}
|
2024-09-02 11:10:35 +00:00
|
|
|
return 1.0 - shadow_sum;
|
2024-08-29 18:46:30 +00:00
|
|
|
}
|
|
|
|
|
2024-08-30 14:01:46 +00:00
|
|
|
vec4 shadowmap(int idx, in vec4 peye, in vec4 neye) {
|
2024-08-29 15:32:34 +00:00
|
|
|
vec3 fragment = vec3(peye);
|
2024-08-30 12:25:47 +00:00
|
|
|
float shadowFactor = 1.0;
|
2024-08-30 14:01:46 +00:00
|
|
|
light_t light = u_lights[idx];
|
|
|
|
|
|
|
|
if (light.processed_shadows) {
|
|
|
|
if (light.type == LIGHT_DIRECTIONAL) {
|
2024-08-31 10:17:59 +00:00
|
|
|
shadowFactor = shadow_csm(-peye.z, light.dir, idx, light.shadow_bias, light.normal_bias);
|
2024-08-30 14:01:46 +00:00
|
|
|
} 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);
|
2024-08-31 10:17:59 +00:00
|
|
|
shadowFactor = shadow_vsm(length(dir), -sc.xyz, idx, light.min_variance, light.variance_transition);
|
2024-08-24 17:32:25 +00:00
|
|
|
}
|
2024-08-29 15:32:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return vec4(vec3(shadowFactor), 1.0);
|
|
|
|
}
|
|
|
|
|
2024-08-30 14:01:46 +00:00
|
|
|
vec4 shadowing(int idx) {
|
2024-08-29 15:32:34 +00:00
|
|
|
if (u_shadow_receiver) {
|
2024-08-30 14:01:46 +00:00
|
|
|
return shadowmap(idx, vpeye, vneye);
|
2024-08-29 15:32:34 +00:00
|
|
|
} else {
|
|
|
|
return vec4(1.0);
|
|
|
|
}
|
|
|
|
}
|
2024-08-30 14:01:46 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|