1
0
Fork 0
fgdata/Shaders/HDR/clustered.glsl
Fernando García Liñán 7b5255eac4 HDR: Safer divisions
2024-02-08 22:37:07 +01:00

163 lines
4.9 KiB
GLSL

#version 330 core
uniform sampler3D fg_Clusters;
uniform sampler2D fg_ClusteredIndices;
uniform sampler2D fg_ClusteredPointLights;
uniform sampler2D fg_ClusteredSpotLights;
uniform int fg_ClusteredMaxPointLights;
uniform int fg_ClusteredMaxSpotLights;
uniform int fg_ClusteredMaxLightIndices;
uniform int fg_ClusteredTileSize;
uniform int fg_ClusteredDepthSlices;
uniform float fg_ClusteredSliceScale;
uniform float fg_ClusteredSliceBias;
uniform int fg_ClusteredHorizontalTiles;
uniform int fg_ClusteredVerticalTiles;
// surface.glsl
vec3 surface_eval_analytical(
vec3 base_color, float metallic, float roughness, vec3 f0,
vec3 light_intensity, float occlusion,
vec3 N, vec3 L, vec3 V);
struct PointLight {
vec3 position;
vec3 color;
float intensity;
float range;
};
struct SpotLight {
vec3 position;
vec3 direction;
vec3 color;
float intensity;
float range;
float cos_cutoff;
float exponent;
};
PointLight unpackPointLight(int index)
{
float v = (float(index) + 0.5) / float(fg_ClusteredMaxPointLights);
PointLight light;
vec4 block;
block = texture(fg_ClusteredPointLights, vec2(0.25, v));
light.position = block.xyz;
light.range = block.w;
block = texture(fg_ClusteredPointLights, vec2(0.75, v));
light.color = block.xyz;
light.intensity = block.w;
return light;
}
SpotLight unpackSpotLight(int index)
{
float v = (float(index) + 0.5) / float(fg_ClusteredMaxSpotLights);
SpotLight light;
vec4 block;
block = texture(fg_ClusteredSpotLights, vec2(0.125, v));
light.position = block.xyz;
light.range = block.w;
block = texture(fg_ClusteredSpotLights, vec2(0.375, v));
light.direction = block.xyz;
light.cos_cutoff = block.w;
block = texture(fg_ClusteredSpotLights, vec2(0.625, v));
light.color = block.xyz;
light.intensity = block.w;
block = texture(fg_ClusteredSpotLights, vec2(0.875, v));
light.exponent = block.x;
return light;
}
int getIndex(int counter)
{
vec2 coords = vec2(mod(float(counter), float(fg_ClusteredMaxLightIndices)) + 0.5,
float(counter / fg_ClusteredMaxLightIndices) + 0.5);
// Normalize
coords /= vec2(fg_ClusteredMaxLightIndices);
return int(texture(fg_ClusteredIndices, coords).r);
}
float get_square_falloff_attenuation(vec3 to_light, float range)
{
float inv_range = 1.0 / max(range, 1e-5);
float dd = dot(to_light, to_light);
float factor = dd * inv_range * inv_range;
float smooth_factor = max(1.0 - factor * factor, 0.0);
return (smooth_factor * smooth_factor) / max(dd, 1e-5);
}
float get_spot_angle_attenuation(vec3 l, vec3 light_dir,
float cos_cutoff, float exponent)
{
float cd = dot(-l, light_dir);
if (cd < cos_cutoff)
return 0.0;
return pow(cd, exponent);
}
vec3 eval_scene_lights(vec3 base_color, float metallic, float roughness, vec3 f0,
vec3 P, vec3 N, vec3 V)
{
int slice = int(max(log2(-P.z) * fg_ClusteredSliceScale
+ fg_ClusteredSliceBias, 0.0));
vec3 cluster_coords = vec3(floor(gl_FragCoord.xy / fg_ClusteredTileSize),
slice) + vec3(0.5); // Pixel center
// Normalize
cluster_coords /= vec3(fg_ClusteredHorizontalTiles,
fg_ClusteredVerticalTiles,
fg_ClusteredDepthSlices);
vec3 cluster = texture(fg_Clusters, cluster_coords).rgb;
int light_index = int(cluster.r);
int point_count = int(cluster.g);
int spot_count = int(cluster.b);
vec3 color = vec3(0.0);
for (int i = 0; i < point_count; ++i) {
int index = getIndex(light_index++);
PointLight light = unpackPointLight(index);
vec3 to_light = light.position - P;
vec3 L = normalize(to_light);
float attenuation = get_square_falloff_attenuation(
to_light, light.range);
if (attenuation <= 0.0)
continue;
vec3 intensity = light.color * light.intensity * attenuation;
color += surface_eval_analytical(
base_color, metallic, roughness, f0,
intensity, 1.0,
N, L, V);
}
for (int i = 0; i < spot_count; ++i) {
int index = getIndex(light_index++);
SpotLight light = unpackSpotLight(index);
vec3 to_light = light.position - P;
vec3 L = normalize(to_light);
float attenuation = get_square_falloff_attenuation(
to_light, light.range);
attenuation *= get_spot_angle_attenuation(
L, light.direction, light.cos_cutoff, light.exponent);
if (attenuation <= 0.0)
continue;
vec3 intensity = light.color * light.intensity * attenuation;
color += surface_eval_analytical(
base_color, metallic, roughness, f0,
intensity, 1.0,
N, L, V);
}
return color;
}