This repository has been archived on 2024-03-22. You can view files and clone it, but cannot push or open issues or pull requests.
DX11Starter/HelpersPBR.hlsli
2022-04-09 20:19:44 -04:00

104 lines
No EOL
3 KiB
HLSL

#ifndef __SHADER_HELPERS_PBR__
#define __SHADER_HELPERS_PBR__
// Lambert diffuse BRDF - Same as the basic lighting diffuse calculation!
// - NOTE: this function assumes the vectors are already NORMALIZED!
float DiffusePBR(float3 normal, float3 dirToLight)
{
return saturate(dot(normal, dirToLight));
}
// Calculates diffuse amount based on energy conservation
//
// diffuse - Diffuse amount
// specular - Specular color (including light color)
// metalness - surface metalness amount
//
// Metals should have an albedo of (0,0,0)...mostly
// See slide 65: http://blog.selfshadow.com/publications/s2014-shading-course/hoffman/s2014_pbs_physics_math_slides.pdf
float3 DiffuseEnergyConserve(float3 diffuse, float3 specular, float metalness)
{
return diffuse * ((1 - saturate(specular)) * (1 - metalness));
}
// GGX (Trowbridge-Reitz)
//
// a - Roughness
// h - Half vector
// n - Normal
//
// D(h, n) = a^2 / pi * ((n dot h)^2 * (a^2 - 1) + 1)^2
float SpecDistribution(float3 n, float3 h, float roughness)
{
// Pre-calculations
float NdotH = saturate(dot(n, h));
float NdotH2 = NdotH * NdotH;
float a = roughness * roughness;
float a2 = max(a * a, MIN_ROUGHNESS); // Applied after remap!
// ((n dot h)^2 * (a^2 - 1) + 1)
float denomToSquare = NdotH2 * (a2 - 1) + 1;
// Can go to zero if roughness is 0 and NdotH is 1; MIN_ROUGHNESS helps here
// Final value
return a2 / (PI * denomToSquare * denomToSquare);
}
// Fresnel term - Schlick approx.
//
// v - View vector
// h - Half vector
// f0 - Value when l = n (full specular color)
//
// F(v,h,f0) = f0 + (1-f0)(1 - (v dot h))^5
float3 Fresnel(float3 v, float3 h, float3 f0)
{
// Pre-calculations
float VdotH = saturate(dot(v, h));
// Final value
return f0 + (1 - f0) * pow(1 - VdotH, 5);
}
// Geometric Shadowing - Schlick-GGX (based on Schlick-Beckmann)
// - k is remapped to a / 2, roughness remapped to (r+1)/2
//
// n - Normal
// v - View vector
//
// G(l,v)
float GeometricShadowing(float3 n, float3 v, float roughness)
{
// End result of remapping:
float k = pow(roughness + 1, 2) / 8.0f;
float NdotV = saturate(dot(n, v));
// Final value
return NdotV / (NdotV * (1 - k) + k);
}
// Microfacet BRDF (Specular)
//
// f(l,v) = D(h)F(v,h)G(l,v,h) / 4(n dot l)(n dot v)
// - part of the denominator are canceled out by numerator (see below)
//
// D() - Spec Dist - Trowbridge-Reitz (GGX)
// F() - Fresnel - Schlick approx
// G() - Geometric Shadowing - Schlick-GGX
float3 MicrofacetBRDF(float3 n, float3 l, float3 v, float roughness, float3 specColor)
{
// Other vectors
float3 h = normalize(v + l);
// Grab various functions
float D = SpecDistribution(n, h, roughness);
float3 F = Fresnel(v, h, specColor);
float G = GeometricShadowing(n, v, roughness) * GeometricShadowing(n, l, roughness);
// Final formula
// Denominator dot products partially canceled by G()!
// See page 16: http://blog.selfshadow.com/publications/s2012-shading-course/hoffman/s2012_pbs_physics_math_notes.pdf
return (D * F * G) / (4 * max(dot(n, v), dot(n, l)));
}
#endif