Fix: EEVEE: Incorrect GGX BRDF pdf and BTDF evaluation #111591
|
@ -1036,15 +1036,15 @@ void EEVEE_lightbake_filter_glossy(EEVEE_ViewLayerData *sldata,
|
|||
break;
|
||||
case 2:
|
||||
pinfo->samples_len = 40.0f;
|
||||
bias = 2.0f;
|
||||
bias = 1.0f;
|
||||
break;
|
||||
case 3:
|
||||
pinfo->samples_len = 64.0f;
|
||||
bias = 2.0f;
|
||||
bias = 1.0f;
|
||||
break;
|
||||
default:
|
||||
pinfo->samples_len = 128.0f;
|
||||
bias = 2.0f;
|
||||
bias = 1.0f;
|
||||
break;
|
||||
}
|
||||
#else /* Constant Sample count (slow) */
|
||||
|
|
|
@ -99,12 +99,22 @@ vec3 F_brdf_multi_scatter(vec3 f0, vec3 f90, vec2 lut)
|
|||
}
|
||||
|
||||
/* GGX */
|
||||
float bxdf_ggx_D(float NH, float a2)
|
||||
{
|
||||
return a2 / (M_PI * sqr((a2 - 1.0) * (NH * NH) + 1.0));
|
||||
}
|
||||
|
||||
float D_ggx_opti(float NH, float a2)
|
||||
{
|
||||
float tmp = (NH * a2 - NH) * NH + 1.0;
|
||||
return M_PI * tmp * tmp; /* Doing RCP and mul a2 at the end */
|
||||
}
|
||||
|
||||
float bxdf_ggx_smith_G1(float NX, float a2)
|
||||
{
|
||||
return 2.0 / (1.0 + sqrt(1.0 + a2 * (1.0 / (NX * NX) - 1.0)));
|
||||
}
|
||||
|
||||
float G1_Smith_GGX_opti(float NX, float a2)
|
||||
{
|
||||
/* Using Brian Karis approach and refactoring by NX/NX
|
||||
|
@ -115,6 +125,7 @@ float G1_Smith_GGX_opti(float NX, float a2)
|
|||
/* return 2 / (1 + sqrt(1 + a2 * (1 - NX*NX) / (NX*NX) ) ); /* Reference function */
|
||||
}
|
||||
|
||||
/* Compute the GGX BRDF without the Fresnel term, multiplied by the cosine foreshortening term. */
|
||||
float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness)
|
||||
{
|
||||
float a = roughness;
|
||||
|
@ -125,12 +136,11 @@ float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness)
|
|||
float NL = max(dot(N, L), 1e-8);
|
||||
float NV = max(dot(N, V), 1e-8);
|
||||
|
||||
float G = G1_Smith_GGX_opti(NV, a2) * G1_Smith_GGX_opti(NL, a2); /* Doing RCP at the end */
|
||||
float D = D_ggx_opti(NH, a2);
|
||||
float G = bxdf_ggx_smith_G1(NV, a2) * bxdf_ggx_smith_G1(NL, a2);
|
||||
float D = bxdf_ggx_D(NH, a2);
|
||||
|
||||
/* Denominator is canceled by G1_Smith */
|
||||
/* bsdf = D * G / (4.0 * NL * NV); /* Reference function */
|
||||
return NL * a2 / (D * G); /* NL to Fit cycles Equation : line. 345 in bsdf_microfacet.h */
|
||||
/* brdf * NL = `((D * G) / (4 * NV * NL)) * NL`. */
|
||||
return (0.25 * D * G) / NV;
|
||||
}
|
||||
|
||||
void accumulate_light(vec3 light, float fac, inout vec4 accum)
|
||||
|
|
|
@ -19,9 +19,9 @@ float pdf_ggx_reflect(float NH, float NV, float VH, float alpha)
|
|||
{
|
||||
float a2 = sqr(alpha);
|
||||
#if USE_VISIBLE_NORMAL
|
||||
float D = a2 / D_ggx_opti(NH, a2);
|
||||
float G1 = NV * 2.0 / G1_Smith_GGX_opti(NV, a2);
|
||||
return G1 * VH * D / NV;
|
||||
float D = bxdf_ggx_D(NH, a2);
|
||||
float G1 = bxdf_ggx_smith_G1(NV, a2);
|
||||
return 0.25 * D * G1 / NV;
|
||||
#else
|
||||
return NH * a2 / D_ggx_opti(NH, a2);
|
||||
#endif
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* BxDF evaluation functions.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl)
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GGX
|
||||
|
@ -15,12 +15,7 @@
|
|||
|
||||
float bxdf_ggx_D(float NH, float a2)
|
||||
{
|
||||
return a2 / (M_PI * sqr((NH * a2 - NH) * NH + 1.0));
|
||||
}
|
||||
|
||||
float bxdf_ggx_smith_G1(float NX, float a2)
|
||||
{
|
||||
return 2.0 / (1.0 + sqrt(1.0 + a2 * (1 - NX * NX) / (NX * NX)));
|
||||
return a2 / (M_PI * square((a2 - 1.0) * (NH * NH) + 1.0));
|
||||
}
|
||||
|
||||
float bxdf_ggx_D_opti(float NH, float a2)
|
||||
|
@ -30,53 +25,71 @@ float bxdf_ggx_D_opti(float NH, float a2)
|
|||
return M_PI * tmp * tmp;
|
||||
}
|
||||
|
||||
float bxdf_ggx_smith_G1(float NX, float a2)
|
||||
{
|
||||
return 2.0 / (1.0 + sqrt(1.0 + a2 * (1.0 / (NX * NX) - 1.0)));
|
||||
}
|
||||
|
||||
float bxdf_ggx_smith_G1_opti(float NX, float a2)
|
||||
{
|
||||
/* Using Brian Karis approach and refactoring by NX/NX
|
||||
* this way the `(2*NL)*(2*NV)` in `G = G1(V) * G1(L)` gets canceled by the BRDF denominator
|
||||
* `4*NL*NV` Rcp is done on the whole G later. Note that this is not convenient for the
|
||||
* transmission formula. */
|
||||
// return 2 / (1 + sqrt(1 + a2 * (1 - NX*NX) / (NX*NX) ) ); /* Reference function. */
|
||||
return NX + sqrt(NX * (NX - NX * a2) + a2);
|
||||
}
|
||||
|
||||
/* Compute the GGX BRDF without the Fresnel term, multiplied by the cosine foreshortening term. */
|
||||
float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness)
|
||||
{
|
||||
float a2 = sqr(roughness);
|
||||
float a2 = square(roughness);
|
||||
|
||||
vec3 H = normalize(L + V);
|
||||
float NH = max(dot(N, H), 1e-8);
|
||||
float NL = max(dot(N, L), 1e-8);
|
||||
float NV = max(dot(N, V), 1e-8);
|
||||
|
||||
/* Doing RCP at the end */
|
||||
float G = bxdf_ggx_smith_G1_opti(NV, a2) * bxdf_ggx_smith_G1_opti(NL, a2);
|
||||
float D = bxdf_ggx_D_opti(NH, a2);
|
||||
/* TODO: maybe implement non-separable shadowing-masking term following Cycles. */
|
||||
float G = bxdf_ggx_smith_G1(NV, a2) * bxdf_ggx_smith_G1(NL, a2);
|
||||
float D = bxdf_ggx_D(NH, a2);
|
||||
|
||||
/* Denominator is canceled by G1_Smith */
|
||||
// bsdf = D * G / (4.0 * NL * NV); /* Reference function. */
|
||||
/* NL term to fit Cycles. NOTE(fclem): Not sure what it */
|
||||
return NL * a2 / (D * G);
|
||||
/* brdf * NL = `((D * G) / (4 * NV * NL)) * NL`. */
|
||||
return (0.25 * D * G) / NV;
|
||||
}
|
||||
|
||||
/* Compute the GGX BTDF without the Fresnel term, multiplied by the cosine foreshortening term. */
|
||||
float btdf_ggx(vec3 N, vec3 L, vec3 V, float roughness, float eta)
|
||||
{
|
||||
float a2 = sqr(roughness);
|
||||
float LV = dot(L, V);
|
||||
if (is_equal(eta, 1.0, 1e-4)) {
|
||||
/* Only valid when `L` and `V` point in the opposite directions. */
|
||||
return float(is_equal(LV, -1.0, 1e-3));
|
||||
}
|
||||
|
||||
vec3 H = normalize(L + V);
|
||||
bool valid = (eta < 1.0) ? (LV < -eta) : (LV * eta < -1.0);
|
||||
if (!valid) {
|
||||
/* Impossible configuration for transmission due to total internal reflection. */
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
vec3 H = eta * L + V;
|
||||
H = (eta < 1.0) ? H : -H;
|
||||
float inv_len_H = safe_rcp(length(H));
|
||||
H *= inv_len_H;
|
||||
|
||||
/* For transmission, `L` lies in the opposite hemisphere as `H`, therefore negate `L`. */
|
||||
float NH = max(dot(N, H), 1e-8);
|
||||
float NL = max(dot(N, L), 1e-8);
|
||||
float NL = max(dot(N, -L), 1e-8);
|
||||
float NV = max(dot(N, V), 1e-8);
|
||||
float VH = max(dot(V, H), 1e-8);
|
||||
float LH = max(dot(L, H), 1e-8);
|
||||
float Ht2 = sqr(eta * LH + VH);
|
||||
float VH = saturate(dot(V, H));
|
||||
float LH = saturate(dot(-L, H));
|
||||
|
||||
/* Doing RCP at the end */
|
||||
float G = bxdf_ggx_smith_G1_opti(NV, a2) * bxdf_ggx_smith_G1_opti(NL, a2);
|
||||
float D = bxdf_ggx_D_opti(NH, a2);
|
||||
float a2 = square(roughness);
|
||||
float G = bxdf_ggx_smith_G1(NV, a2) * bxdf_ggx_smith_G1(NL, a2);
|
||||
float D = bxdf_ggx_D(NH, a2);
|
||||
|
||||
/* `btdf = abs(VH*LH) * ior^2 * D * G(V) * G(L) / (Ht2 * NV)`. */
|
||||
return abs(VH * LH) * sqr(eta) * 4.0 * a2 / (D * G * (Ht2 * NV));
|
||||
/* `btdf * NL = abs(VH * LH) * ior^2 * D * G(V) * G(L) / (Ht2 * NV * NL) * NL`. */
|
||||
return (D * G * VH * LH * square(eta * inv_len_H)) / NV;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -19,7 +19,7 @@ float sample_pdf_ggx_reflect(float NH, float NV, float VH, float G1, float alpha
|
|||
{
|
||||
float a2 = sqr(alpha);
|
||||
#if GGX_USE_VISIBLE_NORMAL
|
||||
return G1 * VH * bxdf_ggx_D(NH, a2) / NV;
|
||||
return 0.25 * bxdf_ggx_D(NH, a2) * G1 / NV;
|
||||
#else
|
||||
return NH * bxdf_ggx_D(NH, a2);
|
||||
#endif
|
||||
|
@ -29,9 +29,9 @@ float sample_pdf_ggx_refract(
|
|||
float NH, float NV, float VH, float LH, float G1, float alpha, float eta)
|
||||
{
|
||||
float a2 = sqr(alpha);
|
||||
float D = bxdf_ggx_D_opti(NH, a2);
|
||||
float D = bxdf_ggx_D(NH, a2);
|
||||
float Ht2 = sqr(eta * LH + VH);
|
||||
return VH * abs(LH) * ((G1 * D) * sqr(eta) * a2 / (D * NV * Ht2));
|
||||
return (D * G1 * abs(VH * LH) * sqr(eta)) / (NV * Ht2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +89,7 @@ vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt, out float G1)
|
|||
* \param T: Tangent vector.
|
||||
* \param B: Bitangent vector.
|
||||
|
||||
* \return pdf: associated pdf for a reflection ray. 0 if ray is invalid.
|
||||
* \return pdf: the pdf of sampling the reflected ray. 0 if ray is invalid.
|
||||
*/
|
||||
vec3 sample_ggx_reflect(vec3 rand, float alpha, vec3 V, vec3 N, vec3 T, vec3 B, out float pdf)
|
||||
{
|
||||
|
@ -120,7 +120,7 @@ vec3 sample_ggx_reflect(vec3 rand, float alpha, vec3 V, vec3 N, vec3 T, vec3 B,
|
|||
* \param T: Tangent vector.
|
||||
* \param B: Bitangent vector.
|
||||
|
||||
* \return pdf_reflect: associated pdf for a reflection ray.
|
||||
* \return pdf: the pdf of sampling the refracted ray. 0 if ray is invalid.
|
||||
*/
|
||||
vec3 sample_ggx_refract(
|
||||
vec3 rand, float alpha, float ior, vec3 V, vec3 N, vec3 T, vec3 B, out float pdf)
|
||||
|
|
Loading…
Reference in New Issue