| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- /* light.glsl -- Contains everything you need to manage lights
- *
- * Copyright (c) 2025-2026 Le Juez Victor
- *
- * This software is provided 'as-is', without any express or implied warranty.
- * For conditions of distribution and use, see accompanying LICENSE file.
- */
- /* === Includes === */
- #include "../math.glsl"
- #include "../pbr.glsl"
- /* === Macros === */
- #if defined(NUM_FORWARD_LIGHTS)
- # define SAMPLE_SHADOW_PROJ(name) float name(int lightIndex, vec4 Pls, float cNdotL, mat2 diskRot)
- # define SAMPLE_SHADOW_OMNI(name) float name(int lightIndex, vec3 Pws, float cNdotL, mat2 diskRot)
- # define LIGHT uLights[lightIndex]
- #else
- # define SAMPLE_SHADOW_PROJ(name) float name(vec3 Pws, float cNdotL, mat2 diskRot)
- # define SAMPLE_SHADOW_OMNI(name) float name(vec3 Pws, float cNdotL, mat2 diskRot)
- # define LIGHT uLight
- #endif
- /* === Constants === */
- // Should be defined in client side
- // #define NUM_FORWARD_LIGHTS 8
- #define LIGHT_DIR 0
- #define LIGHT_SPOT 1
- #define LIGHT_OMNI 2
- #define SHADOW_SAMPLES 8
- const vec2 VOGEL_DISK[8] = vec2[8](
- vec2(0.250000, 0.000000),
- vec2(-0.319290, 0.292496),
- vec2(0.048872, -0.556877),
- vec2(0.402444, 0.524918),
- vec2(-0.738535, -0.130636),
- vec2(0.699605, -0.445031),
- vec2(-0.234004, 0.870484),
- vec2(-0.446271, -0.859268)
- );
- /* === Structures === */
- struct Light {
- mat4 viewProj;
- vec3 color;
- vec3 position;
- vec3 direction;
- float specular;
- float energy;
- float range;
- float near;
- float far;
- float attenuation;
- float innerCutOff;
- float outerCutOff;
- float shadowSoftness;
- float shadowDepthBias;
- float shadowSlopeBias;
- int shadowLayer; //< less than zero if no shadows
- int type;
- };
- /* === Uniform Block === */
- #ifdef NUM_FORWARD_LIGHTS
- layout(std140) uniform LightArrayBlock {
- Light uLights[NUM_FORWARD_LIGHTS];
- int uNumLights;
- };
- #else
- layout(std140) uniform LightBlock {
- Light uLight;
- };
- #endif
- /* === Functions === */
- vec3 L_Diffuse(float cLdotH, float cNdotV, float cNdotL, float roughness)
- {
- float FD90_minus_1 = 2.0 * cLdotH * cLdotH * roughness - 0.5;
- float FdV = 1.0 + FD90_minus_1 * PBR_SchlickFresnel(cNdotV);
- float FdL = 1.0 + FD90_minus_1 * PBR_SchlickFresnel(cNdotL);
- return vec3(M_INV_PI * (FdV * FdL * cNdotL)); // Diffuse BRDF (Burley)
- }
- vec3 L_Specular(vec3 F0, float cLdotH, float cNdotH, float cNdotV, float cNdotL, float roughness)
- {
- roughness = max(roughness, 0.02); // >0.01 to avoid FP16 overflow after GGX distribution
- float alphaGGX = roughness * roughness;
- float D = PBR_DistributionGGX(cNdotH, alphaGGX);
- float G = PBR_GeometryGGX(cNdotL, cNdotV, alphaGGX);
- float cLdotH5 = PBR_SchlickFresnel(cLdotH);
- float F90 = clamp(50.0 * F0.g, 0.0, 1.0);
- vec3 F = F0 + (F90 - F0) * cLdotH5;
- return cNdotL * D * F * G; // Specular BRDF (Schlick GGX)
- }
- #ifdef L_SHADOW_IMPL
- mat2 L_ShadowDebandingMatrix(vec2 fragCoord)
- {
- float r = M_TAU * M_HashIGN(fragCoord);
- float sr = sin(r), cr = cos(r);
- return mat2(vec2(cr, -sr), vec2(sr, cr));
- }
- SAMPLE_SHADOW_PROJ(L_SampleShadowDir)
- {
- #if !defined(NUM_FORWARD_LIGHTS)
- vec4 Pls = LIGHT.viewProj * vec4(Pws, 1.0);
- #endif
- vec3 projCoords = Pls.xyz / Pls.w * 0.5 + 0.5;
- float bias = LIGHT.shadowDepthBias + LIGHT.shadowSlopeBias * (1.0 - cNdotL);
- float compareDepth = projCoords.z - bias;
- float shadow = 0.0;
- for (int i = 0; i < SHADOW_SAMPLES; ++i) {
- vec2 offset = diskRot * VOGEL_DISK[i] * LIGHT.shadowSoftness;
- shadow += texture(uShadowDirTex, vec4(projCoords.xy + offset, LIGHT.shadowLayer, compareDepth));
- }
- shadow /= float(SHADOW_SAMPLES);
- vec3 distToBorder = min(projCoords, 1.0 - projCoords);
- float edgeFade = smoothstep(0.0, 0.15, min(distToBorder.x, min(distToBorder.y, distToBorder.z)));
- shadow = mix(1.0, shadow, edgeFade);
- return shadow;
- }
- SAMPLE_SHADOW_PROJ(L_SampleShadowSpot)
- {
- #if !defined(NUM_FORWARD_LIGHTS)
- vec4 Pls = LIGHT.viewProj * vec4(Pws, 1.0);
- #endif
- vec3 projCoords = Pls.xyz / Pls.w * 0.5 + 0.5;
- float bias = LIGHT.shadowDepthBias + LIGHT.shadowSlopeBias * (1.0 - cNdotL);
- float compareDepth = projCoords.z - bias;
- float shadow = 0.0;
- for (int i = 0; i < SHADOW_SAMPLES; ++i) {
- vec2 offset = diskRot * VOGEL_DISK[i] * LIGHT.shadowSoftness;
- shadow += texture(uShadowSpotTex, vec4(projCoords.xy + offset, LIGHT.shadowLayer, compareDepth));
- }
- return shadow / float(SHADOW_SAMPLES);
- }
- SAMPLE_SHADOW_OMNI(L_SampleShadowOmni)
- {
- vec3 lightToFrag = Pws - LIGHT.position;
- float currentDepth = length(lightToFrag);
- float bias = LIGHT.shadowDepthBias + LIGHT.shadowSlopeBias * (1.0 - cNdotL);
- float compareDepth = (currentDepth - bias) / LIGHT.far;
- mat3 OBN = M_OrthonormalBasis(lightToFrag / currentDepth);
- float shadow = 0.0;
- for (int i = 0; i < SHADOW_SAMPLES; ++i) {
- vec2 diskOffset = diskRot * VOGEL_DISK[i] * LIGHT.shadowSoftness;
- shadow += texture(uShadowOmniTex, vec4(OBN * vec3(diskOffset.xy, 1.0), LIGHT.shadowLayer), compareDepth);
- }
- return shadow / float(SHADOW_SAMPLES);
- }
- #endif // L_SHADOW_IMPL
- /* === Undefs === */
- #undef SAMPLE_SHADOW_PROJ
- #undef SAMPLE_SHADOW_OMNI
- #undef LIGHT
|