#ifndef LIGHTING_GLSL #define LIGHTING_GLSL #include "lights.glsl" #define PI 3.14159265359 // GGX / Trowbridge-Reitz normal distribution float distributionGGX(float NoH, float roughness) { float a = roughness * roughness; float a2 = a * a; float d = NoH * NoH * (a2 - 1.0) + 1.0; return a2 / (PI * d * d); } // Smith-Schlick GGX, k remapped for direct lighting float geometrySmithGGX(float NoV, float NoL, float roughness) { float r = roughness + 1.0; float k = (r * r) / 8.0; float ggxV = NoV / (NoV * (1.0 - k) + k); float ggxL = NoL / (NoL * (1.0 - k) + k); return ggxV * ggxL; } vec3 fresnelSchlick(float VoH, vec3 f0) { return f0 + (1.0 - f0) * pow(1.0 - VoH, 5.0); } // Inverse square falloff, windowed so it reaches exactly 0 at the light range. // The range is also the sphere the light culling uses, so no light gets cut off at a tile edge. float calculateDistanceAttenuation(float distance, float range) { float window = clamp(1.0 - pow(distance / range, 4.0), 0.0, 1.0); return (window * window) / max(distance * distance, 0.0001); } float calculateSpotAttenuation(Light light, vec3 l) { float cd = dot(normalize(light.direction), -l); return smoothstep(light.spotOuterCos, light.spotInnerCos, cd); } vec3 calculateLight(Light light, vec3 fragPos, vec3 n, vec3 v, vec3 diffuseColor, float roughness, float metallic, vec3 f0) { vec3 l; float attenuation = 1.0; if (light.type == TYPE_DIRECTIONAL_LIGHT) { l = normalize(-light.direction); } else { vec3 toLight = light.position - fragPos; float distance = length(toLight); l = toLight / max(distance, 0.0001); attenuation = calculateDistanceAttenuation(distance, light.range); if (light.type == TYPE_SPOT_LIGHT) { attenuation *= calculateSpotAttenuation(light, l); } } float NoL = clamp(dot(n, l), 0.0, 1.0); if (NoL <= 0.0 || attenuation <= 0.0) { return vec3(0.0); } vec3 h = normalize(v + l); float NoV = clamp(dot(n, v), 0.0001, 1.0); float NoH = clamp(dot(n, h), 0.0, 1.0); float VoH = clamp(dot(v, h), 0.0, 1.0); float D = distributionGGX(NoH, roughness); float G = geometrySmithGGX(NoV, NoL, roughness); vec3 F = fresnelSchlick(VoH, f0); vec3 specular = (D * G * F) / (4.0 * NoV * NoL + 0.0001); vec3 kd = (vec3(1.0) - F) * (1.0 - metallic); vec3 diffuse = kd * diffuseColor / PI; return (diffuse + specular) * light.color * light.intensity * attenuation * NoL; } // blue -> green -> red, used to visualise how many lights a tile has vec3 lightHeatmapColor(uint lightCount) { float t = clamp(float(lightCount) / 16.0, 0.0, 1.0); if (t < 0.5) { return mix(vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0), t * 2.0); } return mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), (t - 0.5) * 2.0); } #endif // LIGHTING_GLSL