#version 450 const int MAX_LIGHTS = 4; struct Light { vec4 direction; vec4 color; }; layout(location = 0) in vec2 fragUV; layout(location = 1) in vec3 fragNormal; layout(location = 2) in vec3 fragWorldPos; layout(location = 3) in vec4 fragLightPos; layout(location = 0) out vec4 outColor; layout(set = 0, binding = 0) uniform UniformBufferObject { mat4 view; mat4 proj; mat4 lightSpace; vec4 ambientColor; Light lights[MAX_LIGHTS]; } ubo; layout(set = 1, binding = 0) uniform sampler2D texSampler; // 阴影深度图:手动对比(非 comparison sampler,逻辑清晰且免 format feature 依赖) layout(set = 2, binding = 0) uniform sampler2D shadowMap; // 主光(lights[0])阴影因子:出光视锥外/未被遮挡 = 1.0 float ShadowFactor(vec3 N, vec3 L) { vec3 proj = fragLightPos.xyz / fragLightPos.w; proj = proj * 0.5 + 0.5; // NDC [-1,1] -> UV [0,1] if (proj.x < 0.0 || proj.x > 1.0 || proj.y < 0.0 || proj.y > 1.0 || proj.z > 1.0) { return 1.0; // 光视锥外:不施加阴影(边界由 border color 兜底) } // 斜率缩放偏差,缓解 shadow acne float bias = max(0.004 * (1.0 - dot(N, L)), 0.0008); float closest = texture(shadowMap, proj.xy).r; return (proj.z - bias > closest) ? 0.25 : 1.0; } void main() { vec3 albedo = texture(texSampler, fragUV).rgb; vec3 N = normalize(fragNormal); vec3 result = ubo.ambientColor.rgb * albedo; for (int i = 0; i < MAX_LIGHTS; ++i) { vec3 L = normalize(-ubo.lights[i].direction.xyz); float diff = max(dot(N, L), 0.0); float shadow = (i == 0) ? ShadowFactor(N, L) : 1.0; result += ubo.lights[i].color.rgb * ubo.lights[i].color.a * diff * albedo * shadow; } outColor = vec4(result, 1.0); }