Files
Destrum/destrum/assets_src/shaders/lighting.glsl
T

97 lines
2.9 KiB
GLSL

#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