#version 460 core out vec4 FragColor; in VS_OUT { vec3 FragPos; vec3 Normal; vec2 TexCoords; } fs_in; uniform sampler2D diffuseTexture; uniform sampler2DArray shadowMap; uniform vec3 lightDir; uniform vec3 viewPos; uniform float farPlane; uniform mat4 view; layout (std140, binding = 0) uniform LightSpaceMatrices { mat4 lightSpaceMatrices[16]; }; uniform float cascadePlaneDistances[16]; uniform int cascadeCount; // number of frusta - 1 float ShadowCalculation(vec3 fragPosWorldSpace) { // select cascade layer vec4 fragPosViewSpace = view * vec4(fragPosWorldSpace, 1.0); float depthValue = abs(fragPosViewSpace.z); int layer = -1; for (int i = 0; i < cascadeCount; ++i) { if (depthValue < cascadePlaneDistances[i]) { layer = i; break; } } if (layer == -1) { layer = cascadeCount; } vec4 fragPosLightSpace = lightSpaceMatrices[layer] * vec4(fragPosWorldSpace, 1.0); // perform perspective divide vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; // transform to [0,1] range projCoords = projCoords * 0.5 + 0.5; // get depth of current fragment from light's perspective float currentDepth = projCoords.z; if (currentDepth > 1.0) { return 0.0; } // calculate bias (based on depth map resolution and slope) vec3 normal = normalize(fs_in.Normal); float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); if (layer == cascadeCount) { bias *= 1 / (farPlane * 0.5f); } else { bias *= 1 / (cascadePlaneDistances[layer] * 0.5f); } // PCF float shadow = 0.0; vec2 texelSize = 1.0 / vec2(textureSize(shadowMap, 0)); for(int x = -1; x <= 1; ++x) { for(int y = -1; y <= 1; ++y) { float pcfDepth = texture(shadowMap, vec3(projCoords.xy + vec2(x, y) * texelSize, layer)).r; shadow += (currentDepth - bias) > pcfDepth ? 1.0 : 0.0; } } shadow /= 9.0; // keep the shadow at 0.0 when outside the far_plane region of the light's frustum. if(projCoords.z > 1.0) { shadow = 0.0; } return shadow; } void main() { vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb; vec3 normal = normalize(fs_in.Normal); vec3 lightColor = vec3(0.3); // ambient vec3 ambient = 0.3 * color; // diffuse float diff = max(dot(lightDir, normal), 0.0); vec3 diffuse = diff * lightColor; // specular vec3 viewDir = normalize(viewPos - fs_in.FragPos); vec3 reflectDir = reflect(-lightDir, normal); float spec = 0.0; vec3 halfwayDir = normalize(lightDir + viewDir); spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0); vec3 specular = spec * lightColor; // calculate shadow float shadow = ShadowCalculation(fs_in.FragPos); vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color; FragColor = vec4(lighting, 1.0); }