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

95 lines
2.5 KiB
GLSL

#version 330 core
const float DIELECTRIC_SPECULAR = 0.04;
// math.glsl
float M_PI();
float M_1_PI();
float sqr(float x);
float pow5(float x);
/*
* Fresnel, Schlick's approximation.
*/
vec3 F_Schlick(float VdotH, vec3 f0)
{
return f0 + (vec3(1.0) - f0) * pow5(clamp(1.0 - VdotH, 0.0, 1.0));
}
/*
* Fresnel, Schlick's approximation, monochromatic.
*/
float F_Schlick(float VdotH, float f0)
{
return f0 + (1.0 - f0) * pow5(clamp(1.0 - VdotH, 0.0, 1.0));
}
/*
* Normal distribution function, Trowbridge-Reitz/GGX microfacet distribution.
*/
float D_GGX(float NdotH, float a2)
{
float f = (NdotH * a2 - NdotH) * NdotH + 1.0;
return M_PI() * f * f;
}
/*
* Geometric attenuation, Smith-GGX formulation.
*/
float G1_Smith_GGX(float NdotX, float a2)
{
return NdotX + sqrt(NdotX * (NdotX - NdotX * a2) + a2);
}
/*
* Get the Fresnel reflectance at 0 degrees (light hitting the surface
* perpendicularly) from PBR parameters.
*/
vec3 f0_from_pbr(vec3 base_color, float metallic)
{
return mix(vec3(DIELECTRIC_SPECULAR), base_color, metallic);
}
/*
* Evaluate the color of a standard PBR surface illuminated by an analytical
* light source. The evaluated BSDF consists of a specular lobe and a diffuse
* lobe. The specular lobe is the Cook-Torrance microfacet model, and the
* diffuse lobe is a simple Lambertian.
*
* Note that the calculations here do not match the literature. Brian Karis
* approach of refactoring by NdotX/NdotX has been used to optimize the code.
*/
vec3 surface_eval_analytical(
// Material
vec3 base_color, float metallic, float roughness, vec3 f0,
// Light
vec3 light_intensity, float occlusion,
// Vectors
vec3 N, vec3 L, vec3 V)
{
float NdotL = dot(N, L);
// Skip fragments that are completely occluded or that are not facing the light
if (occlusion <= 0.0 || NdotL <= 0.0)
return vec3(0.0);
NdotL = max(NdotL, 1e-4);
float a = max(sqr(roughness), 0.045);
float a2 = sqr(a);
vec3 H = normalize(L + V);
float NdotV = max(dot(N, V), 1e-4);
float NdotH = max(dot(N, H), 1e-4);
float VdotH = max(dot(V, H), 1e-4);
vec3 c_diff = mix(base_color * (1.0 - DIELECTRIC_SPECULAR), vec3(0.0), metallic);
vec3 F = F_Schlick(VdotH, f0);
float G = G1_Smith_GGX(NdotV, a2) * G1_Smith_GGX(NdotL, a2);
float D = D_GGX(NdotH, a2);
vec3 f_specular = (F * a2) / max(D * G, 1e-5);
vec3 f_diffuse = (vec3(1.0) - F) * c_diff * M_1_PI();
vec3 bsdf = f_diffuse + f_specular;
return bsdf * light_intensity * occlusion * NdotL;
}