diff --git a/libs/ibl/src/CubemapIBL.cpp b/libs/ibl/src/CubemapIBL.cpp index f6ce8ab26..5d2c71f10 100644 --- a/libs/ibl/src/CubemapIBL.cpp +++ b/libs/ibl/src/CubemapIBL.cpp @@ -167,7 +167,17 @@ static float Fresnel(float f0, float f90, float LoH) { return f0 * (1 - Fc) + f90 * Fc; } -static float Visibility(float NoV, float NoL, float a) { +#define Visibility VisibilitySmith + +static float VisibilitySchlick(float NoV, float NoL, float a) { + // Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering" + float k = a * 0.5; + float Vis_SchlickV = NoV * (1 - k) + k; + float Vis_SchlickL = NoL * (1 - k) + k; + return 0.25 / (Vis_SchlickV * Vis_SchlickL); +} + +static float VisibilitySmith(float NoV, float NoL, float a) { // Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs" // Height-correlated GGX const float a2 = a * a; @@ -181,6 +191,17 @@ static float UTILS_UNUSED VisibilityAshikhmin(float NoV, float NoL, float /*a*/) return 1 / (4 * (NoL + NoV - NoL * NoV)); } +static float DiffuseDisney(float NoV, float NoL, float LoH, float roughness) { + // Lagarde and de Rousiers 2014, "Moving Frostbite to PBR" + const float energyBias = lerp(0.f, 0.5f, roughness); + const float energyFactor = lerp(1.0f, 1.0f / 1.51f, roughness); + const float fd90 = energyBias + 2.0f * LoH * LoH * roughness; + const float lightScatter = Fresnel(1.0f, fd90, NoL); + const float viewScatter = Fresnel(1.0f, fd90, NoV); + + return lightScatter * viewScatter * energyFactor; +} + /* * * Importance sampling GGX - Trowbridge-Reitz @@ -745,8 +766,8 @@ static float2 UTILS_UNUSED DFV_NoIS(float NoV, float roughness, size_t numSample * */ -static float2 DFV(float NoV, float linearRoughness, size_t numSamples) { - float2 r = 0; +static float3 DFV(float NoV, float linearRoughness, size_t numSamples) { + float3 r = 0; const float3 V(std::sqrt(1 - NoV * NoV), 0, NoV); for (size_t i = 0; i < numSamples; i++) { const float2 u = hammersley(uint32_t(i), 1.0f / numSamples); @@ -778,17 +799,28 @@ static float2 DFV(float NoV, float linearRoughness, size_t numSamples) { * Er() = f0 * DFV.x + f90 * DFV.y * */ - const float v = Visibility(NoV, NoL, linearRoughness) * NoL * (VoH / NoH); + const float v = Visibility(NoV, NoL, linearRoughness) * NoL * (4.0f * VoH / NoH); const float Fc = pow5(1 - VoH); r.x += v * (1.0f - Fc); r.y += v * Fc; } + + { + const float3 L = hemisphereCosSample(u); + const float3 H = normalize(V + L); + + const float NoL = saturate(L.z); + const float NoH = saturate(H.z); + const float VoH = saturate(dot(V, H)); + + r.z += DiffuseDisney(NoV, NoL, VoH, std::sqrt(linearRoughness)); + } } - return r * (4.0f / numSamples); + return r / numSamples; } -static float2 DFV_Multiscatter(float NoV, float linearRoughness, size_t numSamples) { - float2 r = 0; +static float3 DFV_Multiscatter(float NoV, float linearRoughness, size_t numSamples) { + float3 r = 0; const float3 V(std::sqrt(1 - NoV * NoV), 0, NoV); for (size_t i = 0; i < numSamples; i++) { const float2 u = hammersley(uint32_t(i), 1.0f / numSamples); @@ -798,7 +830,7 @@ static float2 DFV_Multiscatter(float NoV, float linearRoughness, size_t numSampl const float NoL = saturate(L.z); const float NoH = saturate(H.z); if (NoL > 0) { - const float v = Visibility(NoV, NoL, linearRoughness) * NoL * (VoH / NoH); + const float v = Visibility(NoV, NoL, linearRoughness) * NoL * (4.0f * VoH / NoH); const float Fc = pow5(1 - VoH); /* * Assuming f90 = 1 @@ -828,8 +860,19 @@ static float2 DFV_Multiscatter(float NoV, float linearRoughness, size_t numSampl r.x += v * Fc; r.y += v; } + + { + const float3 L = hemisphereCosSample(u); + const float3 H = normalize(V + L); + + const float NoL = saturate(L.z); + const float NoH = saturate(H.z); + const float VoH = saturate(dot(V, H)); + + r.z += DiffuseDisney(NoV, NoL, VoH, std::sqrt(linearRoughness)); + } } - return r * (4.0f / numSamples); + return r / numSamples; } static float UTILS_UNUSED DFV_LazanyiTerm(float NoV, float linearRoughness, size_t numSamples) { @@ -1025,7 +1068,7 @@ void CubemapIBL::DFG(JobSystem& js, Image& dst, bool multiscatter, bool cloth) { for (size_t x = 0; x < height; x++, data++) { // const float NoV = float(x) / (width-1); const float NoV = saturate((x + 0.5f) / width); - float3 r = { dfvFunction(NoV, linear_roughness, 1024), 0 }; + float3 r = { dfvFunction(NoV, linear_roughness, 1024) }; if (cloth) { r.b = float(DFV_Charlie_Uniform(NoV, linear_roughness, 4096)); }