diff --git a/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl b/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl index 53b55e259a..904c9978ec 100644 --- a/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl +++ b/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl @@ -218,28 +218,21 @@ vec3 mx_fresnel_hoffman_schlick(float cosTheta, FresnelData fd) // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/ float mx_fresnel_dielectric(float cosTheta, float ior) { - if (cosTheta < 0.0) + float c = cosTheta; + float g2 = ior*ior + c*c - 1.0; + if (g2 < 0.0) { + // Total internal reflection return 1.0; } - float g = ior*ior + cosTheta*cosTheta - 1.0; - - // Check for total internal reflection - if (g < 0.0) - { - return 1.0; - } - - g = sqrt(g); - float gmc = g - cosTheta; - float gpc = g + cosTheta; - float x = gmc / gpc; - float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0); - return 0.5 * x * x * (1.0 + y * y); + float g = sqrt(g2); + return 0.5 * mx_square((g - c) / (g + c)) * + (1.0 + mx_square(((g + c) * c - 1.0) / ((g - c) * c + 1.0))); } -void mx_fresnel_dielectric_polarized(float cosTheta, float ior, out float Rp, out float Rs) +// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/ +vec2 mx_fresnel_dielectric_polarized(float cosTheta, float ior) { float cosTheta2 = mx_square(clamp(cosTheta, 0.0, 1.0)); float sinTheta2 = 1.0 - cosTheta2; @@ -247,13 +240,16 @@ void mx_fresnel_dielectric_polarized(float cosTheta, float ior, out float Rp, ou float t0 = max(ior * ior - sinTheta2, 0.0); float t1 = t0 + cosTheta2; float t2 = 2.0 * sqrt(t0) * cosTheta; - Rs = (t1 - t2) / (t1 + t2); + float Rs = (t1 - t2) / (t1 + t2); float t3 = cosTheta2 * t0 + sinTheta2 * sinTheta2; float t4 = t2 * sinTheta2; - Rp = Rs * (t3 - t4) / (t3 + t4); + float Rp = Rs * (t3 - t4) / (t3 + t4); + + return vec2(Rp, Rs); } +// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs) { float cosTheta2 = mx_square(clamp(cosTheta, 0.0, 1.0)); @@ -280,34 +276,9 @@ vec3 mx_fresnel_conductor(float cosTheta, vec3 n, vec3 k) return 0.5 * (Rp + Rs); } -// Phase shift due to a dielectric material -void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS) -{ - float cosB = cos(atan(eta2 / eta1)); // Brewster's angle - if (eta2 > eta1) - { - phiP = cosTheta < cosB ? M_PI : 0.0f; - phiS = 0.0f; - } - else - { - phiP = cosTheta < cosB ? 0.0f : M_PI; - phiS = M_PI; - } -} - -// Phase shift due to a conducting material +// https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS) { - if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) - { - // Use dielectric formula to increase performance - float phiPx, phiSx; - mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx); - phiP = vec3(phiPx, phiPx, phiPx); - phiS = vec3(phiSx, phiSx, phiSx); - return; - } vec3 k2 = kappa2 / eta2; vec3 sinThetaSqr = vec3(1.0) - cosTheta * cosTheta; vec3 A = eta2*eta2*(vec3(1.0)-k2*k2) - eta1*eta1*sinThetaSqr; @@ -320,7 +291,7 @@ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, mx_square(eta2*eta2*(vec3(1.0)+k2*k2)*cosTheta) - eta1*eta1*(U*U+V*V)); } -// Evaluation XYZ sensitivity curves in Fourier space +// https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html vec3 mx_eval_sensitivity(float opd, vec3 shift) { // Use Gaussian fits, given by 3 parameters: val, pos and var @@ -340,57 +311,39 @@ vec3 mx_fresnel_airy(float cosTheta, FresnelData fd) // XYZ to CIE 1931 RGB color space (using neutral E illuminant) const mat3 XYZ_TO_RGB = mat3(2.3706743, -0.5138850, 0.0052982, -0.9000405, 1.4253036, -0.0146949, -0.4706338, 0.0885814, 1.0093968); - // Convert nm -> m - float d = fd.tf_thickness * 1.0e-9; - // Assume vacuum on the outside float eta1 = 1.0; float eta2 = max(fd.tf_ior, eta1); vec3 eta3 = (fd.model == FRESNEL_MODEL_SCHLICK) ? mx_f0_to_ior(fd.F0) : fd.ior; vec3 kappa3 = (fd.model == FRESNEL_MODEL_SCHLICK) ? vec3(0.0) : fd.extinction; + float cosThetaT = sqrt(1.0 - (1.0 - mx_square(cosTheta)) * mx_square(eta1 / eta2)); - // Compute the Spectral versions of the Fresnel reflectance and - // transmitance for each interface. - float R12p, T121p, R12s, T121s; + // First interface + vec2 R12 = mx_fresnel_dielectric_polarized(cosTheta, eta2 / eta1); + if (cosThetaT <= 0.0) + { + // Total internal reflection + R12 = vec2(1.0); + } + vec2 T121 = vec2(1.0) - R12; + + // Second interface vec3 R23p, R23s; - - // Reflected and transmitted parts in the thin film - mx_fresnel_dielectric_polarized(cosTheta, eta2 / eta1, R12p, R12s); - - // Reflected part by the base - float scale = eta1 / eta2; - float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale; - float cosTheta2 = sqrt(cosThetaTSqr); if (fd.model == FRESNEL_MODEL_SCHLICK) { - vec3 f = mx_fresnel_hoffman_schlick(cosTheta2, fd); + vec3 f = mx_fresnel_hoffman_schlick(cosThetaT, fd); R23p = 0.5 * f; R23s = 0.5 * f; } else { - mx_fresnel_conductor_polarized(cosTheta2, eta3 / eta2, kappa3 / eta2, R23p, R23s); - } - - // Check for total internal reflection - if (cosThetaTSqr <= 0.0f) - { - R12p = 1.0; - R12s = 1.0; + mx_fresnel_conductor_polarized(cosThetaT, eta3 / eta2, kappa3 / eta2, R23p, R23s); } - // Compute the transmission coefficients - T121p = 1.0 - R12p; - T121s = 1.0 - R12s; - - // Optical path difference - float D = 2.0 * eta2 * d * cosTheta2; - - float phi21p, phi21s; - vec3 phi23p, phi23s, r123s, r123p; - - // Evaluate the phase shift - mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s); + // Phase shift + float cosB = cos(atan(eta2 / eta1)); + vec2 phi21 = vec2(cosTheta < cosB ? 0.0 : M_PI, M_PI); + vec3 phi23p, phi23s; if (fd.model == FRESNEL_MODEL_SCHLICK) { phi23p = vec3((eta3[0] < eta2) ? M_PI : 0.0, @@ -400,46 +353,46 @@ vec3 mx_fresnel_airy(float cosTheta, FresnelData fd) } else { - mx_fresnel_conductor_phase_polarized(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s); + mx_fresnel_conductor_phase_polarized(cosThetaT, eta2, eta3, kappa3, phi23p, phi23s); } + vec3 r123p = max(sqrt(R12.x*R23p), 0.0); + vec3 r123s = max(sqrt(R12.y*R23s), 0.0); - phi21p = M_PI - phi21p; - phi21s = M_PI - phi21s; - - r123p = max(vec3(0.0), sqrt(R12p*R23p)); - r123s = max(vec3(0.0), sqrt(R12s*R23s)); - - // Evaluate iridescence term + // Iridescence term vec3 I = vec3(0.0); vec3 Cm, Sm; + // Optical path difference + float distMeters = fd.tf_thickness * 1.0e-9; + float opd = 2.0 * eta2 * cosThetaT * distMeters; + // Iridescence term using spectral antialiasing for Parallel polarization // Reflectance term for m=0 (DC term amplitude) - vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p); - I += R12p + Rs; + vec3 Rs = (mx_square(T121.x) * R23p) / (vec3(1.0) - R12.x*R23p); + I += R12.x + Rs; // Reflectance term for m>0 (pairs of diracs) - Cm = Rs - T121p; + Cm = Rs - T121.x; for (int m=1; m<=2; m++) { Cm *= r123p; - Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p))); + Sm = 2.0 * mx_eval_sensitivity(float(m) * opd, float(m)*(phi23p+vec3(phi21.x))); I += Cm*Sm; } // Iridescence term using spectral antialiasing for Perpendicular polarization // Reflectance term for m=0 (DC term amplitude) - vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s); - I += R12s + Rp; + vec3 Rp = (mx_square(T121.y) * R23s) / (vec3(1.0) - R12.y*R23s); + I += R12.y + Rp; // Reflectance term for m>0 (pairs of diracs) - Cm = Rp - T121s; + Cm = Rp - T121.y; for (int m=1; m<=2; m++) { Cm *= r123s; - Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s))); + Sm = 2.0 * mx_eval_sensitivity(float(m) * opd, float(m)*(phi23s+vec3(phi21.y))); I += Cm*Sm; } @@ -447,7 +400,7 @@ vec3 mx_fresnel_airy(float cosTheta, FresnelData fd) I *= 0.5; // Convert back to RGB reflectance - I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0)); + I = clamp(XYZ_TO_RGB * I, 0.0, 1.0); return I; }