From 4b3224acd723dacea570a1f94642f2b61d110f42 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 26 Aug 2025 11:50:15 +0700 Subject: [PATCH 01/25] initial iridescent fresnel, only single channel ior --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 165 ++++++++++++++++++++- 1 file changed, 157 insertions(+), 8 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 8284f46e6e..25c5e64d2b 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -10,6 +10,7 @@ #include "nbl/builtin/hlsl/numbers.hlsl" #include "nbl/builtin/hlsl/complex.hlsl" #include "nbl/builtin/hlsl/tgmath.hlsl" +#include "nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl" #include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" namespace nbl @@ -366,22 +367,54 @@ struct Conductor return retval; } + // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) + static vector __polarized_w_phase_shift(NBL_CONST_REF_ARG(T) orientedEta, scalar_type orientedEtak, scalar_type cosTheta, NBL_REF_ARG(vector) phi) + { + scalar_type cosTheta_2 = cosTheta * cosTheta; + scalar_type sinTheta2 = scalar_type(1.0) - cosTheta_2; + const scalar_type eta = orientedEta[0]; + const scalar_type eta2 = eta*eta; + const scalar_type etak = orientedEtak; + const scalar_type etak2 = etak*etak; + + scalar_type z = eta2 - etak2 - sinTheta2; + scalar_type w = hlsl::sqrt(z * z + scalar_type(4.0) * eta2 * eta2 * etak2); + scalar_type a2 = (z + w) / scalar_type(2.0); + scalar_type b2 = (w - z) / scalar_type(2.0); + scalar_type b = hlsl::sqrt(b2); + + const scalar_type etaLen2 = eta2 + etak2; + assert(etaLen2 > hlsl::exp2(-numeric_limits::digits)); + scalar_type t1 = etaLen2 * cosTheta_2; + const scalar_type etaCosTwice = eta * clampedCosTheta * scalar_type(2.0); + + phi.y = hlsl::atan(scalar_type(2.0) * b * cosTheta, a2 + b2 - cosTheta_2) + numbers::pi; + phi.x = hlsl::atan(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * etak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); + + vector R; // (rp, rs) + const T rs_common = etaLen2 + cosTheta_2; + R.y = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); + const T rp_common = t1 + scalar_type(1.0); + R.x = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); + return R; + } + T operator()() { - const scalar_type cosTheta2 = clampedCosTheta * clampedCosTheta; - //const float sinTheta2 = 1.0 - cosTheta2; + const scalar_type cosTheta_2 = clampedCosTheta * clampedCosTheta; + //const float sinTheta2 = 1.0 - cosTheta_2; const T etaLen2 = eta * eta + etak2; assert(hlsl::all(etaLen2 > hlsl::promote(hlsl::exp2(-numeric_limits::digits)))); - const T etaCosTwice = eta * clampedCosTheta * 2.0f; + const T etaCosTwice = eta * clampedCosTheta * hlsl::promote(2.0); - const T rs_common = etaLen2 + (T)(cosTheta2); + const T rs_common = etaLen2 + hlsl::promote(cosTheta_2); const T rs2 = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); - const T rp_common = etaLen2 * cosTheta2 + (T)(1.0); + const T rp_common = etaLen2 * cosTheta_2 + hlsl::promote(1.0); const T rp2 = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); - return (rs2 + rp2) * 0.5f; + return (rs2 + rp2) * hlsl::promote(0.5); } T eta; @@ -405,9 +438,35 @@ struct Dielectric return retval; } + // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) + static vector __polarized_w_phase_shift(NBL_CONST_REF_ARG(T) orientedEta, scalar_type cosTheta, NBL_REF_ARG(vector) phi) + { + scalar_type sinTheta2 = scalar_type(1.0) - cosTheta * cosTheta; + const scalar_type eta = orientedEta[0]; + const scalar_type eta2 = eta * eta; + + // TIR + if (eta2 * sinTheta2 > scalar_type(1.0)) + { + const scalar_type phase_advance_s = hlsl::sqrt(sinTheta2 - scalar_type(1.0) / eta2) / cosTheta; + phi = scalar_type(2.0) * hlsl::atan(vector(-eta2 * phase_advance_s, + phase_advance_s)); + return hlsl::promote >(1.0); + } + + scalar_type t0 = hlsl::sqrt(eta2 - sinTheta2); + scalar_type t2 = eta * cosTheta; + + vector R; // (rp, rs) + R.x = (t0 - t2) / (t0 + t2); + R.y = (cosTheta - t0) / (cosTheta + t0); + phi = hlsl::mix(hlsl::promote >(0.0), hlsl::promote >(numbers::pi), R < vector(0.0)); + return R; + } + static T __call(NBL_CONST_REF_ARG(T) orientedEta2, scalar_type absCosTheta) { - const scalar_type sinTheta2 = 1.0 - absCosTheta * absCosTheta; + const scalar_type sinTheta2 = scalar_type(1.0) - absCosTheta * absCosTheta; // the max() clamping can handle TIR when orientedEta2<1.0 const T t0 = hlsl::sqrt(hlsl::max(orientedEta2 - sinTheta2, hlsl::promote(0.0))); @@ -416,7 +475,7 @@ struct Dielectric const T t2 = orientedEta2 * absCosTheta; const T rp = (t0 - t2) / (t0 + t2); - return (rs * rs + rp * rp) * 0.5f; + return (rs * rs + rp * rp) * scalar_type(0.5); } T operator()() @@ -451,6 +510,96 @@ struct DielectricFrontFaceOnly scalar_type absCosTheta; }; +// adapted from https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html +template) +struct Iridescent +{ + using scalar_type = typename vector_traits::scalar_type; + using vector2_type = vector; + using vector_type = T; + + // Depolarization functions for natural light + static scalar_type depolarize(vector2_type polV) + { + return scalar_type(0.5) * (polV.x + polV.y); + } + static vector_type depolarizeColor(vector_type colS, vector_type colP) + { + return scalar_type(0.5) * (colS + colP); + } + + // Evaluation XYZ sensitivity curves in Fourier space + static vector_type evalSensitivity(scalar_type opd, scalar_type shift) + { + // Use Gaussian fits, given by 3 parameters: val, pos and var + scalar_type phase = scalar_type(2.0) * numbers::pi * opd * scalar_type(1.0e-6); + vector_type val = vector_type(5.4856e-13, 4.4201e-13, 5.2481e-13); + vector_type pos = vector_type(1.6810e+06, 1.7953e+06, 2.2084e+06); + vector_type var = vector_type(4.3278e+09, 9.3046e+09, 6.6121e+09); + vector_type xyz = val * hlsl::sqrt(scalar_type(2.0) * numbers::pi * var) * hlsl::cos(pos * phase + shift) * hlsl::exp(- var * phase * phase); + xyz.x += scalar_type(9.7470e-14) * hlsl::sqrt(scalar_type(2.0) * numbers::pi * scalar_type(4.5282e+09)) * hlsl::cos(scalar_type(2.2399e+06) * phase + shift) * hlsl::exp(scalar_type(-4.5282e+09) * phase * phase); + return xyz / scalar_type(1.0685e-7); + } + + T operator()() + { + // Force ior_1 -> 1.0 when Dinc -> 0.0 + scalar_type ior_1 = hlsl::mix(1.0, ior1, hlsl::smoothstep(0.0, 0.03, Dinc)); + + scalar_type eta1 = ior_1; // ior/1.0 (air) + scalar_type rcp_eta1 = 1.0/eta1; + scalar_type cosTheta_1 = cosTheta; + scalar_type cosTheta_2 = hlsl::sqrt(1.0 - rcp_eta1*rcp_eta1*(1.0-cosTheta_1*cosTheta_1) ); + + // First interface + vector2_type phi12; + vector2_type R12 = Dielectric >::__polarized_w_phase_shift(hlsl::promote >(eta1), cosTheta_1, phi12); + vector2_type R21 = R12; + vector2_type T121 = hlsl::promote(1.0) - R12; + vector2_type phi21 = hlsl::promote(numbers::pi) - phi12; + + // Second interface + scalar_type eta2 = ior2/ior_1; + scalar_type etak2 = iork2/ior_1; + vector2_type R23, phi23; + Conductor >::__polarized_w_phase_shift(hlsl::promote >(eta2), etak2, cosTheta_2, phi23); + + // Phase shift + scalar_type OPD = Dinc*cosTheta_2; + vector2_type phi2 = phi21 + phi23; + + // Compound terms + vector_type I = hlsl::promote(0.0); + vector2_type R123 = R12*R23; + vector2_type r123 = hlsl::sqrt(R123); + vector2_type Rs = T121*T121*R23 / (hlsl::promote(1.0) - R123); + + // Reflectance term for m=0 (DC term amplitude) + vector2_type C0 = R12 + Rs; + vector_type S0 = evalSensitivity(0.0, 0.0); + I += depol(C0) * S0; + + // Reflectance term for m>0 (pairs of diracs) + vector2_type Cm = Rs - T121; + NBL_UNROLL for (uint32_t m = 1; m <= 3; m++) + { + Cm *= r123; + vector_type SmS = scalar_type(2.0) * evalSensitivity(scalar_type(m) * OPD, scalar_type(m) * phi2.x); + vector_type SmP = scalar_type(2.0) * evalSensitivity(scalar_type(m) * OPD, scalar_type(m) * phi2.y); + I += depolColor(Cm.x*SmS, Cm.y*SmP); + } + + // Convert back to RGB reflectance + return hlsl::clamp(hlsl::mul(colorspace::decode::XYZtoscRGB, I), vector_type(0.0), vector_type(1.0)); + } + + scalar_type cosTheta; // LdotH + scalar_type Dinc; // thickness of thin film in micrometers, max 25 + scalar_type ior1; // thin-film index + scalar_type ior2; // complex conductor index, k==0 makes dielectric + scalar_type iork2; +}; + // gets the sum of all R, T R T, T R^3 T, T R^5 T, ... paths template || concepts::FloatingPointLikeVectorial) From 9c7190654df1d664b6dc01aea6a98baae1abcb75 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 26 Aug 2025 16:29:11 +0700 Subject: [PATCH 02/25] iridescent fresnel does rgb IOR, use in brdf --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 210 +++++++++++------- .../hlsl/bxdf/reflection/iridescent.hlsl | 202 +++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 1 + 3 files changed, 328 insertions(+), 85 deletions(-) create mode 100644 include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 25c5e64d2b..62c5f17d51 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -368,35 +368,24 @@ struct Conductor } // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) - static vector __polarized_w_phase_shift(NBL_CONST_REF_ARG(T) orientedEta, scalar_type orientedEtak, scalar_type cosTheta, NBL_REF_ARG(vector) phi) + static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, NBL_CONST_REF_ARG(T) orientedEtak, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) { scalar_type cosTheta_2 = cosTheta * cosTheta; scalar_type sinTheta2 = scalar_type(1.0) - cosTheta_2; const scalar_type eta = orientedEta[0]; const scalar_type eta2 = eta*eta; - const scalar_type etak = orientedEtak; + const scalar_type etak = orientedEtak[0]; const scalar_type etak2 = etak*etak; - scalar_type z = eta2 - etak2 - sinTheta2; - scalar_type w = hlsl::sqrt(z * z + scalar_type(4.0) * eta2 * eta2 * etak2); - scalar_type a2 = (z + w) / scalar_type(2.0); - scalar_type b2 = (w - z) / scalar_type(2.0); - scalar_type b = hlsl::sqrt(b2); - const scalar_type etaLen2 = eta2 + etak2; assert(etaLen2 > hlsl::exp2(-numeric_limits::digits)); scalar_type t1 = etaLen2 * cosTheta_2; const scalar_type etaCosTwice = eta * clampedCosTheta * scalar_type(2.0); - phi.y = hlsl::atan(scalar_type(2.0) * b * cosTheta, a2 + b2 - cosTheta_2) + numbers::pi; - phi.x = hlsl::atan(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * etak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); - - vector R; // (rp, rs) const T rs_common = etaLen2 + cosTheta_2; - R.y = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); + Rs = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); const T rp_common = t1 + scalar_type(1.0); - R.x = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); - return R; + Rp = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); } T operator()() @@ -438,30 +427,20 @@ struct Dielectric return retval; } - // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) - static vector __polarized_w_phase_shift(NBL_CONST_REF_ARG(T) orientedEta, scalar_type cosTheta, NBL_REF_ARG(vector) phi) + // returns reflectance R = (rp, rs) + static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) { scalar_type sinTheta2 = scalar_type(1.0) - cosTheta * cosTheta; const scalar_type eta = orientedEta[0]; const scalar_type eta2 = eta * eta; - // TIR - if (eta2 * sinTheta2 > scalar_type(1.0)) - { - const scalar_type phase_advance_s = hlsl::sqrt(sinTheta2 - scalar_type(1.0) / eta2) / cosTheta; - phi = scalar_type(2.0) * hlsl::atan(vector(-eta2 * phase_advance_s, - phase_advance_s)); - return hlsl::promote >(1.0); - } - scalar_type t0 = hlsl::sqrt(eta2 - sinTheta2); scalar_type t2 = eta * cosTheta; - vector R; // (rp, rs) - R.x = (t0 - t2) / (t0 + t2); - R.y = (cosTheta - t0) / (cosTheta + t0); - phi = hlsl::mix(hlsl::promote >(0.0), hlsl::promote >(numbers::pi), R < vector(0.0)); - return R; + scalar_type rp = (t0 - t2) / (t0 + t2); + Rp = rp * rp; + scalar_type rs = (cosTheta - t0) / (cosTheta + t0); + Rs = rs * rs; } static T __call(NBL_CONST_REF_ARG(T) orientedEta2, scalar_type absCosTheta) @@ -515,89 +494,150 @@ template struct Iridescent { using scalar_type = typename vector_traits::scalar_type; - using vector2_type = vector; + using monochrome_type = vector; using vector_type = T; - // Depolarization functions for natural light - static scalar_type depolarize(vector2_type polV) - { - return scalar_type(0.5) * (polV.x + polV.y); - } - static vector_type depolarizeColor(vector_type colS, vector_type colP) + // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) + static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, scalar_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) { - return scalar_type(0.5) * (colS + colP); + scalar_type cosTheta_2 = cosTheta * cosTheta; + scalar_type sinTheta2 = scalar_type(1.0) - cosTheta_2; + const vector_type eta2 = orientedEta*orientedEta; + const vector_type etak2 = orientedEtak*orientedEtak; + + vector_type z = eta2 - etak2 - hlsl::promote(sinTheta2); + vector_type w = hlsl::sqrt(z * z + scalar_type(4.0) * eta2 * eta2 * etak2); + vector_type a2 = (z + w) * hlsl::promote(0.5); + vector_type b2 = (w - z) * hlsl::promote(0.5); + vector_type b = hlsl::sqrt(b2); + + const vector_type t0 = eta2 + etak2; + const vector_type t1 = t0 * cosTheta_2; + + phiS = hlsl::atan(scalar_type(2.0) * b * cosTheta, a2 + b2 - hlsl::promote(cosTheta_2)); + phiP = hlsl::atan(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * orientedEtak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); } // Evaluation XYZ sensitivity curves in Fourier space - static vector_type evalSensitivity(scalar_type opd, scalar_type shift) + static vector_type evalSensitivity(vector_type opd, vector_type shift) { // Use Gaussian fits, given by 3 parameters: val, pos and var - scalar_type phase = scalar_type(2.0) * numbers::pi * opd * scalar_type(1.0e-6); + vector_type phase = scalar_type(2.0) * numbers::pi * opd * scalar_type(1.0e-9); + vector_type phase2 = phase * phase; vector_type val = vector_type(5.4856e-13, 4.4201e-13, 5.2481e-13); vector_type pos = vector_type(1.6810e+06, 1.7953e+06, 2.2084e+06); vector_type var = vector_type(4.3278e+09, 9.3046e+09, 6.6121e+09); - vector_type xyz = val * hlsl::sqrt(scalar_type(2.0) * numbers::pi * var) * hlsl::cos(pos * phase + shift) * hlsl::exp(- var * phase * phase); - xyz.x += scalar_type(9.7470e-14) * hlsl::sqrt(scalar_type(2.0) * numbers::pi * scalar_type(4.5282e+09)) * hlsl::cos(scalar_type(2.2399e+06) * phase + shift) * hlsl::exp(scalar_type(-4.5282e+09) * phase * phase); + vector_type xyz = val * hlsl::sqrt(scalar_type(2.0) * numbers::pi * var) * hlsl::cos(pos * phase + shift) * hlsl::exp(-var * phase2); + xyz.x += scalar_type(9.7470e-14) * hlsl::sqrt(scalar_type(2.0) * numbers::pi * scalar_type(4.5282e+09)) * hlsl::cos(hlsl::promote(2.2399e+06) * phase + shift) * hlsl::exp(hlsl::promote(-4.5282e+09) * phase2); return xyz / scalar_type(1.0685e-7); } T operator()() { - // Force ior_1 -> 1.0 when Dinc -> 0.0 - scalar_type ior_1 = hlsl::mix(1.0, ior1, hlsl::smoothstep(0.0, 0.03, Dinc)); + // Force ior_2 -> 1.0 when Dinc -> 0.0 + scalar_type ior_2 = hlsl::mix(1.0, ior2, hlsl::smoothstep(0.0, 0.03, Dinc)); + const vector_type wavelengths = vector_type(580.0, 550.0, 450.0); - scalar_type eta1 = ior_1; // ior/1.0 (air) - scalar_type rcp_eta1 = 1.0/eta1; + vector_type eta12 = ior_2/ior1; + vector_type eta23 = ior3/ior_2; + vector_type etak23 = iork3/ior_2; scalar_type cosTheta_1 = cosTheta; - scalar_type cosTheta_2 = hlsl::sqrt(1.0 - rcp_eta1*rcp_eta1*(1.0-cosTheta_1*cosTheta_1) ); - - // First interface - vector2_type phi12; - vector2_type R12 = Dielectric >::__polarized_w_phase_shift(hlsl::promote >(eta1), cosTheta_1, phi12); - vector2_type R21 = R12; - vector2_type T121 = hlsl::promote(1.0) - R12; - vector2_type phi21 = hlsl::promote(numbers::pi) - phi12; - - // Second interface - scalar_type eta2 = ior2/ior_1; - scalar_type etak2 = iork2/ior_1; - vector2_type R23, phi23; - Conductor >::__polarized_w_phase_shift(hlsl::promote >(eta2), etak2, cosTheta_2, phi23); - - // Phase shift - scalar_type OPD = Dinc*cosTheta_2; - vector2_type phi2 = phi21 + phi23; - - // Compound terms + vector_type cosTheta_2; + + vector_type R12p, T121p, R23p, R12s, T121s, R23s, ct2; + NBL_UNROLL for(uint32_t i = 0; i < vector_traits::Dimension; i++) // TODO: could probably do calcs on vectors instead of loop + { + const scalar_type scale = ior1[i]/ior_2[i]; //(cosTheta_1 > 0) ? ior1[i]/ior_2[i] : ior_2[i]/ior1[i]; + const scalar_type cosThetaTSqr = 1 - (1-cosTheta_1*cosTheta_1) * scale * scale; + + /* Check for total internal reflection */ + if (cosThetaTSqr <= 0.0f) + { + R12s[i] = 1.0; + R12p[i] = 1.0; + + // Compute the transmission coefficients + T121p[i] = 0.0; + T121s[i] = 0.0; + } + else + { + cosTheta_2[i] = hlsl::sqrt(cosThetaTSqr); + Dielectric::__polarized(hlsl::promote(eta12[i]), cosTheta_1, R12p[i], R12s[i]); + + // Reflected part by the base + // if kappa==0, base material is dielectric + if (hlsl::all::Dimension> >(iork3 < hlsl::promote(hlsl::numeric_limits::min))) + Dielectric::__polarized(hlsl::promote(eta23[i]), cosTheta_2[i], R23p[i], R23s[i]); + else + Conductor::__polarized(hlsl::promote(eta23[i]), hlsl::promote(etak23[i]), cosTheta_2[i], R23p[i], R23s[i]); + + // Compute the transmission coefficients + T121p[i] = 1.0 - R12p[i]; + T121s[i] = 1.0 - R12s[i]; + } + } + + /* Optical Path Difference */ + const vector_type D = 2.0 * ior_2 * Dinc * cosTheta_2; + const vector_type Dphi = 2.0 * numbers::pi * D / wavelengths; + + vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs, cosP, irid; vector_type I = hlsl::promote(0.0); - vector2_type R123 = R12*R23; - vector2_type r123 = hlsl::sqrt(R123); - vector2_type Rs = T121*T121*R23 / (hlsl::promote(1.0) - R123); + /* Evaluate the phase shift */ + phase_shift(eta12, hlsl::promote(0.0), hlsl::promote(cosTheta_1), phi21p, phi21s); + phase_shift(eta23, etak23, cosTheta_2, phi23p, phi23s); + phi21p = hlsl::promote(numbers::pi) - phi21p; + phi21s = hlsl::promote(numbers::pi) - phi21s; + + r123p = hlsl::sqrt(R12p*R23p); + r123s = hlsl::sqrt(R12s*R23s); + + vector_type C0, Cm, Sm; + const vector_type S0 = hlsl::promote(1.0); + + /* Iridescence term using spectral antialiasing for Parallel polarization */ + // Reflectance term for m=0 (DC term amplitude) + Rs = (T121p*T121p*R23p) / (hlsl::promote(1.0) - R12p*R23p); + C0 = R12p + Rs; + I += C0 * S0; + + // Reflectance term for m>0 (pairs of diracs) + Cm = Rs - T121p; + NBL_UNROLL for (int m=1; m<=2; ++m) + { + Cm *= r123p; + Sm = 2.0 * evalSensitivity(m*D, m*(phi23p+phi21p)); + I += Cm*Sm; + } + + /* Iridescence term using spectral antialiasing for Perpendicular polarization */ // Reflectance term for m=0 (DC term amplitude) - vector2_type C0 = R12 + Rs; - vector_type S0 = evalSensitivity(0.0, 0.0); - I += depol(C0) * S0; + Rs = (T121s*T121s*R23s) / (hlsl::promote(1.0) - R12s*R23s); + C0 = R12s + Rs; + I += C0 * S0; // Reflectance term for m>0 (pairs of diracs) - vector2_type Cm = Rs - T121; - NBL_UNROLL for (uint32_t m = 1; m <= 3; m++) + Cm = Rs - T121s; + NBL_UNROLL for (int m=1; m<=2; ++m) { - Cm *= r123; - vector_type SmS = scalar_type(2.0) * evalSensitivity(scalar_type(m) * OPD, scalar_type(m) * phi2.x); - vector_type SmP = scalar_type(2.0) * evalSensitivity(scalar_type(m) * OPD, scalar_type(m) * phi2.y); - I += depolColor(Cm.x*SmS, Cm.y*SmP); + Cm *= r123s; + Sm = 2.0 * evalSensitivity(m*D, m*(phi23s+phi21s)); + I += Cm*Sm; } - // Convert back to RGB reflectance - return hlsl::clamp(hlsl::mul(colorspace::decode::XYZtoscRGB, I), vector_type(0.0), vector_type(1.0)); + // note: original paper used the CIE XYZ 1931 to RGB conversion + I = hlsl::mul(colorspace::decode::XYZtoscRGB, I); + return hlsl::max(I, hlsl::promote(0.0)); } scalar_type cosTheta; // LdotH - scalar_type Dinc; // thickness of thin film in micrometers, max 25 - scalar_type ior1; // thin-film index - scalar_type ior2; // complex conductor index, k==0 makes dielectric - scalar_type iork2; + scalar_type Dinc; // thickness of thin film in nanometers, max 25000nm + vector_type ior1; // usually air (1.0) + vector_type ior2; // thin-film index + vector_type ior3; // complex conductor index, k==0 makes dielectric + vector_type iork3; }; diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl new file mode 100644 index 0000000000..6d34d3d7ba --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl @@ -0,0 +1,202 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_IRIDESCENT_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_IRIDESCENT_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template) +struct SIridescent +{ + using this_t = SIridescent; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + + using ndf_type = ndf::GGX; + using fresnel_type = fresnel::Iridescent; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; + + struct SCreationParams + { + scalar_type A; + scalar_type thickness; // thin-film thickness in nm + spectral_type ior0 + spectral_type ior1 + spectral_type ior2 + spectral_type iork2; + }; + using creation_type = SCreationParams; + + struct SIridQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + + scalar_type devsh_v; + scalar_type devsh_l; + }; + using query_type = SIridQuery; + + static this_t create(scalar_type A, scalar_type thickness, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1, NBL_CONST_REF_ARG(spectral_type) ior2, NBL_CONST_REF_ARG(spectral_type) iork2) + { + this_t retval; + retval.__base.ndf.A = vector2_type(A, A); + retval.__base.ndf.a2 = A*A; + retval.__base.ndf.one_minus_a2 = scalar_type(1.0) - A*A; + retval.__base.fresnel.Dinc = thickness; + retval.__base.fresnel.ior1 = ior0; + retval.__base.fresnel.ior2 = ior1; + retval.__base.fresnel.ior3 = ior2; + retval.__base.fresnel.iork3 = iork2; + return retval; + } + static this_t create(NBL_CONST_REF_ARG(creation_type) params) + { + return create(params.A, params.thickness, params.ior0, params.ior1, params.ior2, params.iork2); + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + query_type query; + ndf_type ggx_ndf = __base.getNDF(); + query.devsh_v = ggx_ndf.devsh_part(interaction.getNdotV2()); + query.devsh_l = ggx_ndf.devsh_part(_sample.getNdotL2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.maxNdotL = _sample.getNdotL(_clamp); + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + fresnel_type f = __base.getFresnel(); + f.cosTheta = cache.getLdotH(); + return f() * DG; + } + else + return hlsl::promote(0.0); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(isocache_type) cache) + { + SGGXAnisotropic ggx_aniso = SGGXAnisotropic::create(__base.ndf.A.x, __base.ndf.A.y, __base.fresnel.ior3/__base.fresnel.ior2, __base.fresnel.iork3/__base.fresnel.ior2); + anisocache_type anisocache; + sample_type s = ggx_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SGGXDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } + + scalar_type ndf; + scalar_type G1_over_2NdotV; + }; + + SGGXDG1Query dg1_query; + ndf_type ggx_ndf = __base.getNDF(); + dg1_query.ndf = __base.__D(cache); + + const scalar_type devsh_v = query.getDevshV(); + dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), devsh_v); + + measure_transform_type dualMeasure = __base.template __DG1(dg1_query); + return dualMeasure.getMicrofacetMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + + spectral_type quo = hlsl::promote(0.0); + if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + ndf_type ggx_ndf = __base.getNDF(); + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + const scalar_type G2_over_G1 = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + + fresnel_type f = __base.getFresnel(); + f.cosTheta = cache.getLdotH(); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +} +} +} +} + +#endif diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 17e07257b4..fcc79d3714 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -347,6 +347,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/beckmann.hlsl LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/ggx.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/lambertian.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/oren_nayar.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/iridescent.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/beckmann.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/ggx.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/lambertian.hlsl") From 0f5130684242ec75d051a8d94edba8d1e30c565f Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 27 Aug 2025 11:29:57 +0700 Subject: [PATCH 03/25] some bug fixes --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 51 ++++++++++--------- include/nbl/builtin/hlsl/bxdf/reflection.hlsl | 1 + .../hlsl/bxdf/reflection/iridescent.hlsl | 15 ++++-- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 62c5f17d51..26d80d4316 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -367,7 +367,7 @@ struct Conductor return retval; } - // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) + // TODO: will probably merge with __call at some point static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, NBL_CONST_REF_ARG(T) orientedEtak, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) { scalar_type cosTheta_2 = cosTheta * cosTheta; @@ -380,11 +380,11 @@ struct Conductor const scalar_type etaLen2 = eta2 + etak2; assert(etaLen2 > hlsl::exp2(-numeric_limits::digits)); scalar_type t1 = etaLen2 * cosTheta_2; - const scalar_type etaCosTwice = eta * clampedCosTheta * scalar_type(2.0); + const scalar_type etaCosTwice = eta * cosTheta * scalar_type(2.0); - const T rs_common = etaLen2 + cosTheta_2; + const scalar_type rs_common = etaLen2 + cosTheta_2; Rs = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); - const T rp_common = t1 + scalar_type(1.0); + const scalar_type rp_common = t1 + scalar_type(1.0); Rp = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); } @@ -427,7 +427,7 @@ struct Dielectric return retval; } - // returns reflectance R = (rp, rs) + // TODO: will probably merge with __call at some point static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) { scalar_type sinTheta2 = scalar_type(1.0) - cosTheta * cosTheta; @@ -435,7 +435,7 @@ struct Dielectric const scalar_type eta2 = eta * eta; scalar_type t0 = hlsl::sqrt(eta2 - sinTheta2); - scalar_type t2 = eta * cosTheta; + scalar_type t2 = eta2 * cosTheta; scalar_type rp = (t0 - t2) / (t0 + t2); Rp = rp * rp; @@ -451,6 +451,7 @@ struct Dielectric const T t0 = hlsl::sqrt(hlsl::max(orientedEta2 - sinTheta2, hlsl::promote(0.0))); const T rs = (hlsl::promote(absCosTheta) - t0) / (hlsl::promote(absCosTheta) + t0); + // one additional orientedEta multiplied to remove the 1/orientedEta and make it the same as t0 for rs const T t2 = orientedEta2 * absCosTheta; const T rp = (t0 - t2) / (t0 + t2); @@ -498,10 +499,10 @@ struct Iridescent using vector_type = T; // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) - static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, scalar_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) + static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, const vector_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) { - scalar_type cosTheta_2 = cosTheta * cosTheta; - scalar_type sinTheta2 = scalar_type(1.0) - cosTheta_2; + vector_type cosTheta_2 = cosTheta * cosTheta; + vector_type sinTheta2 = hlsl::promote(1.0) - cosTheta_2; const vector_type eta2 = orientedEta*orientedEta; const vector_type etak2 = orientedEtak*orientedEtak; @@ -514,28 +515,28 @@ struct Iridescent const vector_type t0 = eta2 + etak2; const vector_type t1 = t0 * cosTheta_2; - phiS = hlsl::atan(scalar_type(2.0) * b * cosTheta, a2 + b2 - hlsl::promote(cosTheta_2)); - phiP = hlsl::atan(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * orientedEtak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); + phiS = hlsl::atan2(scalar_type(2.0) * b * cosTheta, a2 + b2 - cosTheta_2); + phiP = hlsl::atan2(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * orientedEtak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); } // Evaluation XYZ sensitivity curves in Fourier space static vector_type evalSensitivity(vector_type opd, vector_type shift) { // Use Gaussian fits, given by 3 parameters: val, pos and var - vector_type phase = scalar_type(2.0) * numbers::pi * opd * scalar_type(1.0e-9); + vector_type phase = scalar_type(2.0) * numbers::pi * opd * scalar_type(1.0e-9); vector_type phase2 = phase * phase; vector_type val = vector_type(5.4856e-13, 4.4201e-13, 5.2481e-13); vector_type pos = vector_type(1.6810e+06, 1.7953e+06, 2.2084e+06); vector_type var = vector_type(4.3278e+09, 9.3046e+09, 6.6121e+09); - vector_type xyz = val * hlsl::sqrt(scalar_type(2.0) * numbers::pi * var) * hlsl::cos(pos * phase + shift) * hlsl::exp(-var * phase2); - xyz.x += scalar_type(9.7470e-14) * hlsl::sqrt(scalar_type(2.0) * numbers::pi * scalar_type(4.5282e+09)) * hlsl::cos(hlsl::promote(2.2399e+06) * phase + shift) * hlsl::exp(hlsl::promote(-4.5282e+09) * phase2); + vector_type xyz = val * hlsl::sqrt(scalar_type(2.0) * numbers::pi * var) * hlsl::cos(pos * phase + shift) * hlsl::exp(-var * phase2); + xyz.x = xyz.x + scalar_type(9.7470e-14) * hlsl::sqrt(scalar_type(2.0) * numbers::pi * scalar_type(4.5282e+09)) * hlsl::cos(scalar_type(2.2399e+06) * phase[0] + shift[0]) * hlsl::exp(scalar_type(-4.5282e+09) * phase2[0]); return xyz / scalar_type(1.0685e-7); } T operator()() { // Force ior_2 -> 1.0 when Dinc -> 0.0 - scalar_type ior_2 = hlsl::mix(1.0, ior2, hlsl::smoothstep(0.0, 0.03, Dinc)); + vector_type ior_2 = ior2;//hlsl::mix(1.0, ior2, hlsl::smoothStep(0.0, 0.03, Dinc)); const vector_type wavelengths = vector_type(580.0, 550.0, 450.0); vector_type eta12 = ior_2/ior1; @@ -579,8 +580,8 @@ struct Iridescent } /* Optical Path Difference */ - const vector_type D = 2.0 * ior_2 * Dinc * cosTheta_2; - const vector_type Dphi = 2.0 * numbers::pi * D / wavelengths; + const vector_type D = hlsl::promote(2.0 * Dinc) * ior_2 * cosTheta_2; + const vector_type Dphi = hlsl::promote(2.0 * numbers::pi) * D / wavelengths; vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs, cosP, irid; vector_type I = hlsl::promote(0.0); @@ -588,8 +589,8 @@ struct Iridescent /* Evaluate the phase shift */ phase_shift(eta12, hlsl::promote(0.0), hlsl::promote(cosTheta_1), phi21p, phi21s); phase_shift(eta23, etak23, cosTheta_2, phi23p, phi23s); - phi21p = hlsl::promote(numbers::pi) - phi21p; - phi21s = hlsl::promote(numbers::pi) - phi21s; + phi21p = hlsl::promote(numbers::pi) - phi21p; + phi21s = hlsl::promote(numbers::pi) - phi21s; r123p = hlsl::sqrt(R12p*R23p); r123s = hlsl::sqrt(R12s*R23s); @@ -608,7 +609,7 @@ struct Iridescent NBL_UNROLL for (int m=1; m<=2; ++m) { Cm *= r123p; - Sm = 2.0 * evalSensitivity(m*D, m*(phi23p+phi21p)); + Sm = hlsl::promote(2.0) * evalSensitivity(hlsl::promote(m)*D, hlsl::promote(m)*(phi23p+phi21p)); I += Cm*Sm; } @@ -623,12 +624,16 @@ struct Iridescent NBL_UNROLL for (int m=1; m<=2; ++m) { Cm *= r123s; - Sm = 2.0 * evalSensitivity(m*D, m*(phi23s+phi21s)); + Sm = hlsl::promote(2.0) * evalSensitivity(hlsl::promote(m)*D, hlsl::promote(m) *(phi23s+phi21s)); I += Cm*Sm; } - // note: original paper used the CIE XYZ 1931 to RGB conversion - I = hlsl::mul(colorspace::decode::XYZtoscRGB, I); + const scalar_type r = 2.3646381 * I[0] - 0.8965361 * I[1] - 0.4680737 * I[2]; + const scalar_type g = -0.5151664 * I[0] + 1.4264000 * I[1] + 0.0887608 * I[2]; + const scalar_type b = 0.0052037 * I[0] - 0.0144081 * I[1] + 1.0092106 * I[2]; + I[0] = r; + I[1] = g; + I[2] = b; return hlsl::max(I, hlsl::promote(0.0)); } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl index ed049d3d1a..774f011021 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl @@ -8,6 +8,7 @@ #include "nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl" #include "nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl" #include "nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl" namespace nbl { diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl index 6d34d3d7ba..8103edb647 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl @@ -42,9 +42,9 @@ struct SIridescent { scalar_type A; scalar_type thickness; // thin-film thickness in nm - spectral_type ior0 - spectral_type ior1 - spectral_type ior2 + spectral_type ior0; + spectral_type ior1; + spectral_type ior2; spectral_type iork2; }; using creation_type = SCreationParams; @@ -195,6 +195,15 @@ struct SIridescent }; } + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + } } } From f205d4e92e98252aaea3aa029bffa935b3bc14c9 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 27 Aug 2025 15:32:55 +0700 Subject: [PATCH 04/25] replace loop for vector operations in fresnel --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 129 +++++++++------------ 1 file changed, 57 insertions(+), 72 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 26d80d4316..4c83d33ad5 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -368,23 +368,23 @@ struct Conductor } // TODO: will probably merge with __call at some point - static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, NBL_CONST_REF_ARG(T) orientedEtak, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) + static void __polarized(const T orientedEta, const T orientedEtak, const T cosTheta, NBL_REF_ARG(T) Rp, NBL_REF_ARG(T) Rs) { - scalar_type cosTheta_2 = cosTheta * cosTheta; - scalar_type sinTheta2 = scalar_type(1.0) - cosTheta_2; - const scalar_type eta = orientedEta[0]; - const scalar_type eta2 = eta*eta; - const scalar_type etak = orientedEtak[0]; - const scalar_type etak2 = etak*etak; + T cosTheta_2 = cosTheta * cosTheta; + T sinTheta2 = hlsl::promote(1.0) - cosTheta_2; + const T eta = orientedEta; + const T eta2 = eta*eta; + const T etak = orientedEtak; + const T etak2 = etak*etak; - const scalar_type etaLen2 = eta2 + etak2; - assert(etaLen2 > hlsl::exp2(-numeric_limits::digits)); - scalar_type t1 = etaLen2 * cosTheta_2; - const scalar_type etaCosTwice = eta * cosTheta * scalar_type(2.0); + const T etaLen2 = eta2 + etak2; + assert(hlsl::all(etaLen2 > hlsl::promote(hlsl::exp2(-numeric_limits::digits)))); + T t1 = etaLen2 * cosTheta_2; + const T etaCosTwice = eta * cosTheta * scalar_type(2.0); - const scalar_type rs_common = etaLen2 + cosTheta_2; + const T rs_common = etaLen2 + cosTheta_2; Rs = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); - const scalar_type rp_common = t1 + scalar_type(1.0); + const T rp_common = t1 + hlsl::promote(1.0); Rp = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); } @@ -428,22 +428,22 @@ struct Dielectric } // TODO: will probably merge with __call at some point - static void __polarized(NBL_CONST_REF_ARG(T) orientedEta, scalar_type cosTheta, NBL_REF_ARG(scalar_type) Rp, NBL_REF_ARG(scalar_type) Rs) + static void __polarized(const T orientedEta, const T cosTheta, NBL_REF_ARG(T) Rp, NBL_REF_ARG(T) Rs) { - scalar_type sinTheta2 = scalar_type(1.0) - cosTheta * cosTheta; - const scalar_type eta = orientedEta[0]; - const scalar_type eta2 = eta * eta; + T sinTheta2 = hlsl::promote(1.0) - cosTheta * cosTheta; + const T eta = orientedEta; + const T eta2 = eta * eta; - scalar_type t0 = hlsl::sqrt(eta2 - sinTheta2); - scalar_type t2 = eta2 * cosTheta; + T t0 = hlsl::sqrt(eta2 - sinTheta2); + T t2 = eta2 * cosTheta; - scalar_type rp = (t0 - t2) / (t0 + t2); + T rp = (t0 - t2) / (t0 + t2); Rp = rp * rp; - scalar_type rs = (cosTheta - t0) / (cosTheta + t0); + T rs = (cosTheta - t0) / (cosTheta + t0); Rs = rs * rs; } - static T __call(NBL_CONST_REF_ARG(T) orientedEta2, scalar_type absCosTheta) + static T __call(const T orientedEta2, scalar_type absCosTheta) { const scalar_type sinTheta2 = scalar_type(1.0) - absCosTheta * absCosTheta; @@ -496,7 +496,7 @@ struct Iridescent { using scalar_type = typename vector_traits::scalar_type; using monochrome_type = vector; - using vector_type = T; + using vector_type = T; // assert dim==3? // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, const vector_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) @@ -506,7 +506,7 @@ struct Iridescent const vector_type eta2 = orientedEta*orientedEta; const vector_type etak2 = orientedEtak*orientedEtak; - vector_type z = eta2 - etak2 - hlsl::promote(sinTheta2); + vector_type z = eta2 - etak2 - sinTheta2; vector_type w = hlsl::sqrt(z * z + scalar_type(4.0) * eta2 * eta2 * etak2); vector_type a2 = (z + w) * hlsl::promote(0.5); vector_type b2 = (w - z) * hlsl::promote(0.5); @@ -515,8 +515,8 @@ struct Iridescent const vector_type t0 = eta2 + etak2; const vector_type t1 = t0 * cosTheta_2; - phiS = hlsl::atan2(scalar_type(2.0) * b * cosTheta, a2 + b2 - cosTheta_2); - phiP = hlsl::atan2(scalar_type(2.0) * eta2 * cosTheta * (scalar_type(2.0) * orientedEtak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); + phiS = hlsl::atan2(hlsl::promote(2.0) * b * cosTheta, a2 + b2 - cosTheta_2); + phiP = hlsl::atan2(hlsl::promote(2.0) * eta2 * cosTheta * (hlsl::promote(2.0) * orientedEtak * hlsl::sqrt(a2) - etak2 * b), t1 - a2 + b2); } // Evaluation XYZ sensitivity curves in Fourier space @@ -535,58 +535,44 @@ struct Iridescent T operator()() { - // Force ior_2 -> 1.0 when Dinc -> 0.0 - vector_type ior_2 = ior2;//hlsl::mix(1.0, ior2, hlsl::smoothStep(0.0, 0.03, Dinc)); const vector_type wavelengths = vector_type(580.0, 550.0, 450.0); - vector_type eta12 = ior_2/ior1; - vector_type eta23 = ior3/ior_2; - vector_type etak23 = iork3/ior_2; + vector_type eta12 = ior2/ior1; + vector_type eta23 = ior3/ior2; + vector_type etak23 = iork3/ior2; scalar_type cosTheta_1 = cosTheta; vector_type cosTheta_2; - vector_type R12p, T121p, R23p, R12s, T121s, R23s, ct2; - NBL_UNROLL for(uint32_t i = 0; i < vector_traits::Dimension; i++) // TODO: could probably do calcs on vectors instead of loop - { - const scalar_type scale = ior1[i]/ior_2[i]; //(cosTheta_1 > 0) ? ior1[i]/ior_2[i] : ior_2[i]/ior1[i]; - const scalar_type cosThetaTSqr = 1 - (1-cosTheta_1*cosTheta_1) * scale * scale; - - /* Check for total internal reflection */ - if (cosThetaTSqr <= 0.0f) - { - R12s[i] = 1.0; - R12p[i] = 1.0; - - // Compute the transmission coefficients - T121p[i] = 0.0; - T121s[i] = 0.0; - } - else - { - cosTheta_2[i] = hlsl::sqrt(cosThetaTSqr); - Dielectric::__polarized(hlsl::promote(eta12[i]), cosTheta_1, R12p[i], R12s[i]); - - // Reflected part by the base - // if kappa==0, base material is dielectric - if (hlsl::all::Dimension> >(iork3 < hlsl::promote(hlsl::numeric_limits::min))) - Dielectric::__polarized(hlsl::promote(eta23[i]), cosTheta_2[i], R23p[i], R23s[i]); - else - Conductor::__polarized(hlsl::promote(eta23[i]), hlsl::promote(etak23[i]), cosTheta_2[i], R23p[i], R23s[i]); - - // Compute the transmission coefficients - T121p[i] = 1.0 - R12p[i]; - T121s[i] = 1.0 - R12s[i]; - } - } + vector_type R12p, R23p, R12s, R23s; + const vector_type scale = ior1/ior2; + const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(1-cosTheta_1*cosTheta_1) * scale * scale; + + cosTheta_2 = hlsl::sqrt(cosTheta2_2); + Dielectric::__polarized(eta12, hlsl::promote(cosTheta_1), R12p, R12s); + + // Reflected part by the base + // if kappa==0, base material is dielectric + if (hlsl::all::Dimension> >(iork3 < hlsl::promote(hlsl::numeric_limits::min))) + Dielectric::__polarized(eta23, cosTheta_2, R23p, R23s); + else + Conductor::__polarized(eta23, etak23, cosTheta_2, R23p, R23s); + + // Check for total internal reflection + R12s = hlsl::mix(R12s, hlsl::promote(1.0), cosTheta2_2 <= hlsl::promote(0.0)); + R12p = hlsl::mix(R12p, hlsl::promote(1.0), cosTheta2_2 <= hlsl::promote(0.0)); + + // Compute the transmission coefficients + vector_type T121p = hlsl::promote(1.0) - R12p; + vector_type T121s = hlsl::promote(1.0) - R12s; - /* Optical Path Difference */ - const vector_type D = hlsl::promote(2.0 * Dinc) * ior_2 * cosTheta_2; + // Optical Path Difference + const vector_type D = hlsl::promote(2.0 * Dinc) * ior2 * cosTheta_2; const vector_type Dphi = hlsl::promote(2.0 * numbers::pi) * D / wavelengths; - vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs, cosP, irid; + vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs; vector_type I = hlsl::promote(0.0); - /* Evaluate the phase shift */ + // Evaluate the phase shift phase_shift(eta12, hlsl::promote(0.0), hlsl::promote(cosTheta_1), phi21p, phi21s); phase_shift(eta23, etak23, cosTheta_2, phi23p, phi23s); phi21p = hlsl::promote(numbers::pi) - phi21p; @@ -598,7 +584,7 @@ struct Iridescent vector_type C0, Cm, Sm; const vector_type S0 = hlsl::promote(1.0); - /* Iridescence term using spectral antialiasing for Parallel polarization */ + // Iridescence term using spectral antialiasing // Reflectance term for m=0 (DC term amplitude) Rs = (T121p*T121p*R23p) / (hlsl::promote(1.0) - R12p*R23p); C0 = R12p + Rs; @@ -613,7 +599,6 @@ struct Iridescent I += Cm*Sm; } - /* Iridescence term using spectral antialiasing for Perpendicular polarization */ // Reflectance term for m=0 (DC term amplitude) Rs = (T121s*T121s*R23s) / (hlsl::promote(1.0) - R12s*R23s); C0 = R12s + Rs; @@ -634,11 +619,11 @@ struct Iridescent I[0] = r; I[1] = g; I[2] = b; - return hlsl::max(I, hlsl::promote(0.0)); + return hlsl::max(I, hlsl::promote(0.0)) * hlsl::promote(0.5); } scalar_type cosTheta; // LdotH - scalar_type Dinc; // thickness of thin film in nanometers, max 25000nm + scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm vector_type ior1; // usually air (1.0) vector_type ior2; // thin-film index vector_type ior3; // complex conductor index, k==0 makes dielectric From f6daa6fd50fdc41d0b85a1c3686f4b1eee2ab4ea Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 27 Aug 2025 16:27:07 +0700 Subject: [PATCH 05/25] added iridescent btdf --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 4 +- .../hlsl/bxdf/reflection/iridescent.hlsl | 4 +- .../nbl/builtin/hlsl/bxdf/transmission.hlsl | 1 + .../hlsl/bxdf/transmission/iridescent.hlsl | 212 ++++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 1 + 5 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 4c83d33ad5..fe9bd890ef 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -540,7 +540,7 @@ struct Iridescent vector_type eta12 = ior2/ior1; vector_type eta23 = ior3/ior2; vector_type etak23 = iork3/ior2; - scalar_type cosTheta_1 = cosTheta; + scalar_type cosTheta_1 = absCosTheta; vector_type cosTheta_2; vector_type R12p, R23p, R12s, R23s; @@ -622,7 +622,7 @@ struct Iridescent return hlsl::max(I, hlsl::promote(0.0)) * hlsl::promote(0.5); } - scalar_type cosTheta; // LdotH + scalar_type absCosTheta;// LdotH scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm vector_type ior1; // usually air (1.0) vector_type ior2; // thin-film index diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl index 8103edb647..c8f0631a03 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl @@ -114,7 +114,7 @@ struct SIridescent dualMeasure.maxNdotL = _sample.getNdotL(_clamp); scalar_type DG = dualMeasure.getProjectedLightMeasure(); fresnel_type f = __base.getFresnel(); - f.cosTheta = cache.getLdotH(); + f.absCosTheta = cache.getLdotH(); return f() * DG; } else @@ -183,7 +183,7 @@ struct SIridescent const scalar_type G2_over_G1 = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); fresnel_type f = __base.getFresnel(); - f.cosTheta = cache.getLdotH(); + f.absCosTheta = cache.getLdotH(); const spectral_type reflectance = f(); quo = reflectance * G2_over_G1; } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl index 81f531e1a6..8fe66cefa1 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl @@ -8,6 +8,7 @@ #include "nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl" namespace nbl { diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl new file mode 100644 index 0000000000..aa2fde373e --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl @@ -0,0 +1,212 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_IRIDESCENT_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_IRIDESCENT_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template) +struct SIridescent +{ + using this_t = SIridescent; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(matrix3x3_type, Config); + NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + using brdf_type = reflection::SGGXIsotropic; + + using ndf_type = ndf::GGX; + using fresnel_type = fresnel::Iridescent; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; + + struct SCreationParams + { + scalar_type A; + scalar_type thickness; // thin-film thickness in nm + spectral_type ior0; + spectral_type ior1; + spectral_type ior2; + }; + using creation_type = SCreationParams; + + struct SIridQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + + scalar_type devsh_v; + scalar_type devsh_l; + }; + using query_type = SIridQuery; + + static this_t create(scalar_type A, scalar_type thickness, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1, NBL_CONST_REF_ARG(spectral_type) ior2) + { + this_t retval; + retval.__base.ndf.A = vector2_type(A, A); + retval.__base.ndf.a2 = A*A; + retval.__base.ndf.one_minus_a2 = scalar_type(1.0) - A*A; + retval.__base.fresnel.Dinc = thickness; + retval.__base.fresnel.ior1 = ior0; + retval.__base.fresnel.ior2 = ior1; + retval.__base.fresnel.ior3 = ior2; + retval.__base.fresnel.iork3 = hlsl::promote(0.0); // always 0.0 for dielectric base layer + return retval; + } + static this_t create(NBL_CONST_REF_ARG(creation_type) params) + { + return create(params.A, params.thickness, params.ior0, params.ior1, params.ior2); + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + query_type query; + ndf_type ggx_ndf = __base.getNDF(); + query.devsh_v = ggx_ndf.devsh_part(interaction.getNdotV2()); + query.devsh_l = ggx_ndf.devsh_part(_sample.getNdotL2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.absNdotL = _sample.getNdotL(_clamp); + dualMeasure.orientedEta = orientedEta.value[0]; + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getLdotH()); + return hlsl::promote(f()[0]) * DG; + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(isocache_type) cache) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); + SGGXDielectricAnisotropic ggx_aniso = SGGXDielectricAnisotropic::create(orientedEta, __base.ndf.A.x, __base.ndf.A.y); + anisocache_type anisocache; + sample_type s = ggx_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SGGXDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } + scalar_type getOrientedEta() NBL_CONST_MEMBER_FUNC { return orientedEta; } + + scalar_type ndf; + scalar_type G1_over_2NdotV; + scalar_type orientedEta; + }; + + SGGXDG1Query dg1_query; + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); + dg1_query.orientedEta = orientedEta.value[0]; + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getLdotH()); + const scalar_type reflectance = f()[0]; + + ndf_type ggx_ndf = __base.getNDF(); + dg1_query.ndf = __base.__D(cache); + dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), query.getDevshV()); + + measure_transform_type dualMeasure = __base.template __DG1(dg1_query, cache); + return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * dualMeasure.getMicrofacetMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + const bool transmitted = cache.isTransmission(); + + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + ndf_type ggx_ndf = __base.getNDF(); + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + scalar_type quo; + quo = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +} + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +} +} +} + +#endif diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index fcc79d3714..91b0b411e8 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -352,6 +352,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/beckmann.hl LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/ggx.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/lambertian.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/smooth_dielectric.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/iridescent.hlsl") #subgroup LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/subgroup/ballot.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/subgroup/basic.hlsl") From 09402ea10d5947f34f713f24786bc22ac093205d Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 27 Aug 2025 16:29:52 +0700 Subject: [PATCH 06/25] added unit tests --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index fe3ca26794..ea439349dc 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit fe3ca267946d7f4d0fbe4cff9c437b2e62637da9 +Subproject commit ea439349dc9461f4d18eab143fe705e4cbf10c81 From aa0f6e85a16f66585ecc8c0d42228c83b2a9251a Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 28 Aug 2025 10:43:47 +0700 Subject: [PATCH 07/25] latest example --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index ceb40d10fa..91658e3f07 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit ceb40d10fa5ee578b9a10fa1111eeae54c670db7 +Subproject commit 91658e3f0737d192cafba9e073c5d67cc6eaaac6 From d2cb193b2cc25aa5abe82943b36648892eb7e724 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 28 Aug 2025 15:50:22 +0700 Subject: [PATCH 08/25] moved colorspace transform mats into struct functions --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 12 +- include/nbl/builtin/hlsl/colorspace.hlsl | 198 ++++++++++++++++++ .../builtin/hlsl/colorspace/decodeCIEXYZ.hlsl | 79 ------- .../builtin/hlsl/colorspace/encodeCIEXYZ.hlsl | 83 -------- src/nbl/builtin/CMakeLists.txt | 3 +- 5 files changed, 202 insertions(+), 173 deletions(-) create mode 100644 include/nbl/builtin/hlsl/colorspace.hlsl delete mode 100644 include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl delete mode 100644 include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index fe9bd890ef..7fde50ab95 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -10,7 +10,7 @@ #include "nbl/builtin/hlsl/numbers.hlsl" #include "nbl/builtin/hlsl/complex.hlsl" #include "nbl/builtin/hlsl/tgmath.hlsl" -#include "nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl" +#include "nbl/builtin/hlsl/colorspace.hlsl" #include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" namespace nbl @@ -535,7 +535,7 @@ struct Iridescent T operator()() { - const vector_type wavelengths = vector_type(580.0, 550.0, 450.0); + const vector_type wavelengths = vector_type(colorspace::scRGB::wavelength_R, colorspace::scRGB::wavelength_G, colorspace::scRGB::wavelength_B); vector_type eta12 = ior2/ior1; vector_type eta23 = ior3/ior2; @@ -613,13 +613,7 @@ struct Iridescent I += Cm*Sm; } - const scalar_type r = 2.3646381 * I[0] - 0.8965361 * I[1] - 0.4680737 * I[2]; - const scalar_type g = -0.5151664 * I[0] + 1.4264000 * I[1] + 0.0887608 * I[2]; - const scalar_type b = 0.0052037 * I[0] - 0.0144081 * I[1] + 1.0092106 * I[2]; - I[0] = r; - I[1] = g; - I[2] = b; - return hlsl::max(I, hlsl::promote(0.0)) * hlsl::promote(0.5); + return hlsl::max(colorspace::scRGB::FromXYZ(I), hlsl::promote(0.0)) * hlsl::promote(0.5); } scalar_type absCosTheta;// LdotH diff --git a/include/nbl/builtin/hlsl/colorspace.hlsl b/include/nbl/builtin/hlsl/colorspace.hlsl new file mode 100644 index 0000000000..cadc29fa67 --- /dev/null +++ b/include/nbl/builtin/hlsl/colorspace.hlsl @@ -0,0 +1,198 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_COLOR_SPACE_COLORSPACE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_COLOR_SPACE_COLORSPACE_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace colorspace +{ + +struct colorspace_base +{ + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 580.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 550.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 450.0f; +}; + +struct scRGB : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 3.240970f, -1.537383f, -0.498611f), + float32_t3(-0.969244f, 1.875968f, 0.041555f), + float32_t3( 0.055630f, -0.203977f, 1.056972f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(0.412391f, 0.357584f, 0.180481f), + float32_t3(0.212639f, 0.715169f, 0.072192f), + float32_t3(0.019331f, 0.119195f, 0.950532f) + ); + return hlsl::mul(mat, val); + } +}; + +struct sRGB : scRGB {}; +struct BT709 : scRGB {}; + +struct Display_P3 : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 2.4934969119f, -0.9313836179f, -0.4027107845f), + float32_t3(-0.8294889696f, 1.7626640603f, 0.0236246858f), + float32_t3( 0.0358458302f, -0.0761723893f, 0.9568845240f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(0.4865709486f, 0.2656676932f, 0.1982172852f), + float32_t3(0.2289745641f, 0.6917385218f, 0.0792869141f), + float32_t3(0.0000000000f, 0.0451133819f, 1.0439443689f) + ); + return hlsl::mul(mat, val); + } +}; + +struct DCI_P3 : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(1.0f, 0.0f, 0.0f), + float32_t3(0.0f, 1.0f, 0.0f), + float32_t3(0.0f, 0.0f, 1.0f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(1.0f, 0.0f, 0.0f), + float32_t3(0.0f, 1.0f, 0.0f), + float32_t3(0.0f, 0.0f, 1.0f) + ); + return hlsl::mul(mat, val); + } +}; + +struct BT2020 : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 1.716651f, -0.355671f, -0.253366f), + float32_t3(-0.666684f, 1.616481f, 0.015769f), + float32_t3( 0.017640f, -0.042771f, 0.942103f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(0.636958f, 0.144617f, 0.168881f), + float32_t3(0.262700f, 0.677998f, 0.059302f), + float32_t3(0.000000f, 0.028073f, 1.060985f) + ); + return hlsl::mul(mat, val); + } +}; + +struct HDR10_ST2084 : BT2020 {}; +struct DOLBYIVISION : BT2020 {}; +struct HDR10_HLG : BT2020 {}; + +struct AdobeRGB : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 2.0415879038f, -0.5650069743f, -0.3447313508f), + float32_t3(-0.9692436363f, 1.8759675015f, 0.0415550574f), + float32_t3( 0.0134442806f, -0.1183623922f, 1.0151749944f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(0.5766690429f, 0.1855582379f, 0.1882286462f), + float32_t3(0.2973449753f, 0.6273635663f, 0.0752914585f), + float32_t3(0.0270313614f, 0.0706888525f, 0.9913375368f) + ); + return hlsl::mul(mat, val); + } +}; + +struct ACES2065_1 : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 1.0498110175f, 0.0000000000f, -0.0000974845f), + float32_t3(-0.4959030231f, 1.3733130458f, 0.0982400361f), + float32_t3( 0.0000000000f, 0.0000000000f, 0.9912520182f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3(0.9525523959f, 0.0000000000f, 0.0000936786f), + float32_t3(0.3439664498f, 0.7281660966f, -0.0721325464f), + float32_t3(0.0000000000f, 0.0000000000f, 1.0088251844f) + ); + return hlsl::mul(mat, val); + } +}; + +struct ACEScc : colorspace_base +{ + static float32_t3 FromXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 1.6410233797f, -0.3248032942f, -0.2364246952f), + float32_t3(-0.6636628587f, 1.6153315917f, 0.0167563477f), + float32_t3( 0.0117218943f, -0.0082844420f, 0.9883948585f) + ); + return hlsl::mul(mat, val); + } + + static float32_t3 ToXYZ(float32_t3 val) + { + const float32_t3x3 mat = float32_t3x3( + float32_t3( 0.6624541811f, 0.1340042065f, 0.1561876870f), + float32_t3( 0.2722287168f, 0.6740817658f, 0.0536895174f), + float32_t3(-0.0055746495f, 0.0040607335f, 1.0103391003f) + ); + return hlsl::mul(mat, val); + } +}; + +struct ACEScct : ACEScc {}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl b/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl deleted file mode 100644 index 29a93124d9..0000000000 --- a/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl +++ /dev/null @@ -1,79 +0,0 @@ - -// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef _NBL_BUILTIN_HLSL_COLOR_SPACE_DECODE_CIE_XYZ_INCLUDED_ -#define _NBL_BUILTIN_HLSL_COLOR_SPACE_DECODE_CIE_XYZ_INCLUDED_ - -#include - -namespace nbl -{ -namespace hlsl -{ -namespace colorspace -{ -namespace decode -{ - -NBL_CONSTEXPR float32_t3x3 XYZtoscRGB = float32_t3x3( - float32_t3( 3.240970f, -1.537383f, -0.498611f), - float32_t3(-0.969244f, 1.875968f, 0.041555f), - float32_t3( 0.055630f, -0.203977f, 1.056972f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtosRGB = XYZtoscRGB; -NBL_CONSTEXPR float32_t3x3 XYZtoBT709 = XYZtoscRGB; - - -NBL_CONSTEXPR float32_t3x3 XYZtoDisplay_P3 = float32_t3x3( - float32_t3( 2.4934969119f, -0.9313836179f, -0.4027107845f), - float32_t3(-0.8294889696f, 1.7626640603f, 0.0236246858f), - float32_t3( 0.0358458302f, -0.0761723893f, 0.9568845240f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtoDCI_P3 = float32_t3x3( - float32_t3(1.0f, 0.0f, 0.0f), - float32_t3(0.0f, 1.0f, 0.0f), - float32_t3(0.0f, 0.0f, 1.0f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtoBT2020 = float32_t3x3( - float32_t3( 1.716651f, -0.355671f, -0.253366f), - float32_t3(-0.666684f, 1.616481f, 0.015769f), - float32_t3( 0.017640f, -0.042771f, 0.942103f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtoHDR10_ST2084 = XYZtoBT2020; -NBL_CONSTEXPR float32_t3x3 XYZtoDOLBYIVISION = XYZtoBT2020; -NBL_CONSTEXPR float32_t3x3 XYZtoHDR10_HLG = XYZtoBT2020; - -NBL_CONSTEXPR float32_t3x3 XYZtoAdobeRGB = float32_t3x3( - float32_t3( 2.0415879038f, -0.5650069743f, -0.3447313508f), - float32_t3(-0.9692436363f, 1.8759675015f, 0.0415550574f), - float32_t3( 0.0134442806f, -0.1183623922f, 1.0151749944f) -); - - -NBL_CONSTEXPR float32_t3x3 XYZtoACES2065_1 = float32_t3x3( - float32_t3( 1.0498110175f, 0.0000000000f, -0.0000974845f), - float32_t3(-0.4959030231f, 1.3733130458f, 0.0982400361f), - float32_t3( 0.0000000000f, 0.0000000000f, 0.9912520182f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtoACEScc = float32_t3x3( - float32_t3( 1.6410233797f, -0.3248032942f, -0.2364246952f), - float32_t3(-0.6636628587f, 1.6153315917f, 0.0167563477f), - float32_t3( 0.0117218943f, -0.0082844420f, 0.9883948585f) -); - -NBL_CONSTEXPR float32_t3x3 XYZtoACEScct = XYZtoACEScc; - -} -} -} -} - - -#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl b/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl deleted file mode 100644 index cc8ac317b7..0000000000 --- a/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl +++ /dev/null @@ -1,83 +0,0 @@ - - -// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. -// This file is part of the "Nabla Engine". -// For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef _NBL_BUILTIN_HLSL_COLOR_SPACE_ENCODE_CIE_XYZ_INCLUDED_ -#define _NBL_BUILTIN_HLSL_COLOR_SPACE_ENCODE_CIE_XYZ_INCLUDED_ - -#include - -namespace nbl -{ -namespace hlsl -{ -namespace colorspace -{ - -NBL_CONSTEXPR float32_t3x3 scRGBtoXYZ = float32_t3x3( - float32_t3(0.412391f, 0.357584f, 0.180481f), - float32_t3(0.212639f, 0.715169f, 0.072192f), - float32_t3(0.019331f, 0.119195f, 0.950532f) -); - -NBL_CONSTEXPR float32_t3x3 sRGBtoXYZ = scRGBtoXYZ; - -NBL_CONSTEXPR float32_t3x3 BT709toXYZ = scRGBtoXYZ; - - -NBL_CONSTEXPR float32_t3x3 Display_P3toXYZ = float32_t3x3( - float32_t3(0.4865709486f, 0.2656676932f, 0.1982172852f), - float32_t3(0.2289745641f, 0.6917385218f, 0.0792869141f), - float32_t3(0.0000000000f, 0.0451133819f, 1.0439443689f) -); - - -NBL_CONSTEXPR float32_t3x3 DCI_P3toXYZ = float32_t3x3( - float32_t3(1.0f, 0.0f, 0.0f), - float32_t3(0.0f, 1.0f, 0.0f), - float32_t3(0.0f, 0.0f, 1.0f) -); - - -NBL_CONSTEXPR float32_t3x3 BT2020toXYZ = float32_t3x3( - float32_t3(0.636958f, 0.144617f, 0.168881f), - float32_t3(0.262700f, 0.677998f, 0.059302f), - float32_t3(0.000000f, 0.028073f, 1.060985f) -); - -NBL_CONSTEXPR float32_t3x3 HDR10_ST2084toXYZ = BT2020toXYZ; - -NBL_CONSTEXPR float32_t3x3 DOLBYIVISIONtoXYZ = BT2020toXYZ; - -NBL_CONSTEXPR float32_t3x3 HDR10_HLGtoXYZ = BT2020toXYZ; - - -NBL_CONSTEXPR float32_t3x3 AdobeRGBtoXYZ = float32_t3x3( - float32_t3(0.5766690429f, 0.1855582379f, 0.1882286462f), - float32_t3(0.2973449753f, 0.6273635663f, 0.0752914585f), - float32_t3(0.0270313614f, 0.0706888525f, 0.9913375368f) -); - - -NBL_CONSTEXPR float32_t3x3 ACES2065_1toXYZ = float32_t3x3( - float32_t3(0.9525523959f, 0.0000000000f, 0.0000936786f), - float32_t3(0.3439664498f, 0.7281660966f, -0.0721325464f), - float32_t3(0.0000000000f, 0.0000000000f, 1.0088251844f) -); - - -NBL_CONSTEXPR float32_t3x3 ACEScctoXYZ = float32_t3x3( - float32_t3( 0.6624541811f, 0.1340042065f, 0.1561876870f), - float32_t3( 0.2722287168f, 0.6740817658f, 0.0536895174f), - float32_t3(-0.0055746495f, 0.0040607335f, 1.0103391003f) -); - -NBL_CONSTEXPR float32_t3x3 ACESccttoXYZ = ACEScctoXYZ; - -} -} -} - -#endif \ No newline at end of file diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 91b0b411e8..8f0f90cf48 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -307,8 +307,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/acceleration_structures.hlsl" #colorspace LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/EOTF.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/OETF.hlsl") -LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/decodeCIEXYZ.hlsl") -LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/encodeCIEXYZ.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace.hlsl") #barycentrics LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/barycentric/utils.hlsl") #scanning append From 33fc955d716b3322985abe39f269634cae651674 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 28 Aug 2025 16:22:22 +0700 Subject: [PATCH 09/25] some more colorspace utility --- examples_tests | 2 +- include/nbl/builtin/hlsl/colorspace.hlsl | 84 ++++++++++++------------ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/examples_tests b/examples_tests index 91658e3f07..99206836d6 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 91658e3f0737d192cafba9e073c5d67cc6eaaac6 +Subproject commit 99206836d6afd6fe55735b88f4a576a0b52e10a2 diff --git a/include/nbl/builtin/hlsl/colorspace.hlsl b/include/nbl/builtin/hlsl/colorspace.hlsl index cadc29fa67..f6b5d76ae8 100644 --- a/include/nbl/builtin/hlsl/colorspace.hlsl +++ b/include/nbl/builtin/hlsl/colorspace.hlsl @@ -23,25 +23,25 @@ struct colorspace_base struct scRGB : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 3.240970f, -1.537383f, -0.498611f), float32_t3(-0.969244f, 1.875968f, 0.041555f), float32_t3( 0.055630f, -0.203977f, 1.056972f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(0.412391f, 0.357584f, 0.180481f), float32_t3(0.212639f, 0.715169f, 0.072192f), float32_t3(0.019331f, 0.119195f, 0.950532f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct sRGB : scRGB {}; @@ -49,71 +49,71 @@ struct BT709 : scRGB {}; struct Display_P3 : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 2.4934969119f, -0.9313836179f, -0.4027107845f), float32_t3(-0.8294889696f, 1.7626640603f, 0.0236246858f), float32_t3( 0.0358458302f, -0.0761723893f, 0.9568845240f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(0.4865709486f, 0.2656676932f, 0.1982172852f), float32_t3(0.2289745641f, 0.6917385218f, 0.0792869141f), float32_t3(0.0000000000f, 0.0451133819f, 1.0439443689f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct DCI_P3 : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(1.0f, 0.0f, 0.0f), float32_t3(0.0f, 1.0f, 0.0f), float32_t3(0.0f, 0.0f, 1.0f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(1.0f, 0.0f, 0.0f), float32_t3(0.0f, 1.0f, 0.0f), float32_t3(0.0f, 0.0f, 1.0f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct BT2020 : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 1.716651f, -0.355671f, -0.253366f), float32_t3(-0.666684f, 1.616481f, 0.015769f), float32_t3( 0.017640f, -0.042771f, 0.942103f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(0.636958f, 0.144617f, 0.168881f), float32_t3(0.262700f, 0.677998f, 0.059302f), float32_t3(0.000000f, 0.028073f, 1.060985f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct HDR10_ST2084 : BT2020 {}; @@ -122,71 +122,71 @@ struct HDR10_HLG : BT2020 {}; struct AdobeRGB : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 2.0415879038f, -0.5650069743f, -0.3447313508f), float32_t3(-0.9692436363f, 1.8759675015f, 0.0415550574f), float32_t3( 0.0134442806f, -0.1183623922f, 1.0151749944f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(0.5766690429f, 0.1855582379f, 0.1882286462f), float32_t3(0.2973449753f, 0.6273635663f, 0.0752914585f), float32_t3(0.0270313614f, 0.0706888525f, 0.9913375368f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct ACES2065_1 : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 1.0498110175f, 0.0000000000f, -0.0000974845f), float32_t3(-0.4959030231f, 1.3733130458f, 0.0982400361f), float32_t3( 0.0000000000f, 0.0000000000f, 0.9912520182f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3(0.9525523959f, 0.0000000000f, 0.0000936786f), float32_t3(0.3439664498f, 0.7281660966f, -0.0721325464f), float32_t3(0.0000000000f, 0.0000000000f, 1.0088251844f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct ACEScc : colorspace_base { - static float32_t3 FromXYZ(float32_t3 val) + static float32_t3x3 FromXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 1.6410233797f, -0.3248032942f, -0.2364246952f), float32_t3(-0.6636628587f, 1.6153315917f, 0.0167563477f), float32_t3( 0.0117218943f, -0.0082844420f, 0.9883948585f) ); - return hlsl::mul(mat, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } - static float32_t3 ToXYZ(float32_t3 val) + static float32_t3x3 ToXYZ() { - const float32_t3x3 mat = float32_t3x3( + return float32_t3x3( float32_t3( 0.6624541811f, 0.1340042065f, 0.1561876870f), float32_t3( 0.2722287168f, 0.6740817658f, 0.0536895174f), float32_t3(-0.0055746495f, 0.0040607335f, 1.0103391003f) ); - return hlsl::mul(mat, val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } }; struct ACEScct : ACEScc {}; From d8b67737a16b332539fb91a26a1eb21e49a6a445 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 3 Nov 2025 11:13:49 +0700 Subject: [PATCH 10/25] restore encode/decode matrices --- .../builtin/hlsl/colorspace/decodeCIEXYZ.hlsl | 79 ++++++++++++++++++ .../builtin/hlsl/colorspace/encodeCIEXYZ.hlsl | 83 +++++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 2 + 3 files changed, 164 insertions(+) create mode 100644 include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl create mode 100644 include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl diff --git a/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl b/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl new file mode 100644 index 0000000000..29a93124d9 --- /dev/null +++ b/include/nbl/builtin/hlsl/colorspace/decodeCIEXYZ.hlsl @@ -0,0 +1,79 @@ + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_COLOR_SPACE_DECODE_CIE_XYZ_INCLUDED_ +#define _NBL_BUILTIN_HLSL_COLOR_SPACE_DECODE_CIE_XYZ_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace colorspace +{ +namespace decode +{ + +NBL_CONSTEXPR float32_t3x3 XYZtoscRGB = float32_t3x3( + float32_t3( 3.240970f, -1.537383f, -0.498611f), + float32_t3(-0.969244f, 1.875968f, 0.041555f), + float32_t3( 0.055630f, -0.203977f, 1.056972f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtosRGB = XYZtoscRGB; +NBL_CONSTEXPR float32_t3x3 XYZtoBT709 = XYZtoscRGB; + + +NBL_CONSTEXPR float32_t3x3 XYZtoDisplay_P3 = float32_t3x3( + float32_t3( 2.4934969119f, -0.9313836179f, -0.4027107845f), + float32_t3(-0.8294889696f, 1.7626640603f, 0.0236246858f), + float32_t3( 0.0358458302f, -0.0761723893f, 0.9568845240f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtoDCI_P3 = float32_t3x3( + float32_t3(1.0f, 0.0f, 0.0f), + float32_t3(0.0f, 1.0f, 0.0f), + float32_t3(0.0f, 0.0f, 1.0f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtoBT2020 = float32_t3x3( + float32_t3( 1.716651f, -0.355671f, -0.253366f), + float32_t3(-0.666684f, 1.616481f, 0.015769f), + float32_t3( 0.017640f, -0.042771f, 0.942103f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtoHDR10_ST2084 = XYZtoBT2020; +NBL_CONSTEXPR float32_t3x3 XYZtoDOLBYIVISION = XYZtoBT2020; +NBL_CONSTEXPR float32_t3x3 XYZtoHDR10_HLG = XYZtoBT2020; + +NBL_CONSTEXPR float32_t3x3 XYZtoAdobeRGB = float32_t3x3( + float32_t3( 2.0415879038f, -0.5650069743f, -0.3447313508f), + float32_t3(-0.9692436363f, 1.8759675015f, 0.0415550574f), + float32_t3( 0.0134442806f, -0.1183623922f, 1.0151749944f) +); + + +NBL_CONSTEXPR float32_t3x3 XYZtoACES2065_1 = float32_t3x3( + float32_t3( 1.0498110175f, 0.0000000000f, -0.0000974845f), + float32_t3(-0.4959030231f, 1.3733130458f, 0.0982400361f), + float32_t3( 0.0000000000f, 0.0000000000f, 0.9912520182f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtoACEScc = float32_t3x3( + float32_t3( 1.6410233797f, -0.3248032942f, -0.2364246952f), + float32_t3(-0.6636628587f, 1.6153315917f, 0.0167563477f), + float32_t3( 0.0117218943f, -0.0082844420f, 0.9883948585f) +); + +NBL_CONSTEXPR float32_t3x3 XYZtoACEScct = XYZtoACEScc; + +} +} +} +} + + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl b/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl new file mode 100644 index 0000000000..cc8ac317b7 --- /dev/null +++ b/include/nbl/builtin/hlsl/colorspace/encodeCIEXYZ.hlsl @@ -0,0 +1,83 @@ + + +// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h + +#ifndef _NBL_BUILTIN_HLSL_COLOR_SPACE_ENCODE_CIE_XYZ_INCLUDED_ +#define _NBL_BUILTIN_HLSL_COLOR_SPACE_ENCODE_CIE_XYZ_INCLUDED_ + +#include + +namespace nbl +{ +namespace hlsl +{ +namespace colorspace +{ + +NBL_CONSTEXPR float32_t3x3 scRGBtoXYZ = float32_t3x3( + float32_t3(0.412391f, 0.357584f, 0.180481f), + float32_t3(0.212639f, 0.715169f, 0.072192f), + float32_t3(0.019331f, 0.119195f, 0.950532f) +); + +NBL_CONSTEXPR float32_t3x3 sRGBtoXYZ = scRGBtoXYZ; + +NBL_CONSTEXPR float32_t3x3 BT709toXYZ = scRGBtoXYZ; + + +NBL_CONSTEXPR float32_t3x3 Display_P3toXYZ = float32_t3x3( + float32_t3(0.4865709486f, 0.2656676932f, 0.1982172852f), + float32_t3(0.2289745641f, 0.6917385218f, 0.0792869141f), + float32_t3(0.0000000000f, 0.0451133819f, 1.0439443689f) +); + + +NBL_CONSTEXPR float32_t3x3 DCI_P3toXYZ = float32_t3x3( + float32_t3(1.0f, 0.0f, 0.0f), + float32_t3(0.0f, 1.0f, 0.0f), + float32_t3(0.0f, 0.0f, 1.0f) +); + + +NBL_CONSTEXPR float32_t3x3 BT2020toXYZ = float32_t3x3( + float32_t3(0.636958f, 0.144617f, 0.168881f), + float32_t3(0.262700f, 0.677998f, 0.059302f), + float32_t3(0.000000f, 0.028073f, 1.060985f) +); + +NBL_CONSTEXPR float32_t3x3 HDR10_ST2084toXYZ = BT2020toXYZ; + +NBL_CONSTEXPR float32_t3x3 DOLBYIVISIONtoXYZ = BT2020toXYZ; + +NBL_CONSTEXPR float32_t3x3 HDR10_HLGtoXYZ = BT2020toXYZ; + + +NBL_CONSTEXPR float32_t3x3 AdobeRGBtoXYZ = float32_t3x3( + float32_t3(0.5766690429f, 0.1855582379f, 0.1882286462f), + float32_t3(0.2973449753f, 0.6273635663f, 0.0752914585f), + float32_t3(0.0270313614f, 0.0706888525f, 0.9913375368f) +); + + +NBL_CONSTEXPR float32_t3x3 ACES2065_1toXYZ = float32_t3x3( + float32_t3(0.9525523959f, 0.0000000000f, 0.0000936786f), + float32_t3(0.3439664498f, 0.7281660966f, -0.0721325464f), + float32_t3(0.0000000000f, 0.0000000000f, 1.0088251844f) +); + + +NBL_CONSTEXPR float32_t3x3 ACEScctoXYZ = float32_t3x3( + float32_t3( 0.6624541811f, 0.1340042065f, 0.1561876870f), + float32_t3( 0.2722287168f, 0.6740817658f, 0.0536895174f), + float32_t3(-0.0055746495f, 0.0040607335f, 1.0103391003f) +); + +NBL_CONSTEXPR float32_t3x3 ACESccttoXYZ = ACEScctoXYZ; + +} +} +} + +#endif \ No newline at end of file diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 6e4c6f5a56..83aef9f3dd 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -236,6 +236,8 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/acceleration_structures.hlsl" #colorspace LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/EOTF.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/OETF.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/decodeCIEXYZ.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace/encodeCIEXYZ.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/colorspace.hlsl") #barycentrics LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/barycentric/utils.hlsl") From 65239d923961a9d3ec0bcd3cbc91609c761bb9b2 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 3 Nov 2025 14:50:28 +0700 Subject: [PATCH 11/25] colorspace specific dominant wavelengths (still missing ACES), use global conversion matrices --- include/nbl/builtin/hlsl/colorspace.hlsl | 141 +++++++++-------------- 1 file changed, 54 insertions(+), 87 deletions(-) diff --git a/include/nbl/builtin/hlsl/colorspace.hlsl b/include/nbl/builtin/hlsl/colorspace.hlsl index f6b5d76ae8..78186066df 100644 --- a/include/nbl/builtin/hlsl/colorspace.hlsl +++ b/include/nbl/builtin/hlsl/colorspace.hlsl @@ -6,6 +6,8 @@ #define _NBL_BUILTIN_HLSL_COLOR_SPACE_COLORSPACE_INCLUDED_ #include +#include +#include namespace nbl { @@ -16,32 +18,29 @@ namespace colorspace struct colorspace_base { - NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 580.0f; - NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 550.0f; - NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 450.0f; + // default CIE RGB primaries wavelengths + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 700.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 546.1f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 435.8f; }; struct scRGB : colorspace_base { + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 611.4f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 549.1f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 464.2f; + static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 3.240970f, -1.537383f, -0.498611f), - float32_t3(-0.969244f, 1.875968f, 0.041555f), - float32_t3( 0.055630f, -0.203977f, 1.056972f) - ); + return XYZtoscRGB; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoscRGB, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(0.412391f, 0.357584f, 0.180481f), - float32_t3(0.212639f, 0.715169f, 0.072192f), - float32_t3(0.019331f, 0.119195f, 0.950532f) - ); + return scRGBtoXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(scRGBtoXYZ, val); } }; struct sRGB : scRGB {}; @@ -49,71 +48,59 @@ struct BT709 : scRGB {}; struct Display_P3 : colorspace_base { + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 614.9f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 544.2f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 464.2f; + static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 2.4934969119f, -0.9313836179f, -0.4027107845f), - float32_t3(-0.8294889696f, 1.7626640603f, 0.0236246858f), - float32_t3( 0.0358458302f, -0.0761723893f, 0.9568845240f) - ); + return XYZtoDisplay_P3; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoDisplay_P3, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(0.4865709486f, 0.2656676932f, 0.1982172852f), - float32_t3(0.2289745641f, 0.6917385218f, 0.0792869141f), - float32_t3(0.0000000000f, 0.0451133819f, 1.0439443689f) - ); + return Display_P3toXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(Display_P3toXYZ, val); } }; struct DCI_P3 : colorspace_base { + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 614.9f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 544.2f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 464.2f; + static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3(1.0f, 0.0f, 0.0f), - float32_t3(0.0f, 1.0f, 0.0f), - float32_t3(0.0f, 0.0f, 1.0f) - ); + return XYZtoDCI_P3; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoDCI_P3, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(1.0f, 0.0f, 0.0f), - float32_t3(0.0f, 1.0f, 0.0f), - float32_t3(0.0f, 0.0f, 1.0f) - ); + return DCI_P3toXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(DCI_P3toXYZ, val); } }; struct BT2020 : colorspace_base { + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 630.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 532.0f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 467.0f; + static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 1.716651f, -0.355671f, -0.253366f), - float32_t3(-0.666684f, 1.616481f, 0.015769f), - float32_t3( 0.017640f, -0.042771f, 0.942103f) - ); + return XYZtoBT2020; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoBT2020, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(0.636958f, 0.144617f, 0.168881f), - float32_t3(0.262700f, 0.677998f, 0.059302f), - float32_t3(0.000000f, 0.028073f, 1.060985f) - ); + return BT2020toXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(BT2020toXYZ, val); } }; struct HDR10_ST2084 : BT2020 {}; @@ -122,71 +109,51 @@ struct HDR10_HLG : BT2020 {}; struct AdobeRGB : colorspace_base { + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_R = 611.4f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_G = 534.7f; + NBL_CONSTEXPR_STATIC_INLINE float32_t wavelength_B = 464.2f; + static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 2.0415879038f, -0.5650069743f, -0.3447313508f), - float32_t3(-0.9692436363f, 1.8759675015f, 0.0415550574f), - float32_t3( 0.0134442806f, -0.1183623922f, 1.0151749944f) - ); + return XYZtoAdobeRGB; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoAdobeRGB, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(0.5766690429f, 0.1855582379f, 0.1882286462f), - float32_t3(0.2973449753f, 0.6273635663f, 0.0752914585f), - float32_t3(0.0270313614f, 0.0706888525f, 0.9913375368f) - ); + return AdobeRGBtoXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(AdobeRGBtoXYZ, val); } }; struct ACES2065_1 : colorspace_base { static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 1.0498110175f, 0.0000000000f, -0.0000974845f), - float32_t3(-0.4959030231f, 1.3733130458f, 0.0982400361f), - float32_t3( 0.0000000000f, 0.0000000000f, 0.9912520182f) - ); + return XYZtoACES2065_1; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoACES2065_1, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3(0.9525523959f, 0.0000000000f, 0.0000936786f), - float32_t3(0.3439664498f, 0.7281660966f, -0.0721325464f), - float32_t3(0.0000000000f, 0.0000000000f, 1.0088251844f) - ); + return ACES2065_1toXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ACES2065_1toXYZ, val); } }; struct ACEScc : colorspace_base { static float32_t3x3 FromXYZ() { - return float32_t3x3( - float32_t3( 1.6410233797f, -0.3248032942f, -0.2364246952f), - float32_t3(-0.6636628587f, 1.6153315917f, 0.0167563477f), - float32_t3( 0.0117218943f, -0.0082844420f, 0.9883948585f) - ); + return XYZtoACEScc; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(FromXYZ(), val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoACEScc, val); } static float32_t3x3 ToXYZ() { - return float32_t3x3( - float32_t3( 0.6624541811f, 0.1340042065f, 0.1561876870f), - float32_t3( 0.2722287168f, 0.6740817658f, 0.0536895174f), - float32_t3(-0.0055746495f, 0.0040607335f, 1.0103391003f) - ); + return ACEScctoXYZ; } - static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ToXYZ(), val); } + static float32_t3 ToXYZ(float32_t3 val) { return hlsl::mul(ACEScctoXYZ, val); } }; struct ACEScct : ACEScc {}; From 9a8b77e30e79b51370a8d4603a85aeec11c3a208 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 3 Nov 2025 16:57:06 +0700 Subject: [PATCH 12/25] update iridescent fresnel to match fresnel concept --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 56 +++++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index a40a6496c9..824cc6d6df 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -510,13 +510,26 @@ struct Dielectric }; // adapted from https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html -template) +template) struct Iridescent { + using this_t = Iridescent; using scalar_type = typename vector_traits::scalar_type; using monochrome_type = vector; using vector_type = T; // assert dim==3? + static this_t create(scalar_type Dinc, vector_type ior1, vector_type ior2, vector_type ior3, vector_type iork3) + { + this_t retval; + retval.Dinc = Dinc; + retval.eta12 = ior2/ior1; + retval.eta23 = ior3/ior2; + retval.etak23 = scalar_type(0.0); + NBL_IF_CONSTEXPR(SupportsTransmission) + retval.etak23 = iork3/ior2; + return retval; + } + // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, const vector_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) { @@ -552,18 +565,15 @@ struct Iridescent return xyz / scalar_type(1.0685e-7); } - T operator()() + T operator()(const scalar_type clampedCosTheta /* LdotH */) { const vector_type wavelengths = vector_type(colorspace::scRGB::wavelength_R, colorspace::scRGB::wavelength_G, colorspace::scRGB::wavelength_B); - vector_type eta12 = ior2/ior1; - vector_type eta23 = ior3/ior2; - vector_type etak23 = iork3/ior2; - scalar_type cosTheta_1 = absCosTheta; + scalar_type cosTheta_1 = clampedCosTheta; vector_type cosTheta_2; vector_type R12p, R23p, R12s, R23s; - const vector_type scale = ior1/ior2; + const vector_type scale = scalar_type(1.0)/eta12; const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(1-cosTheta_1*cosTheta_1) * scale * scale; cosTheta_2 = hlsl::sqrt(cosTheta2_2); @@ -571,7 +581,7 @@ struct Iridescent // Reflected part by the base // if kappa==0, base material is dielectric - if (hlsl::all::Dimension> >(iork3 < hlsl::promote(hlsl::numeric_limits::min))) + NBL_IF_CONSTEXPR(SupportsTransmission) Dielectric::__polarized(eta23, cosTheta_2, R23p, R23s); else Conductor::__polarized(eta23, etak23, cosTheta_2, R23p, R23s); @@ -635,12 +645,32 @@ struct Iridescent return hlsl::max(colorspace::scRGB::FromXYZ(I), hlsl::promote(0.0)) * hlsl::promote(0.5); } - scalar_type absCosTheta;// LdotH + scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return eta23[0]; } + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + { + OrientedEtaRcps rcpEta; + rcpEta.value = hlsl::promote(1.0) / eta23[0]; + rcpEta.value2 = rcpEta.value * rcpEta.value; + return rcpEta; + } + + this_t getReorientedFresnel(const scalar_type NdotI) NBL_CONST_MEMBER_FUNC + { + const bool flip = NdotI < scalar_type(0.0); + this_t orientedFresnel; + orientedFresnel.Dinc = Dinc; + orientedFresnel.eta12 = hlsl::mix(eta12, hlsl::promote(1.0)/eta12, flip); + orientedFresnel.eta23 = hlsl::mix(eta23, hlsl::promote(1.0)/eta23, flip); + orientedFresnel.etak23 = hlsl::promote(0.0); + NBL_IF_CONSTEXPR(SupportsTransmission) + orientedFresnel.etak23 = hlsl::mix(etak23, hlsl::promote(1.0)/etak23, flip); + return orientedFresnel; + } + scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm - vector_type ior1; // usually air (1.0) - vector_type ior2; // thin-film index - vector_type ior3; // complex conductor index, k==0 makes dielectric - vector_type iork3; + vector_type eta12; // outside (usually air 1.0) -> thin-film IOR + vector_type eta23; // thin-film -> base material IOR + vector_type etak23; // thin-film -> complex component, k==0 makes dielectric }; From 76ad0ed10fb39d65fee94621e339b3b58b5a8f29 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Nov 2025 11:16:00 +0700 Subject: [PATCH 13/25] minor typo fixes --- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 5 +++- include/nbl/builtin/hlsl/colorspace.hlsl | 28 +++++++++++----------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 824cc6d6df..67a2dea9d0 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -522,6 +522,7 @@ struct Iridescent { this_t retval; retval.Dinc = Dinc; + retval.thinFilmIor = ior2; retval.eta12 = ior2/ior1; retval.eta23 = ior3/ior2; retval.etak23 = scalar_type(0.0); @@ -595,7 +596,7 @@ struct Iridescent vector_type T121s = hlsl::promote(1.0) - R12s; // Optical Path Difference - const vector_type D = hlsl::promote(2.0 * Dinc) * ior2 * cosTheta_2; + const vector_type D = hlsl::promote(2.0 * Dinc) * thinFilmIor * cosTheta_2; const vector_type Dphi = hlsl::promote(2.0 * numbers::pi) * D / wavelengths; vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs; @@ -659,6 +660,7 @@ struct Iridescent const bool flip = NdotI < scalar_type(0.0); this_t orientedFresnel; orientedFresnel.Dinc = Dinc; + orientedFresnel.thinFilmIor = thinFilmIor; orientedFresnel.eta12 = hlsl::mix(eta12, hlsl::promote(1.0)/eta12, flip); orientedFresnel.eta23 = hlsl::mix(eta23, hlsl::promote(1.0)/eta23, flip); orientedFresnel.etak23 = hlsl::promote(0.0); @@ -668,6 +670,7 @@ struct Iridescent } scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm + vector_type thinFilmIor; vector_type eta12; // outside (usually air 1.0) -> thin-film IOR vector_type eta23; // thin-film -> base material IOR vector_type etak23; // thin-film -> complex component, k==0 makes dielectric diff --git a/include/nbl/builtin/hlsl/colorspace.hlsl b/include/nbl/builtin/hlsl/colorspace.hlsl index 78186066df..9e6da57344 100644 --- a/include/nbl/builtin/hlsl/colorspace.hlsl +++ b/include/nbl/builtin/hlsl/colorspace.hlsl @@ -32,9 +32,9 @@ struct scRGB : colorspace_base static float32_t3x3 FromXYZ() { - return XYZtoscRGB; + return decode::XYZtoscRGB; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoscRGB, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoscRGB, val); } static float32_t3x3 ToXYZ() { @@ -54,9 +54,9 @@ struct Display_P3 : colorspace_base static float32_t3x3 FromXYZ() { - return XYZtoDisplay_P3; + return decode::XYZtoDisplay_P3; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoDisplay_P3, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoDisplay_P3, val); } static float32_t3x3 ToXYZ() { @@ -73,9 +73,9 @@ struct DCI_P3 : colorspace_base static float32_t3x3 FromXYZ() { - return XYZtoDCI_P3; + return decode::XYZtoDCI_P3; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoDCI_P3, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoDCI_P3, val); } static float32_t3x3 ToXYZ() { @@ -92,9 +92,9 @@ struct BT2020 : colorspace_base static float32_t3x3 FromXYZ() { - return XYZtoBT2020; + return decode::XYZtoBT2020; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoBT2020, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoBT2020, val); } static float32_t3x3 ToXYZ() { @@ -115,9 +115,9 @@ struct AdobeRGB : colorspace_base static float32_t3x3 FromXYZ() { - return XYZtoAdobeRGB; + return decode::XYZtoAdobeRGB; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoAdobeRGB, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoAdobeRGB, val); } static float32_t3x3 ToXYZ() { @@ -130,9 +130,9 @@ struct ACES2065_1 : colorspace_base { static float32_t3x3 FromXYZ() { - return XYZtoACES2065_1; + return decode::XYZtoACES2065_1; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoACES2065_1, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoACES2065_1, val); } static float32_t3x3 ToXYZ() { @@ -145,9 +145,9 @@ struct ACEScc : colorspace_base { static float32_t3x3 FromXYZ() { - return XYZtoACEScc; + return decode::XYZtoACEScc; } - static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(XYZtoACEScc, val); } + static float32_t3 FromXYZ(float32_t3 val) { return hlsl::mul(decode::XYZtoACEScc, val); } static float32_t3x3 ToXYZ() { From 53c930f5cbc52b9d82819a5f47d214e1f95046e8 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Nov 2025 15:10:59 +0700 Subject: [PATCH 14/25] minor fixes to cook torrance, ndf infinity, move util functions into class --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 100 +++++++----------- .../nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl | 25 +++-- include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl | 22 ++-- .../ndf/microfacet_to_light_transform.hlsl | 73 +++++++------ 4 files changed, 106 insertions(+), 114 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index fdce140f68..1ce00ba310 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -67,60 +67,6 @@ struct quant_query_helper return ndf.template createQuantQuery(interaction, cache, dummy); } }; - -template -struct checkValid; - -template -struct checkValid -{ - using scalar_type = typename F::scalar_type; - - template - static bool __call(NBL_CONST_REF_ARG(F) orientedFresnel, NBL_CONST_REF_ARG(Sample) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) - { - return _sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min; - } -}; - -template -struct checkValid -{ - using scalar_type = typename F::scalar_type; - using vector_type = typename F::vector_type; // expect monochrome - - template - static bool __call(NBL_CONST_REF_ARG(F) orientedFresnel, NBL_CONST_REF_ARG(Sample) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) - { - fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(orientedFresnel.getRefractionOrientedEta())); - return cache.isValid(orientedEta); - } -}; - -template -struct getOrientedFresnel; - -template -struct getOrientedFresnel -{ - static F __call(NBL_CONST_REF_ARG(F) fresnel, typename F::scalar_type NdotV) - { - // expect conductor fresnel - return fresnel; - } -}; - -template -NBL_PARTIAL_REQ_TOP(fresnel::TwoSidedFresnel) -struct getOrientedFresnel) > -{ - using scalar_type = typename F::scalar_type; - - static F __call(NBL_CONST_REF_ARG(F) fresnel, scalar_type NdotV) - { - return fresnel.getReorientedFresnel(NdotV); - } -}; } // N (NDF), F (fresnel) @@ -138,6 +84,7 @@ struct SCookTorrance NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = ndf_type::SupportedPaths != ndf::MTT_REFLECT; NBL_HLSL_BXDF_ANISOTROPIC_COND_DECLS(IsAnisotropic); + // utility functions template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(!ndf::NDF_CanOverwriteDG && RequiredInteraction && RequiredMicrofacetCache) @@ -155,13 +102,40 @@ struct SCookTorrance DG = dg.projectedLightMeasure; } + template) + static fresnel_type getOrientedFresnel(NBL_CONST_REF_ARG(fresnel_type) fresnel, scalar_type NdotV) + { + // expect conductor fresnel + return fresnel; + } + template) + static fresnel_type getOrientedFresnel(NBL_CONST_REF_ARG(fresnel_type) fresnel, scalar_type NdotV) + { + return fresnel.getReorientedFresnel(NdotV); + } + + template, + class MicrofacetCache=conditional_t, typename C=bool_constant > + static enable_if_t checkValid(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + return _sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min; + } + template, + class MicrofacetCache=conditional_t, typename C=bool_constant > + static enable_if_t checkValid(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(orientedFresnel.getRefractionOrientedEta())); + return cache.isValid(orientedEta); + } + + // bxdf stuff template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); - if (!impl::checkValid::template __call(_f, _sample, interaction, cache)) + fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); + if (!checkValid(_f, _sample, interaction, cache)) return hlsl::promote(0.0); using quant_query_type = typename ndf_type::quant_query_type; @@ -178,7 +152,7 @@ struct SCookTorrance overwriteDG(DG, ndf, gq, qq, _sample, interaction, cache, isInfinity); - if (isInfinity) + if (isInfinity) // after all calls setting DG, allows compiler to throw away calls to ndf.D if using overwriteDG return hlsl::promote(0.0); scalar_type clampedVdotH = cache.getVdotH(); @@ -245,7 +219,7 @@ struct SCookTorrance const vector3_type localV = interaction.getTangentSpaceV(); const scalar_type NdotV = localV.z; - fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, NdotV); + fresnel_type _f = getOrientedFresnel(fresnel, NdotV); fresnel::OrientedEtaRcps rcpEta = _f.getOrientedEtaRcps(); const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative(localV, hlsl::promote(NdotV)); @@ -325,7 +299,7 @@ struct SCookTorrance dg1_query_type dq = ndf.template createDG1Query(interaction, cache); - fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); + fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); quant_query_type qq = impl::quant_query_helper::template __call(ndf, _f, interaction, cache); quant_type DG1 = ndf.template DG1(dq, qq, _sample, interaction, isInfinity); @@ -344,8 +318,8 @@ struct SCookTorrance NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); - if (!impl::checkValid::template __call(_f, _sample, interaction, cache)) + fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); + if (!checkValid(_f, _sample, interaction, cache)) return scalar_type(0.0); bool isInfinity; @@ -363,9 +337,9 @@ struct SCookTorrance bool isInfinity; scalar_type _pdf = __pdf(_sample, interaction, cache, isInfinity); - fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); + fresnel_type _f = getOrientedFresnel(fresnel, interaction.getNdotV()); - const bool valid = impl::checkValid::template __call(_f, _sample, interaction, cache); + const bool valid = checkValid(_f, _sample, interaction, cache); assert(valid); // expect the generated sample to always be valid, different checks for brdf and btdf scalar_type G2_over_G1 = scalar_type(1.0); diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl index f7d9f7c2f7..70f58a0dc7 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl @@ -107,11 +107,12 @@ struct BeckmannCommon(numeric_limits::infinity); } - isInfinity = false; scalar_type NdotH2 = cache.getNdotH2(); scalar_type nom = exp2((NdotH2 - scalar_type(1.0)) / (log(2.0) * a2 * NdotH2)); scalar_type denom = a2 * NdotH2 * NdotH2; - return numbers::inv_pi * nom / denom; + scalar_type ndf = numbers::inv_pi * nom / denom; + isInfinity = hlsl::isinf(ndf); + return ndf; } scalar_type C2(scalar_type NdotX2) @@ -141,7 +142,9 @@ struct BeckmannCommon(-(cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2) / NdotH2); scalar_type denom = a2 * NdotH2 * NdotH2; - return numbers::inv_pi * nom / denom; + scalar_type ndf = numbers::inv_pi * nom / denom; + isInfinity = hlsl::isinf(ndf); + return ndf; } scalar_type C2(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) @@ -277,12 +280,7 @@ struct Beckmann quant_query_type quant_query; // only has members for refraction NBL_IF_CONSTEXPR(SupportsTransmission) { - quant_query.VdotHLdotH = cache.getVdotHLdotH(); - const scalar_type VdotH = cache.getVdotH(); - const scalar_type VdotH_etaLdotH = hlsl::mix(VdotH + orientedEta * cache.getLdotH(), - VdotH / orientedEta + cache.getLdotH(), - interaction.getPathOrigin() == PathOrigin::PO_SENSOR); - quant_query.neg_rcp2_refractionDenom = scalar_type(-1.0) / (VdotH_etaLdotH * VdotH_etaLdotH); + quant_query = quant_query_type::template create(interaction, cache, orientedEta); } return quant_query; } @@ -337,8 +335,13 @@ struct Beckmann quant_type DG1(NBL_CONST_REF_ARG(dg1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_REF_ARG(bool) isInfinity) { scalar_type D = query.getNdf(); - scalar_type dg1 = query.getNdf() / (scalar_type(1.0) + query.getLambdaV()); - isInfinity = D == bit_cast(numeric_limits::infinity); + isInfinity = hlsl::isinf(D); + if (isInfinity) + { + quant_type dmq; + return dmq; + } + scalar_type dg1 = D / (scalar_type(1.0) + query.getLambdaV()); return createDualMeasureQuantity(dg1, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query); } diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index b27c892abe..1174e1ba93 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -97,7 +97,9 @@ struct GGXCommon / (denom * denom); + scalar_type ndf = a2 * numbers::inv_pi / (denom * denom); + isInfinity = hlsl::isinf(ndf); + return ndf; } scalar_type devsh_part(scalar_type NdotX2) @@ -128,7 +130,9 @@ struct GGXCommon / (a2 * denom * denom); + scalar_type ndf = numbers::inv_pi / (a2 * denom * denom); + isInfinity = hlsl::isinf(ndf); + return ndf; } // TODO: potential idea for making GGX spin using covariance matrix of sorts: https://www.desmos.com/3d/weq2ginq9o @@ -220,12 +224,7 @@ struct GGX quant_query_type quant_query; // only has members for refraction NBL_IF_CONSTEXPR(SupportsTransmission) { - quant_query.VdotHLdotH = cache.getVdotHLdotH(); - const scalar_type VdotH = cache.getVdotH(); - const scalar_type VdotH_etaLdotH = hlsl::mix(VdotH + orientedEta * cache.getLdotH(), - VdotH / orientedEta + cache.getLdotH(), - interaction.getPathOrigin() == PathOrigin::PO_SENSOR); - quant_query.neg_rcp2_refractionDenom = scalar_type(-1.0) / (VdotH_etaLdotH * VdotH_etaLdotH); + quant_query = quant_query_type::template create(interaction, cache, orientedEta); } return quant_query; } @@ -282,9 +281,12 @@ struct GGX quant_type DG1(NBL_CONST_REF_ARG(dg1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_REF_ARG(bool) isInfinity) { scalar_type D = query.getNdfwoNumerator(); - scalar_type dg1_over_2NdotV = query.getNdfwoNumerator() * query.getG1over2NdotV(); - isInfinity = D == bit_cast(numeric_limits::infinity); + isInfinity = hlsl::isinf(D); quant_type dmq; + if (isInfinity) + return dmq; + + scalar_type dg1_over_2NdotV = D * query.getG1over2NdotV(); dmq.microfacetMeasure = scalar_type(2.0) * interaction.getNdotV(_clamp) * dg1_over_2NdotV; NBL_IF_CONSTEXPR(SupportsTransmission) diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl index 58b8892ade..bfcb375ac8 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl @@ -18,9 +18,9 @@ namespace ndf enum MicrofacetTransformTypes : uint16_t { - MTT_REFLECT = 0b01, - MTT_REFRACT = 0b10, - MTT_REFLECT_REFRACT = 0b11 + MTT_REFLECT = 0b01, + MTT_REFRACT = 0b10, + MTT_REFLECT_REFRACT = 0b11 }; namespace microfacet_transform_concepts @@ -43,25 +43,38 @@ NBL_CONCEPT_END( template struct DualMeasureQuantQuery { - using scalar_type = T; + using scalar_type = T; - // note in pbrt it's `abs(VdotH)*abs(LdotH)` - // we leverage the fact that under transmission the sign must always be negative and rest of the code already accounts for that - scalar_type getVdotHLdotH() NBL_CONST_MEMBER_FUNC { return VdotHLdotH; } - scalar_type getNeg_rcp2_refractionDenom() NBL_CONST_MEMBER_FUNC { return neg_rcp2_refractionDenom ; } + template + static DualMeasureQuantQuery create(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta) + { + DualMeasureQuantQuery retval; + retval.VdotHLdotH = cache.getVdotHLdotH(); + const scalar_type VdotH = cache.getVdotH(); + const scalar_type VdotH_etaLdotH = hlsl::mix(VdotH + orientedEta * cache.getLdotH(), + VdotH / orientedEta + cache.getLdotH(), + interaction.getPathOrigin() == PathOrigin::PO_SENSOR); + retval.neg_rcp2_refractionDenom = scalar_type(-1.0) / (VdotH_etaLdotH * VdotH_etaLdotH); + return retval; + } - scalar_type VdotHLdotH; - scalar_type neg_rcp2_refractionDenom; + // note in pbrt it's `abs(VdotH)*abs(LdotH)` + // we leverage the fact that under transmission the sign must always be negative and rest of the code already accounts for that + scalar_type getVdotHLdotH() NBL_CONST_MEMBER_FUNC { return VdotHLdotH; } + scalar_type getNeg_rcp2_refractionDenom() NBL_CONST_MEMBER_FUNC { return neg_rcp2_refractionDenom ; } + + scalar_type VdotHLdotH; + scalar_type neg_rcp2_refractionDenom; }; template struct SDualMeasureQuant { - using value_type = T; - - T microfacetMeasure; - T projectedLightMeasure; + using value_type = T; + + T microfacetMeasure; + T projectedLightMeasure; }; namespace impl @@ -69,19 +82,19 @@ namespace impl template struct createDualMeasureQuantity_helper { - using scalar_type = typename vector_traits::scalar_type; + using scalar_type = typename vector_traits::scalar_type; - static SDualMeasureQuant __call(const T microfacetMeasure, scalar_type clampedNdotV, scalar_type clampedNdotL, scalar_type VdotHLdotH, scalar_type neg_rcp2_refractionDenom) - { - assert(clampedNdotV >= scalar_type(0.0) && clampedNdotL >= scalar_type(0.0)); - SDualMeasureQuant retval; - retval.microfacetMeasure = microfacetMeasure; - // do constexpr booleans first so optimizer picks up this and short circuits - const bool transmitted = reflect_refract==MTT_REFRACT || (reflect_refract!=MTT_REFLECT && VdotHLdotH < scalar_type(0.0)); - retval.projectedLightMeasure = microfacetMeasure * hlsl::mix(scalar_type(0.25),VdotHLdotH*neg_rcp2_refractionDenom,transmitted)/clampedNdotV; - // VdotHLdotH is negative under transmission, so thats denominator is negative - return retval; - } + static SDualMeasureQuant __call(const T microfacetMeasure, scalar_type clampedNdotV, scalar_type clampedNdotL, scalar_type VdotHLdotH, scalar_type neg_rcp2_refractionDenom) + { + assert(clampedNdotV >= scalar_type(0.0) && clampedNdotL >= scalar_type(0.0)); + SDualMeasureQuant retval; + retval.microfacetMeasure = microfacetMeasure; + // do constexpr booleans first so optimizer picks up this and short circuits + const bool transmitted = reflect_refract==MTT_REFRACT || (reflect_refract!=MTT_REFLECT && VdotHLdotH < scalar_type(0.0)); + retval.projectedLightMeasure = microfacetMeasure * hlsl::mix(scalar_type(0.25),VdotHLdotH*neg_rcp2_refractionDenom,transmitted)/clampedNdotV; + // VdotHLdotH is negative under transmission, so thats denominator is negative + return retval; + } }; } @@ -89,18 +102,18 @@ struct createDualMeasureQuantity_helper template SDualMeasureQuant createDualMeasureQuantity(const T specialMeasure, typename vector_traits::scalar_type clampedNdotV, typename vector_traits::scalar_type clampedNdotL) { - typename vector_traits::scalar_type dummy; - return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,dummy,dummy); + typename vector_traits::scalar_type dummy; + return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,dummy,dummy); } template SDualMeasureQuant createDualMeasureQuantity(const T specialMeasure, typename vector_traits::scalar_type clampedNdotV, typename vector_traits::scalar_type clampedNdotL, typename vector_traits::scalar_type VdotHLdotH, typename vector_traits::scalar_type neg_rcp2_refractionDenom) { - return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,VdotHLdotH,neg_rcp2_refractionDenom); + return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,VdotHLdotH,neg_rcp2_refractionDenom); } template SDualMeasureQuant createDualMeasureQuantity(const T specialMeasure, typename vector_traits::scalar_type clampedNdotV, typename vector_traits::scalar_type clampedNdotL, NBL_CONST_REF_ARG(Query) query) { - return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,query.getVdotHLdotH(),query.getNeg_rcp2_refractionDenom()); + return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,query.getVdotHLdotH(),query.getNeg_rcp2_refractionDenom()); } } From 23de8ed07c43740ed934e0a24dafb9b6b20d35be Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Nov 2025 16:09:15 +0700 Subject: [PATCH 15/25] changes to angle adding usage --- .../nbl/builtin/hlsl/math/angle_adding.hlsl | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/include/nbl/builtin/hlsl/math/angle_adding.hlsl b/include/nbl/builtin/hlsl/math/angle_adding.hlsl index 8a89840573..5d99b60ef2 100644 --- a/include/nbl/builtin/hlsl/math/angle_adding.hlsl +++ b/include/nbl/builtin/hlsl/math/angle_adding.hlsl @@ -21,53 +21,45 @@ struct sincos_accumulator { using this_t = sincos_accumulator; - static this_t create() + static this_t create(T cosA) { - this_t retval; - retval.runningSum = complex_t::create(T(1.0), T(0.0)); - return retval; + return create(cosA, sqrt(T(1.0) - cosA * cosA)); } - static this_t create(T cosA) + static this_t create(T cosA, T sinA) { this_t retval; - retval.runningSum = complex_t::create(cosA, T(0)); + retval.runningSum.real(cosA); + retval.runningSum.imag(sinA); + retval.wraparound = 0u; return retval; } - void addCosine(T cosA, T biasA) + void addCosine(T cosA, T sinA) { - const T bias = biasA + runningSum.imag(); const T a = cosA; const T b = runningSum.real(); const bool reverse = abs(min(a, b)) > max(a, b); - const T c = a * b - sqrt((T(1.0) - a * a) * (T(1.0) - b * b)); + const T c = a * b - sinA * runningSum.imag(); + const T d = sinA * b + a * runningSum.imag(); runningSum.real(ieee754::flipSign(c, reverse)); - runningSum.imag(hlsl::mix(bias, bias + numbers::pi, reverse)); + runningSum.imag(ieee754::flipSign(d, reverse)); + + wraparound += hlsl::mix(0u, 1u, reverse); } void addCosine(T cosA) { - addCosine(cosA, T(0.0)); + addCosine(cosA, sqrt(T(1.0) - cosA * cosA)); } T getSumofArccos() { - return acos(runningSum.real()) + runningSum.imag(); - } - - static T getArccosSumofABC_minus_PI(T cosA, T cosB, T cosC, T sinA, T sinB, T sinC) - { - const bool AltminusB = cosA < (-cosB); - const T cosSumAB = cosA * cosB - sinA * sinB; - const bool ABltminusC = cosSumAB < (-cosC); - const bool ABltC = cosSumAB < cosC; - // apply triple angle formula - const T absArccosSumABC = acos(clamp(cosSumAB * cosC - (cosA * sinB + sinA * cosB) * sinC, T(-1.0), T(1.0))); - return ((AltminusB ? ABltC : ABltminusC) ? (-absArccosSumABC) : absArccosSumABC) + ((AltminusB || ABltminusC) ? numbers::pi : (-numbers::pi)); + return acos(runningSum.real()) + wraparound * numbers::pi; } complex_t runningSum; + uint16_t wraparound; }; } From ff9e03fb07d959ecacaf247122f3c1e64c63d9d3 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Nov 2025 16:20:43 +0700 Subject: [PATCH 16/25] better orthonormality for H --- .../builtin/hlsl/bxdf/base/cook_torrance_base.hlsl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 1ce00ba310..9d9f2ceca5 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -128,6 +128,12 @@ struct SCookTorrance return cache.isValid(orientedEta); } + bool dotIsUnity(const vector3_type a, const vector3_type b) + { + const scalar_type ab = hlsl::dot(a, b); + return hlsl::max(ab, scalar_type(1.0) / ab) <= scalar_type(1.0 + 1e-3); + } + // bxdf stuff template, class MicrofacetCache=conditional_t @@ -256,8 +262,14 @@ struct SCookTorrance assert(cache.isValid(fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(_f.getRefractionOrientedEta())))); ray_dir_info_type V = interaction.getV(); + const matrix3x3_type fromTangent = interaction.getFromTangentSpace(); + // tangent frame orthonormality + assert(dotIsUnity(fromTangent[0],fromTangent[1])); + assert(dotIsUnity(fromTangent[1],fromTangent[2])); + assert(dotIsUnity(fromTangent[2],fromTangent[0])); + // NDF sampling produced a unit length direction + assert(dotIsUnity(localH,localH)); const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); - assert(hlsl::abs(hlsl::length(H) - scalar_type(1.0)) < scalar_type(1e-4)); Refract r = Refract::create(V.getDirection(), H); struct reflect_refract_wrapper // so we don't recalculate LdotH From 18b39222c70769fbb8b20d5184845a085f7f83b2 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 4 Nov 2025 17:11:28 +0700 Subject: [PATCH 17/25] minor change to ggx clamp --- include/nbl/builtin/hlsl/bxdf/ndf.hlsl | 1 - include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl | 20 ++++++++------------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl index da1dc5e630..6fab8790a4 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl @@ -103,7 +103,6 @@ NBL_CONSTEXPR_STATIC_INLINE bool RequiredMicrofacetCache = IS_ANISO ? Anisotropi #define NBL_HLSL_NDF_CONSTEXPR_DECLS(ANISO,REFLECT_REFRACT) NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = ANISO;\ NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes SupportedPaths = REFLECT_REFRACT;\ NBL_CONSTEXPR_STATIC_INLINE bool SupportsTransmission = REFLECT_REFRACT != MTT_REFLECT;\ -NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = SupportsTransmission ? BxDFClampMode::BCM_ABS : BxDFClampMode::BCM_NONE;\ NBL_HLSL_BXDF_ANISOTROPIC_COND_DECLS(IsAnisotropic);\ #define NBL_HLSL_NDF_TYPE_ALIASES(...) using this_t = BOOST_PP_REMOVE_PARENS(BOOST_PP_SEQ_ELEM(0, __VA_ARGS__));\ diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index 1174e1ba93..558100548e 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -84,8 +84,6 @@ struct GGXCommon) scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache, NBL_REF_ARG(bool) isInfinity) @@ -118,8 +116,6 @@ struct GGXCommon) scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache, NBL_REF_ARG(bool) isInfinity) { @@ -234,7 +230,7 @@ struct GGX dg1_query_type dg1_query; bool dummy; dg1_query.ndf = __ndf_base.template D(cache, dummy); - scalar_type clampedNdotV = interaction.getNdotV(_clamp); + scalar_type clampedNdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS); dg1_query.G1_over_2NdotV = G1_wo_numerator(clampedNdotV, __ndf_base.devsh_part(interaction.getNdotV2())); return dg1_query; } @@ -252,7 +248,7 @@ struct GGX dg1_query_type dg1_query; bool dummy; dg1_query.ndf = __ndf_base.template D(cache, dummy); - scalar_type clampedNdotV = interaction.getNdotV(_clamp); + scalar_type clampedNdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS); dg1_query.G1_over_2NdotV = G1_wo_numerator(clampedNdotV, __ndf_base.devsh_part(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2())); return dg1_query; } @@ -287,7 +283,7 @@ struct GGX return dmq; scalar_type dg1_over_2NdotV = D * query.getG1over2NdotV(); - dmq.microfacetMeasure = scalar_type(2.0) * interaction.getNdotV(_clamp) * dg1_over_2NdotV; + dmq.microfacetMeasure = scalar_type(2.0) * interaction.getNdotV(BxDFClampMode::BCM_ABS) * dg1_over_2NdotV; NBL_IF_CONSTEXPR(SupportsTransmission) { @@ -305,8 +301,8 @@ struct GGX template && RequiredInteraction && RequiredMicrofacetCache) scalar_type correlated_wo_numerator(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - scalar_type NdotV = interaction.getNdotV(_clamp); - scalar_type NdotL = _sample.getNdotL(_clamp); + scalar_type NdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS); + scalar_type NdotL = _sample.getNdotL(BxDFClampMode::BCM_ABS); scalar_type devsh_v = query.getDevshV(); scalar_type devsh_l = query.getDevshL(); // without numerator, numerator is 2 * NdotV * NdotL, we factor out 4 * NdotV * NdotL, hence 0.5 @@ -329,7 +325,7 @@ struct GGX template && RequiredInteraction && RequiredMicrofacetCache) scalar_type correlated(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - return scalar_type(4.0) * interaction.getNdotV(_clamp) * _sample.getNdotL(_clamp) * correlated_wo_numerator(query, _sample, interaction, cache); + return scalar_type(4.0) * interaction.getNdotV(BxDFClampMode::BCM_ABS) * _sample.getNdotL(BxDFClampMode::BCM_ABS) * correlated_wo_numerator(query, _sample, interaction, cache); } template && RequiredInteraction && RequiredMicrofacetCache) @@ -366,8 +362,8 @@ struct GGX scalar_type G2_over_G1(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { scalar_type G2_over_G1; - scalar_type NdotV = interaction.getNdotV(_clamp); - scalar_type NdotL = _sample.getNdotL(_clamp); + scalar_type NdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS); + scalar_type NdotL = _sample.getNdotL(BxDFClampMode::BCM_ABS); scalar_type devsh_v = query.getDevshV(); scalar_type devsh_l = query.getDevshL(); if (cache.isTransmission()) From f3dd05c1b5efae9f62112b0f611a74dd9d8f6839 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 5 Nov 2025 10:53:26 +0700 Subject: [PATCH 18/25] minor changes to create complex_t in angle_adding --- include/nbl/builtin/hlsl/complex.hlsl | 2 +- include/nbl/builtin/hlsl/math/angle_adding.hlsl | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/nbl/builtin/hlsl/complex.hlsl b/include/nbl/builtin/hlsl/complex.hlsl index f61b707865..da04c49b51 100644 --- a/include/nbl/builtin/hlsl/complex.hlsl +++ b/include/nbl/builtin/hlsl/complex.hlsl @@ -24,7 +24,7 @@ template struct complex_t : public std::complex { using base_t = std::complex; - complex_t(const Scalar real, const Scalar imag) : base_t(real, imag) {} + complex_t(const Scalar real = Scalar(), const Scalar imag = Scalar()) : base_t(real, imag) {} static complex_t create(const Scalar real, const Scalar imag) { complex_t retVal(real, imag); diff --git a/include/nbl/builtin/hlsl/math/angle_adding.hlsl b/include/nbl/builtin/hlsl/math/angle_adding.hlsl index 5d99b60ef2..6918050542 100644 --- a/include/nbl/builtin/hlsl/math/angle_adding.hlsl +++ b/include/nbl/builtin/hlsl/math/angle_adding.hlsl @@ -29,8 +29,9 @@ struct sincos_accumulator static this_t create(T cosA, T sinA) { this_t retval; - retval.runningSum.real(cosA); - retval.runningSum.imag(sinA); + retval.runningSum = complex_t::create(cosA, sinA); + // retval.runningSum.real(cosA); + // retval.runningSum.imag(sinA); retval.wraparound = 0u; return retval; } @@ -38,10 +39,11 @@ struct sincos_accumulator void addCosine(T cosA, T sinA) { const T a = cosA; - const T b = runningSum.real(); - const bool reverse = abs(min(a, b)) > max(a, b); - const T c = a * b - sinA * runningSum.imag(); - const T d = sinA * b + a * runningSum.imag(); + const T cosB = runningSum.real(); + const T sinB = runningSum.imag(); + const bool reverse = abs(min(a, cosB)) > max(a, cosB); + const T c = a * cosB - sinA * sinB; + const T d = sinA * cosB + a * sinB; runningSum.real(ieee754::flipSign(c, reverse)); runningSum.imag(ieee754::flipSign(d, reverse)); From 68d3614043c25efcd1f72f64095a2ab56371066d Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 5 Nov 2025 12:02:52 +0700 Subject: [PATCH 19/25] refactor out common section of cook torrance generate --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 145 +++++++++--------- 1 file changed, 70 insertions(+), 75 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 9d9f2ceca5..8eeecc3456 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -174,80 +174,11 @@ struct SCookTorrance return impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; } - template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) - { - { - const scalar_type NdotV = interaction.getNdotV(); - if (NdotV < numeric_limits::min) - return sample_type::createInvalid(); - assert(!hlsl::isnan(NdotV)); - } - - const vector3_type localV = interaction.getTangentSpaceV(); - const vector3_type localH = ndf.generateH(localV, u); - const scalar_type VdotH = hlsl::dot(localV, localH); - assert(VdotH >= scalar_type(0.0)); // VNDF sampling guarantees VdotH has same sign as NdotV (should be positive for BRDF) - - const scalar_type NdotL = scalar_type(2.0) * VdotH * localH.z - localV.z; - ray_dir_info_type L; - if (NdotL > 0) // compiler's Common Subexpression Elimination pass should re-use 2*VdotH later - { - ray_dir_info_type V = interaction.getV(); - const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); - struct reflect_wrapper // so we don't recalculate VdotH - { - vector3_type operator()() NBL_CONST_MEMBER_FUNC - { - return r(VdotH); - } - bxdf::Reflect r; - scalar_type VdotH; - }; - reflect_wrapper rw; - rw.r = bxdf::Reflect::create(V.getDirection(), H); - rw.VdotH = VdotH; - L = V.template reflect(rw); - - cache = anisocache_type::createForReflection(localV, localH); - - const vector3_type T = interaction.getT(); - const vector3_type B = interaction.getB(); - - return sample_type::create(L, T, B, NdotL); - } - else // fail if samples have invalid paths - return sample_type::createInvalid(); // should check if sample direction is invalid - } - template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(anisocache_type) cache) + sample_type __generate_common(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type localH, + const scalar_type NdotV, const scalar_type VdotH, bool transmitted, + NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, + NBL_REF_ARG(anisocache_type) cache, NBL_REF_ARG(bool) valid) { - const vector3_type localV = interaction.getTangentSpaceV(); - const scalar_type NdotV = localV.z; - - fresnel_type _f = getOrientedFresnel(fresnel, NdotV); - fresnel::OrientedEtaRcps rcpEta = _f.getOrientedEtaRcps(); - - const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative(localV, hlsl::promote(NdotV)); - const vector3_type localH = ndf.generateH(upperHemisphereV, u.xy); - const scalar_type VdotH = hlsl::dot(localV, localH); - - NBL_IF_CONSTEXPR(!ndf_type::GuaranteedVNDF) - { - // allow for rejection sampling, theoretically NdotV=0 or VdotH=0 is valid, but leads to 0 value contribution anyway - if ((IsBSDF ? (NdotV*VdotH) : VdotH) <= scalar_type(0.0)) - return sample_type::createInvalid(); - assert(!hlsl::isnan(NdotV*VdotH)); - } - else - { - assert(NdotV*VdotH >= scalar_type(0.0)); - } - const scalar_type reflectance = _f(hlsl::abs(VdotH))[0]; - - scalar_type rcpChoiceProb; - scalar_type z = u.z; - bool transmitted = math::partitionRandVariable(reflectance, z, rcpChoiceProb); const scalar_type LdotH = hlsl::mix(VdotH, ieee754::copySign(hlsl::sqrt(rcpEta.value2[0]*VdotH*VdotH + scalar_type(1.0) - rcpEta.value2[0]), -VdotH), transmitted); // fail if samples have invalid paths @@ -256,10 +187,12 @@ struct SCookTorrance // VNDF sampling guarantees that `VdotH` has same sign as `NdotV` // and `transmitted` controls the sign of `LdotH` relative to `VdotH` by construction (reflect -> same sign, or refract -> opposite sign) if (ComputeMicrofacetNormal::isTransmissionPath(NdotV, NdotL) != transmitted) + { + valid = false; return sample_type::createInvalid(); // should check if sample direction is invalid + } cache = anisocache_type::createPartial(VdotH, LdotH, localH.z, transmitted, rcpEta); - assert(cache.isValid(fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(_f.getRefractionOrientedEta())))); ray_dir_info_type V = interaction.getV(); const matrix3x3_type fromTangent = interaction.getFromTangentSpace(); @@ -290,10 +223,72 @@ struct SCookTorrance const vector3_type T = interaction.getT(); const vector3_type B = interaction.getB(); - cache.fillTangents(T, B, H); + valid = true; return sample_type::create(L, T, B, NdotL); } + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) + { + const scalar_type NdotV = interaction.getNdotV(); + if (NdotV < numeric_limits::min) + return sample_type::createInvalid(); + assert(!hlsl::isnan(NdotV)); + + const vector3_type localV = interaction.getTangentSpaceV(); + const vector3_type localH = ndf.generateH(localV, u); + const scalar_type VdotH = hlsl::dot(localV, localH); + assert(VdotH >= scalar_type(0.0)); // VNDF sampling guarantees VdotH has same sign as NdotV (should be positive for BRDF) + + fresnel::OrientedEtaRcps dummy; + bool valid; + sample_type s = __generate_common(interaction, localH, NdotV, VdotH, false, dummy, cache, valid); + if (valid) + cache = anisocache_type::createForReflection(localV, localH); + return s; + } + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(anisocache_type) cache) + { + const vector3_type localV = interaction.getTangentSpaceV(); + const scalar_type NdotV = localV.z; + + fresnel_type _f = getOrientedFresnel(fresnel, NdotV); + fresnel::OrientedEtaRcps rcpEta = _f.getOrientedEtaRcps(); + + const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative(localV, hlsl::promote(NdotV)); + const vector3_type localH = ndf.generateH(upperHemisphereV, u.xy); + const scalar_type VdotH = hlsl::dot(localV, localH); + + NBL_IF_CONSTEXPR(!ndf_type::GuaranteedVNDF) + { + // allow for rejection sampling, theoretically NdotV=0 or VdotH=0 is valid, but leads to 0 value contribution anyway + if ((IsBSDF ? (NdotV*VdotH) : VdotH) <= scalar_type(0.0)) + return sample_type::createInvalid(); + assert(!hlsl::isnan(NdotV*VdotH)); + } + else + { + assert(NdotV*VdotH >= scalar_type(0.0)); + } + const scalar_type reflectance = _f(hlsl::abs(VdotH))[0]; + + scalar_type rcpChoiceProb; + scalar_type z = u.z; + bool transmitted = math::partitionRandVariable(reflectance, z, rcpChoiceProb); + + bool valid; + sample_type s = __generate_common(interaction, localH, NdotV, VdotH, transmitted, rcpEta, cache, valid); + if (valid) + { + assert(cache.isValid(fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(_f.getRefractionOrientedEta())))); + const vector3_type T = interaction.getT(); + const vector3_type B = interaction.getB(); + const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); + cache.fillTangents(T, B, H); + } + return s; + } template > enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const conditional_t u, NBL_REF_ARG(isocache_type) cache) { From aebfecf563037973ff635b16ab622106729bd90f Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 6 Nov 2025 16:38:10 +0700 Subject: [PATCH 20/25] added luma contribution hint, getPrefixThroughputWeights to interaction + takes Spectrum template type --- include/nbl/builtin/hlsl/bxdf/common.hlsl | 41 +++++++++++++++++------ include/nbl/builtin/hlsl/bxdf/ndf.hlsl | 2 +- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 34d2e3d66c..58597aa43e 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -188,11 +188,14 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_type)) ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getV()), ::nbl::hlsl::is_same_v, typename T::ray_dir_info_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getN()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getNdotV(clampMode)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getNdotV2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getPathOrigin()), ::nbl::hlsl::is_same_v, PathOrigin)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getLuminosityContributionHint()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getPrefixThroughputWeights()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(normV,normN)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) ); @@ -202,21 +205,25 @@ NBL_CONCEPT_END( #undef iso #include -template) +template && concepts::FloatingPointLikeVectorial) struct SIsotropic { + using this_t = SIsotropic; using ray_dir_info_type = RayDirInfo; using scalar_type = typename RayDirInfo::scalar_type; using vector3_type = typename RayDirInfo::vector3_type; + using spectral_type = vector3_type; // WARNING: Changed since GLSL, now arguments need to be normalized! - static SIsotropic create(NBL_CONST_REF_ARG(RayDirInfo) normalizedV, const vector3_type normalizedN) + static this_t create(NBL_CONST_REF_ARG(RayDirInfo) normalizedV, const vector3_type normalizedN) { - SIsotropic retval; + this_t retval; retval.V = normalizedV; retval.N = normalizedN; retval.NdotV = nbl::hlsl::dot(retval.N, retval.V.getDirection()); retval.NdotV2 = retval.NdotV * retval.NdotV; + retval.luminosityContributionHint = hlsl::promote(1.0); + retval.throughputWeights = hlsl::promote(1.0); return retval; } @@ -230,11 +237,20 @@ struct SIsotropic scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return NdotV2; } PathOrigin getPathOrigin() NBL_CONST_MEMBER_FUNC { return PathOrigin::PO_SENSOR; } + spectral_type getLuminosityContributionHint() NBL_CONST_MEMBER_FUNC { return luminosityContributionHint; } + spectral_type getPrefixThroughputWeights() NBL_CONST_MEMBER_FUNC + { + spectral_type prefixThroughputWeights = luminosityContributionHint * throughputWeights; + return prefixThroughputWeights / math::lpNorm(prefixThroughputWeights); + } RayDirInfo V; vector3_type N; scalar_type NdotV; scalar_type NdotV2; + + spectral_type luminosityContributionHint; + spectral_type throughputWeights; // product of all quotients so far }; #define NBL_CONCEPT_NAME Anisotropic @@ -278,6 +294,7 @@ struct SAnisotropic using scalar_type = typename ray_dir_info_type::scalar_type; using vector3_type = typename ray_dir_info_type::vector3_type; using matrix3x3_type = matrix; + using spectral_type = typename isotropic_interaction_type::spectral_type; // WARNING: Changed since GLSL, now arguments need to be normalized! static this_t create( @@ -319,6 +336,8 @@ struct SAnisotropic scalar_type getNdotV(BxDFClampMode _clamp = BxDFClampMode::BCM_NONE) NBL_CONST_MEMBER_FUNC { return isotropic.getNdotV(_clamp); } scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return isotropic.getNdotV2(); } PathOrigin getPathOrigin() NBL_CONST_MEMBER_FUNC { return isotropic.getPathOrigin(); } + spectral_type getLuminosityContributionHint() NBL_CONST_MEMBER_FUNC { return isotropic.getLuminosityContributionHint(); } + spectral_type getPrefixThroughputWeights() NBL_CONST_MEMBER_FUNC { return isotropic.getPrefixThroughputWeights(); } vector3_type getT() NBL_CONST_MEMBER_FUNC { return T; } vector3_type getB() NBL_CONST_MEMBER_FUNC { return B; } @@ -345,7 +364,7 @@ struct SAnisotropic #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (_sample, T) -#define NBL_CONCEPT_PARAM_1 (inter, surface_interactions::SIsotropic) +#define NBL_CONCEPT_PARAM_1 (inter, surface_interactions::SIsotropic) #define NBL_CONCEPT_PARAM_2 (rdirinfo, typename T::ray_dir_info_type) #define NBL_CONCEPT_PARAM_3 (pV, typename T::vector3_type) #define NBL_CONCEPT_PARAM_4 (frame, typename T::matrix3x3_type) @@ -376,7 +395,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pV)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pV,pV,pV)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pV,pV,pNdotL)), ::nbl::hlsl::is_same_v, T)) - // ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create >(pV,inter)), ::nbl::hlsl::is_same_v, T)) // NOTE: temporarily commented out due to dxc bug https://github.com/microsoft/DirectXShaderCompiler/issues/7154 + // ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create >(pV,inter)), ::nbl::hlsl::is_same_v, T)) // NOTE: temporarily commented out due to dxc bug https://github.com/microsoft/DirectXShaderCompiler/issues/7154 ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getTangentSpaceL()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createInvalid()), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) @@ -525,7 +544,7 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (cache, T) -#define NBL_CONCEPT_PARAM_1 (iso, surface_interactions::SIsotropic >) +#define NBL_CONCEPT_PARAM_1 (iso, surface_interactions::SIsotropic, typename T::vector3_type >) #define NBL_CONCEPT_PARAM_2 (pNdotV, typename T::scalar_type) #define NBL_CONCEPT_PARAM_3 (_sample, SLightSample >) #define NBL_CONCEPT_PARAM_4 (V, typename T::vector3_type) @@ -540,9 +559,9 @@ NBL_CONCEPT_BEGIN(6) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(pNdotV,pNdotV,pNdotV,pNdotV)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(pNdotV,pNdotV,pNdotV)), ::nbl::hlsl::is_same_v, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection >,SLightSample > >(iso,_sample)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection, typename T::vector3_type >,SLightSample > >(iso,_sample)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,V,eta,V)), ::nbl::hlsl::is_same_v, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create >,SLightSample > >(iso,_sample,eta,V)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create, typename T::vector3_type >,SLightSample > >(iso,_sample,eta,V)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ReadableIsotropicMicrofacetCache, T)) ); #undef eta @@ -698,7 +717,7 @@ struct SIsotropicMicrofacetCache #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (cache, T) -#define NBL_CONCEPT_PARAM_1 (aniso, surface_interactions::SAnisotropic > >) +#define NBL_CONCEPT_PARAM_1 (aniso, surface_interactions::SAnisotropic, typename T::vector3_type > >) #define NBL_CONCEPT_PARAM_2 (pNdotL, typename T::scalar_type) #define NBL_CONCEPT_PARAM_3 (_sample, SLightSample >) #define NBL_CONCEPT_PARAM_4 (V, typename T::vector3_type) @@ -724,9 +743,9 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(V,V)), ::nbl::hlsl::is_same_v, T)) // ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,b0,rcp_eta)), ::nbl::hlsl::is_same_v, T)) // TODO: refuses to compile when arg4 is rcp_eta for some reason, eta is fine ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(V,V,pNdotL)), ::nbl::hlsl::is_same_v, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection > >,SLightSample > >(aniso,_sample)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection, typename T::vector3_type > >,SLightSample > >(aniso,_sample)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,V,V,V,eta,V)), ::nbl::hlsl::is_same_v, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create > >,SLightSample > >(aniso,_sample,eta)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create, typename T::vector3_type > >,SLightSample > >(aniso,_sample,eta)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createPartial(pNdotL,pNdotL,pNdotL,b0,rcp_eta)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR)(cache.fillTangents(V,V,V))) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) diff --git a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl index 6fab8790a4..f882374f99 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl @@ -20,7 +20,7 @@ namespace ndf namespace dummy_impl { using sample_t = SLightSample >; -using interaction_t = surface_interactions::SAnisotropic > >; +using interaction_t = surface_interactions::SAnisotropic, vector > >; using cache_t = SAnisotropicMicrofacetCache >; } From b53fa1bbb5bf8bb1ca49dc1832c78be84de71cf8 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 6 Nov 2025 16:40:21 +0700 Subject: [PATCH 21/25] cook torrance bsdf can use spectral fresnel, uses interaction.getPrefixThroughputWeights --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 19 ++++++++++++++----- include/nbl/builtin/hlsl/math/functions.hlsl | 12 ++++++------ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 8eeecc3456..7451318929 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -167,8 +167,8 @@ struct SCookTorrance NBL_IF_CONSTEXPR(IsBSDF) { - const scalar_type reflectance = _f(clampedVdotH)[0]; - return hlsl::promote(hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission())) * DG; + const spectral_type reflectance = impl::__implicit_promote::__call(_f(clampedVdotH)); + return hlsl::mix(reflectance, hlsl::promote(1.0) - reflectance, cache.isTransmission()) * DG; } else return impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; @@ -271,7 +271,9 @@ struct SCookTorrance { assert(NdotV*VdotH >= scalar_type(0.0)); } - const scalar_type reflectance = _f(hlsl::abs(VdotH))[0]; + + spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); + const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(VdotH))), prefixThroughputWeights); scalar_type rcpChoiceProb; scalar_type z = u.z; @@ -312,7 +314,8 @@ struct SCookTorrance NBL_IF_CONSTEXPR(IsBSDF) { - const scalar_type reflectance = _f(hlsl::abs(cache.getVdotH()))[0]; + spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); + const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))), prefixThroughputWeights); return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * DG1.projectedLightMeasure; } else @@ -359,7 +362,13 @@ struct SCookTorrance spectral_type quo; NBL_IF_CONSTEXPR(IsBSDF) - quo = hlsl::promote(G2_over_G1); + { + spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); + spectral_type reflectance = impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))); + const scalar_type scaled_reflectance = hlsl::dot(reflectance, prefixThroughputWeights); + quo = hlsl::mix(reflectance / scaled_reflectance, + (hlsl::promote(1.0) - reflectance) / (scalar_type(1.0) - scaled_reflectance), cache.isTransmission()) * G2_over_G1; + } else { const scalar_type VdotH = cache.getVdotH(); diff --git a/include/nbl/builtin/hlsl/math/functions.hlsl b/include/nbl/builtin/hlsl/math/functions.hlsl index 20442c467b..6eee1fae6e 100644 --- a/include/nbl/builtin/hlsl/math/functions.hlsl +++ b/include/nbl/builtin/hlsl/math/functions.hlsl @@ -33,9 +33,9 @@ struct lp_norm getter; - scalar_type retval = abs(getter(v, 0)); - for (int i = 1; i < extent::value; i++) - retval = max(abs(getter(v, i)),retval); + scalar_type retval = abs(getter(v, 0)); + for (int i = 1; i < vector_traits::Dimension; i++) + retval = max(abs(getter(v, i)),retval); return retval; } }; @@ -49,9 +49,9 @@ struct lp_norm getter; - scalar_type retval = abs(getter(v, 0)); - for (int i = 1; i < extent::value; i++) - retval += abs(getter(v, i)); + scalar_type retval = abs(getter(v, 0)); + for (int i = 1; i < vector_traits::Dimension; i++) + retval += abs(getter(v, i)); return retval; } From 946b05082c4f5e13fbaeb112ed974b52d6879f36 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 6 Nov 2025 16:54:06 +0700 Subject: [PATCH 22/25] thin smooth dielectric use luma coeff from interaction --- .../builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index bc95c03c91..17400adfe2 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -121,7 +121,7 @@ struct SThinSmoothDielectric const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel(interaction.getNdotV(_clamp))); // we are only allowed one choice for the entire ray, so make the probability a weighted sum - const scalar_type reflectionProb = nbl::hlsl::dot(reflectance, luminosityContributionHint); + const scalar_type reflectionProb = nbl::hlsl::dot(reflectance, interaction.getLuminosityContributionHint()); scalar_type rcpChoiceProb; scalar_type z = u.z; @@ -161,7 +161,7 @@ struct SThinSmoothDielectric const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel(interaction.getNdotV(_clamp))); const spectral_type sampleValue = hlsl::mix(reflectance, hlsl::promote(1.0) - reflectance, transmitted); - const scalar_type sampleProb = nbl::hlsl::dot(sampleValue,luminosityContributionHint); + const scalar_type sampleProb = nbl::hlsl::dot(sampleValue,interaction.getLuminosityContributionHint()); const scalar_type _pdf = bit_cast(numeric_limits::infinity); return quotient_pdf_type::create(sampleValue / sampleProb, _pdf); @@ -172,7 +172,6 @@ struct SThinSmoothDielectric } fresnel::Dielectric fresnel; - spectral_type luminosityContributionHint; }; } From 1a2f561b74c548fe0c2142e7e2e8224535cd5e45 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 7 Nov 2025 14:44:05 +0700 Subject: [PATCH 23/25] split iridescent fresnel by SupportsTransmission, adjusted fresnel concept, iridescent bxdf use cook torrance base --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 2 +- include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 141 ++++++++++---- include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl | 2 - .../hlsl/bxdf/reflection/iridescent.hlsl | 181 +---------------- .../hlsl/bxdf/transmission/iridescent.hlsl | 182 +----------------- 5 files changed, 106 insertions(+), 402 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 7451318929..df75072dbe 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -51,7 +51,7 @@ struct quant_query_helper template static quant_query_type __call(NBL_REF_ARG(N) ndf, NBL_CONST_REF_ARG(F) fresnel, NBL_CONST_REF_ARG(I) interaction, NBL_CONST_REF_ARG(C) cache) { - return ndf.template createQuantQuery(interaction, cache, fresnel.orientedEta.value[0]); + return ndf.template createQuantQuery(interaction, cache, fresnel.getRefractionOrientedEta()); } }; diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 67a2dea9d0..c9e92ad13d 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -313,8 +313,9 @@ NBL_CONCEPT_BEGIN(2) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::eta_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel(cosTheta)), ::nbl::hlsl::is_same_v, typename T::vector_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel.getOrientedEtaRcps()), ::nbl::hlsl::is_same_v, OrientedEtaRcps)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel.getOrientedEtaRcps()), ::nbl::hlsl::is_same_v, OrientedEtaRcps)) ); #undef cosTheta #undef fresnel @@ -342,6 +343,7 @@ struct Schlick { using scalar_type = typename vector_traits::scalar_type; using vector_type = T; + using eta_type = vector_type; static Schlick create(NBL_CONST_REF_ARG(T) F0) { @@ -358,11 +360,11 @@ struct Schlick return F0 + (1.0 - F0) * x*x*x*x*x; } - OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { - const T sqrtF0 = hlsl::sqrt(F0); - OrientedEtaRcps rcpEta; - rcpEta.value = (hlsl::promote(1.0) - sqrtF0) / (hlsl::promote(1.0) + sqrtF0); + const eta_type sqrtF0 = hlsl::sqrt(F0); + OrientedEtaRcps rcpEta; + rcpEta.value = (hlsl::promote(1.0) - sqrtF0) / (hlsl::promote(1.0) + sqrtF0); rcpEta.value2 = rcpEta.value * rcpEta.value; return rcpEta; } @@ -375,6 +377,7 @@ struct Conductor { using scalar_type = typename vector_traits::scalar_type; using vector_type = T; + using eta_type = vector_type; static Conductor create(NBL_CONST_REF_ARG(T) eta, NBL_CONST_REF_ARG(T) etak) { @@ -432,10 +435,10 @@ struct Conductor return (rs2 + rp2) * hlsl::promote(0.5); } - OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { - OrientedEtaRcps rcpEta; - rcpEta.value = hlsl::promote(1.0) / eta; + OrientedEtaRcps rcpEta; + rcpEta.value = hlsl::promote(1.0) / eta; rcpEta.value2 = rcpEta.value * rcpEta.value; return rcpEta; } @@ -450,6 +453,7 @@ struct Dielectric { using scalar_type = typename vector_traits::scalar_type; using vector_type = T; + using eta_type = vector_type; static Dielectric create(NBL_CONST_REF_ARG(OrientedEtas) orientedEta) { @@ -510,26 +514,16 @@ struct Dielectric }; // adapted from https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html +template +struct Iridescent; + +namespace impl +{ template) -struct Iridescent +struct iridescent_helper { - using this_t = Iridescent; using scalar_type = typename vector_traits::scalar_type; - using monochrome_type = vector; - using vector_type = T; // assert dim==3? - - static this_t create(scalar_type Dinc, vector_type ior1, vector_type ior2, vector_type ior3, vector_type iork3) - { - this_t retval; - retval.Dinc = Dinc; - retval.thinFilmIor = ior2; - retval.eta12 = ior2/ior1; - retval.eta23 = ior3/ior2; - retval.etak23 = scalar_type(0.0); - NBL_IF_CONSTEXPR(SupportsTransmission) - retval.etak23 = iork3/ior2; - return retval; - } + using vector_type = T; // returns reflectance R = (rp, rs), phi is the phase shift for each plane of polarization (p,s) static void phase_shift(const vector_type orientedEta, const vector_type orientedEtak, const vector_type cosTheta, NBL_REF_ARG(vector_type) phiS, NBL_REF_ARG(vector_type) phiP) @@ -566,7 +560,7 @@ struct Iridescent return xyz / scalar_type(1.0685e-7); } - T operator()(const scalar_type clampedCosTheta /* LdotH */) + T __call(const scalar_type clampedCosTheta) { const vector_type wavelengths = vector_type(colorspace::scRGB::wavelength_R, colorspace::scRGB::wavelength_G, colorspace::scRGB::wavelength_B); @@ -646,11 +640,80 @@ struct Iridescent return hlsl::max(colorspace::scRGB::FromXYZ(I), hlsl::promote(0.0)) * hlsl::promote(0.5); } - scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return eta23[0]; } - OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm + vector_type thinFilmIor; + vector_type eta12; // outside (usually air 1.0) -> thin-film IOR + vector_type eta23; // thin-film -> base material IOR + vector_type etak23; // thin-film -> complex component, k==0 makes dielectric +}; +} + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial) +struct Iridescent) > +{ + using this_t = Iridescent; + using scalar_type = typename vector_traits::scalar_type; + using vector_type = T; // assert dim==3? + using eta_type = vector_type; + + static this_t create(scalar_type Dinc, vector_type ior1, vector_type ior2, vector_type ior3, vector_type iork3) + { + this_t retval; + retval.helper.Dinc = Dinc; + retval.helper.thinFilmIor = ior2; + retval.helper.eta12 = ior2/ior1; + retval.helper.eta23 = ior3/ior2; + retval.helper.etak23 = iork3/ior2; + return retval; + } + + T operator()(const scalar_type clampedCosTheta) + { + return helper.__call(clampedCosTheta); + } + + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + { + OrientedEtaRcps rcpEta; + rcpEta.value = hlsl::promote(1.0) / helper.eta23; + rcpEta.value2 = rcpEta.value * rcpEta.value; + return rcpEta; + } + + impl::iridescent_helper helper; +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial) +struct Iridescent) > +{ + using this_t = Iridescent; + using scalar_type = typename vector_traits::scalar_type; + using vector_type = T; // assert dim==3? + using eta_type = vector; + + static this_t create(scalar_type Dinc, vector_type ior1, vector_type ior2, vector_type ior3) { - OrientedEtaRcps rcpEta; - rcpEta.value = hlsl::promote(1.0) / eta23[0]; + this_t retval; + retval.helper.Dinc = Dinc; + retval.helper.thinFilmIor = ior2; + retval.helper.eta12 = ior2/ior1; + retval.helper.eta23 = ior3/ior2; + retval.helper.etak23 = hlsl::promote(0.0); + return retval; + } + + T operator()(const scalar_type clampedCosTheta) + { + return helper.__call(clampedCosTheta); + } + + scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return helper.eta23[0]; } + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + { + OrientedEtaRcps rcpEta; + rcpEta.value = hlsl::promote(1.0) / helper.eta23[0]; rcpEta.value2 = rcpEta.value * rcpEta.value; return rcpEta; } @@ -659,21 +722,15 @@ struct Iridescent { const bool flip = NdotI < scalar_type(0.0); this_t orientedFresnel; - orientedFresnel.Dinc = Dinc; - orientedFresnel.thinFilmIor = thinFilmIor; - orientedFresnel.eta12 = hlsl::mix(eta12, hlsl::promote(1.0)/eta12, flip); - orientedFresnel.eta23 = hlsl::mix(eta23, hlsl::promote(1.0)/eta23, flip); - orientedFresnel.etak23 = hlsl::promote(0.0); - NBL_IF_CONSTEXPR(SupportsTransmission) - orientedFresnel.etak23 = hlsl::mix(etak23, hlsl::promote(1.0)/etak23, flip); + orientedFresnel.helper.Dinc = helper.Dinc; + orientedFresnel.helper.thinFilmIor = helper.thinFilmIor; + orientedFresnel.helper.eta12 = hlsl::mix(helper.eta12, hlsl::promote(1.0)/helper.eta12, flip); + orientedFresnel.helper.eta23 = hlsl::mix(helper.eta23, hlsl::promote(1.0)/helper.eta23, flip); + orientedFresnel.helper.etak23 = hlsl::promote(0.0); return orientedFresnel; } - scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm - vector_type thinFilmIor; - vector_type eta12; // outside (usually air 1.0) -> thin-film IOR - vector_type eta23; // thin-film -> base material IOR - vector_type etak23; // thin-film -> complex component, k==0 makes dielectric + impl::iridescent_helper helper; }; diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index 558100548e..c2b9e959e1 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -93,7 +93,6 @@ struct GGXCommon(numeric_limits::infinity); } - isInfinity = false; scalar_type denom = scalar_type(1.0) - one_minus_a2 * cache.getNdotH2(); scalar_type ndf = a2 * numbers::inv_pi / (denom * denom); isInfinity = hlsl::isinf(ndf); @@ -124,7 +123,6 @@ struct GGXCommon(numeric_limits::infinity); } - isInfinity = false; scalar_type denom = cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2 + cache.getNdotH2(); scalar_type ndf = numbers::inv_pi / (a2 * denom * denom); isInfinity = hlsl::isinf(ndf); diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl index c8f0631a03..07762d1298 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl @@ -15,184 +15,8 @@ namespace bxdf namespace reflection { -template) -struct SIridescent -{ - using this_t = SIridescent; - NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); - NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); - NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); - NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); - - NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(sample_type, Config); - NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); - NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); - NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); - NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); - - using ndf_type = ndf::GGX; - using fresnel_type = fresnel::Iridescent; - using measure_transform_type = ndf::SDualMeasureQuant; - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - - struct SCreationParams - { - scalar_type A; - scalar_type thickness; // thin-film thickness in nm - spectral_type ior0; - spectral_type ior1; - spectral_type ior2; - spectral_type iork2; - }; - using creation_type = SCreationParams; - - struct SIridQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - - scalar_type devsh_v; - scalar_type devsh_l; - }; - using query_type = SIridQuery; - - static this_t create(scalar_type A, scalar_type thickness, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1, NBL_CONST_REF_ARG(spectral_type) ior2, NBL_CONST_REF_ARG(spectral_type) iork2) - { - this_t retval; - retval.__base.ndf.A = vector2_type(A, A); - retval.__base.ndf.a2 = A*A; - retval.__base.ndf.one_minus_a2 = scalar_type(1.0) - A*A; - retval.__base.fresnel.Dinc = thickness; - retval.__base.fresnel.ior1 = ior0; - retval.__base.fresnel.ior2 = ior1; - retval.__base.fresnel.ior3 = ior2; - retval.__base.fresnel.iork3 = iork2; - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.A, params.thickness, params.ior0, params.ior1, params.ior2, params.iork2); - } - - query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - query_type query; - ndf_type ggx_ndf = __base.getNDF(); - query.devsh_v = ggx_ndf.devsh_part(interaction.getNdotV2()); - query.devsh_l = ggx_ndf.devsh_part(_sample.getNdotL2()); - return query; - } - - spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) - { - struct SGGXG2XQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } - - scalar_type devsh_v; - scalar_type devsh_l; - BxDFClampMode _clamp; - }; - - SGGXG2XQuery g2_query; - g2_query.devsh_v = query.getDevshV(); - g2_query.devsh_l = query.getDevshL(); - g2_query._clamp = _clamp; - - measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); - dualMeasure.maxNdotL = _sample.getNdotL(_clamp); - scalar_type DG = dualMeasure.getProjectedLightMeasure(); - fresnel_type f = __base.getFresnel(); - f.absCosTheta = cache.getLdotH(); - return f() * DG; - } - else - return hlsl::promote(0.0); - } - - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(isocache_type) cache) - { - SGGXAnisotropic ggx_aniso = SGGXAnisotropic::create(__base.ndf.A.x, __base.ndf.A.y, __base.fresnel.ior3/__base.fresnel.ior2, __base.fresnel.iork3/__base.fresnel.ior2); - anisocache_type anisocache; - sample_type s = ggx_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); - cache = anisocache.iso_cache; - return s; - } - - scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - struct SGGXDG1Query - { - using scalar_type = scalar_type; - - scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } - scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } - - scalar_type ndf; - scalar_type G1_over_2NdotV; - }; - - SGGXDG1Query dg1_query; - ndf_type ggx_ndf = __base.getNDF(); - dg1_query.ndf = __base.__D(cache); - - const scalar_type devsh_v = query.getDevshV(); - dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), devsh_v); - - measure_transform_type dualMeasure = __base.template __DG1(dg1_query); - return dualMeasure.getMicrofacetMeasure(); - } - - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - scalar_type _pdf = pdf(query, interaction, cache); - - spectral_type quo = hlsl::promote(0.0); - if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) - { - struct SGGXG2XQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } - - scalar_type devsh_v; - scalar_type devsh_l; - BxDFClampMode _clamp; - }; - - ndf_type ggx_ndf = __base.getNDF(); - - SGGXG2XQuery g2_query; - g2_query.devsh_v = query.getDevshV(); - g2_query.devsh_l = query.getDevshL(); - g2_query._clamp = _clamp; - const scalar_type G2_over_G1 = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); - - fresnel_type f = __base.getFresnel(); - f.absCosTheta = cache.getLdotH(); - const spectral_type reflectance = f(); - quo = reflectance * G2_over_G1; - } - - return quotient_pdf_type::create(quo, _pdf); - } - - SCookTorrance __base; -}; +template +using SIridescent = SCookTorrance, fresnel::Iridescent >; } @@ -200,6 +24,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl index aa2fde373e..2e7aa0e56e 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl @@ -15,185 +15,8 @@ namespace bxdf namespace transmission { -template) -struct SIridescent -{ - using this_t = SIridescent; - NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); - NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); - NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); - NBL_BXDF_CONFIG_ALIAS(matrix3x3_type, Config); - NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); - NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); - - NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(sample_type, Config); - NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); - NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); - NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); - NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); - using brdf_type = reflection::SGGXIsotropic; - - using ndf_type = ndf::GGX; - using fresnel_type = fresnel::Iridescent; - using measure_transform_type = ndf::SDualMeasureQuant; - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - - struct SCreationParams - { - scalar_type A; - scalar_type thickness; // thin-film thickness in nm - spectral_type ior0; - spectral_type ior1; - spectral_type ior2; - }; - using creation_type = SCreationParams; - - struct SIridQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - - scalar_type devsh_v; - scalar_type devsh_l; - }; - using query_type = SIridQuery; - - static this_t create(scalar_type A, scalar_type thickness, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1, NBL_CONST_REF_ARG(spectral_type) ior2) - { - this_t retval; - retval.__base.ndf.A = vector2_type(A, A); - retval.__base.ndf.a2 = A*A; - retval.__base.ndf.one_minus_a2 = scalar_type(1.0) - A*A; - retval.__base.fresnel.Dinc = thickness; - retval.__base.fresnel.ior1 = ior0; - retval.__base.fresnel.ior2 = ior1; - retval.__base.fresnel.ior3 = ior2; - retval.__base.fresnel.iork3 = hlsl::promote(0.0); // always 0.0 for dielectric base layer - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.A, params.thickness, params.ior0, params.ior1, params.ior2); - } - - query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - query_type query; - ndf_type ggx_ndf = __base.getNDF(); - query.devsh_v = ggx_ndf.devsh_part(interaction.getNdotV2()); - query.devsh_l = ggx_ndf.devsh_part(_sample.getNdotL2()); - return query; - } - - spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - struct SGGXG2XQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } - - scalar_type devsh_v; - scalar_type devsh_l; - BxDFClampMode _clamp; - }; - - SGGXG2XQuery g2_query; - g2_query.devsh_v = query.getDevshV(); - g2_query.devsh_l = query.getDevshL(); - g2_query._clamp = _clamp; - - fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); - measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); - dualMeasure.absNdotL = _sample.getNdotL(_clamp); - dualMeasure.orientedEta = orientedEta.value[0]; - scalar_type DG = dualMeasure.getProjectedLightMeasure(); - - fresnel_type f = __base.getFresnel(); - f.absCosTheta = hlsl::abs(cache.getLdotH()); - return hlsl::promote(f()[0]) * DG; - } - - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(isocache_type) cache) - { - fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); - SGGXDielectricAnisotropic ggx_aniso = SGGXDielectricAnisotropic::create(orientedEta, __base.ndf.A.x, __base.ndf.A.y); - anisocache_type anisocache; - sample_type s = ggx_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); - cache = anisocache.iso_cache; - return s; - } - - scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - struct SGGXDG1Query - { - using scalar_type = scalar_type; - - scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } - scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } - scalar_type getOrientedEta() NBL_CONST_MEMBER_FUNC { return orientedEta; } - - scalar_type ndf; - scalar_type G1_over_2NdotV; - scalar_type orientedEta; - }; - - SGGXDG1Query dg1_query; - fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(1.0, hlsl::promote(__base.fresnel.ior3/__base.fresnel.ior2)); - dg1_query.orientedEta = orientedEta.value[0]; - - fresnel_type f = __base.getFresnel(); - f.absCosTheta = hlsl::abs(cache.getLdotH()); - const scalar_type reflectance = f()[0]; - - ndf_type ggx_ndf = __base.getNDF(); - dg1_query.ndf = __base.__D(cache); - dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), query.getDevshV()); - - measure_transform_type dualMeasure = __base.template __DG1(dg1_query, cache); - return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * dualMeasure.getMicrofacetMeasure(); - } - - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - scalar_type _pdf = pdf(query, interaction, cache); - const bool transmitted = cache.isTransmission(); - - struct SGGXG2XQuery - { - using scalar_type = scalar_type; - - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } - BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } - - scalar_type devsh_v; - scalar_type devsh_l; - BxDFClampMode _clamp; - }; - - ndf_type ggx_ndf = __base.getNDF(); - SGGXG2XQuery g2_query; - g2_query.devsh_v = query.getDevshV(); - g2_query.devsh_l = query.getDevshL(); - g2_query._clamp = _clamp; - - scalar_type quo; - quo = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); - - return quotient_pdf_type::create(quo, _pdf); - } - - SCookTorrance __base; -}; +template +using SIridescent = SCookTorrance, fresnel::Iridescent >; } @@ -201,6 +24,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; From 4b8a06b91e3646c524bceef3719581faa0611012 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 7 Nov 2025 16:14:48 +0700 Subject: [PATCH 24/25] fix edge cases for iridescent fresnel --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 28 +++++++++++++------ include/nbl/builtin/hlsl/bxdf/fresnel.hlsl | 11 +++++--- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index df75072dbe..70782969bc 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -128,10 +128,10 @@ struct SCookTorrance return cache.isValid(orientedEta); } - bool dotIsUnity(const vector3_type a, const vector3_type b) + bool dotIsUnity(const vector3_type a, const vector3_type b, const scalar_type value) { const scalar_type ab = hlsl::dot(a, b); - return hlsl::max(ab, scalar_type(1.0) / ab) <= scalar_type(1.0 + 1e-3); + return hlsl::max(ab, value / ab) <= scalar_type(value + 1e-3); } // bxdf stuff @@ -197,11 +197,11 @@ struct SCookTorrance ray_dir_info_type V = interaction.getV(); const matrix3x3_type fromTangent = interaction.getFromTangentSpace(); // tangent frame orthonormality - assert(dotIsUnity(fromTangent[0],fromTangent[1])); - assert(dotIsUnity(fromTangent[1],fromTangent[2])); - assert(dotIsUnity(fromTangent[2],fromTangent[0])); + assert(dotIsUnity(fromTangent[0],fromTangent[1],0.0)); + assert(dotIsUnity(fromTangent[1],fromTangent[2],0.0)); + assert(dotIsUnity(fromTangent[2],fromTangent[0],0.0)); // NDF sampling produced a unit length direction - assert(dotIsUnity(localH,localH)); + assert(dotIsUnity(localH,localH,1.0)); const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); Refract r = Refract::create(V.getDirection(), H); @@ -238,7 +238,17 @@ struct SCookTorrance const vector3_type localV = interaction.getTangentSpaceV(); const vector3_type localH = ndf.generateH(localV, u); const scalar_type VdotH = hlsl::dot(localV, localH); - assert(VdotH >= scalar_type(0.0)); // VNDF sampling guarantees VdotH has same sign as NdotV (should be positive for BRDF) + NBL_IF_CONSTEXPR(!ndf_type::GuaranteedVNDF) // VNDF sampling guarantees VdotH has same sign as NdotV (should be positive for BRDF) + { + // allow for rejection sampling, theoretically NdotV=0 or VdotH=0 is valid, but leads to 0 value contribution anyway + if (VdotH <= scalar_type(0.0)) + return sample_type::createInvalid(); + assert(!hlsl::isnan(NdotV*VdotH)); + } + else + { + assert(VdotH >= scalar_type(0.0)); + } fresnel::OrientedEtaRcps dummy; bool valid; @@ -263,7 +273,7 @@ struct SCookTorrance NBL_IF_CONSTEXPR(!ndf_type::GuaranteedVNDF) { // allow for rejection sampling, theoretically NdotV=0 or VdotH=0 is valid, but leads to 0 value contribution anyway - if ((IsBSDF ? (NdotV*VdotH) : VdotH) <= scalar_type(0.0)) + if (NdotV*VdotH <= scalar_type(0.0)) return sample_type::createInvalid(); assert(!hlsl::isnan(NdotV*VdotH)); } @@ -283,7 +293,7 @@ struct SCookTorrance sample_type s = __generate_common(interaction, localH, NdotV, VdotH, transmitted, rcpEta, cache, valid); if (valid) { - assert(cache.isValid(fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(_f.getRefractionOrientedEta())))); + assert(cache.isValid(fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(_f.getRefractionOrientedEta())))); const vector3_type T = interaction.getT(); const vector3_type B = interaction.getB(); const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index c9e92ad13d..c918f1c0fe 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -401,7 +401,7 @@ struct Conductor static void __polarized(const T orientedEta, const T orientedEtak, const T cosTheta, NBL_REF_ARG(T) Rp, NBL_REF_ARG(T) Rs) { T cosTheta_2 = cosTheta * cosTheta; - T sinTheta2 = hlsl::promote(1.0) - cosTheta_2; + // T sinTheta2 = hlsl::promote(1.0) - cosTheta_2; const T eta = orientedEta; const T eta2 = eta*eta; const T etak = orientedEtak; @@ -470,7 +470,7 @@ struct Dielectric const T eta = orientedEta; const T eta2 = eta * eta; - T t0 = hlsl::sqrt(eta2 - sinTheta2); + T t0 = hlsl::sqrt(hlsl::max(eta2 - sinTheta2, hlsl::promote(0.0))); T t2 = eta2 * cosTheta; T rp = (t0 - t2) / (t0 + t2); @@ -569,9 +569,9 @@ struct iridescent_helper vector_type R12p, R23p, R12s, R23s; const vector_type scale = scalar_type(1.0)/eta12; - const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(1-cosTheta_1*cosTheta_1) * scale * scale; + const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(1.0-cosTheta_1*cosTheta_1) * scale * scale; - cosTheta_2 = hlsl::sqrt(cosTheta2_2); + cosTheta_2 = hlsl::sqrt(hlsl::max(cosTheta2_2, hlsl::promote(0.0))); Dielectric::__polarized(eta12, hlsl::promote(cosTheta_1), R12p, R12s); // Reflected part by the base @@ -585,6 +585,9 @@ struct iridescent_helper R12s = hlsl::mix(R12s, hlsl::promote(1.0), cosTheta2_2 <= hlsl::promote(0.0)); R12p = hlsl::mix(R12p, hlsl::promote(1.0), cosTheta2_2 <= hlsl::promote(0.0)); + R23s = hlsl::mix(R23s, hlsl::promote(0.0), cosTheta2_2 <= hlsl::promote(0.0)); + R23p = hlsl::mix(R23p, hlsl::promote(0.0), cosTheta2_2 <= hlsl::promote(0.0)); + // Compute the transmission coefficients vector_type T121p = hlsl::promote(1.0) - R12p; vector_type T121s = hlsl::promote(1.0) - R12s; From 5782a49f31dd1be02a91c5237f6c2bc56566c5a4 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 7 Nov 2025 16:53:11 +0700 Subject: [PATCH 25/25] removed redundant weight in interaction --- .../builtin/hlsl/bxdf/base/cook_torrance_base.hlsl | 12 ++++++------ include/nbl/builtin/hlsl/bxdf/common.hlsl | 9 --------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index 70782969bc..de4cbcef8e 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -282,8 +282,8 @@ struct SCookTorrance assert(NdotV*VdotH >= scalar_type(0.0)); } - spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); - const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(VdotH))), prefixThroughputWeights); + spectral_type throughputWeights = interaction.getLuminosityContributionHint(); + const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(VdotH))), throughputWeights); scalar_type rcpChoiceProb; scalar_type z = u.z; @@ -324,8 +324,8 @@ struct SCookTorrance NBL_IF_CONSTEXPR(IsBSDF) { - spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); - const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))), prefixThroughputWeights); + spectral_type throughputWeights = interaction.getLuminosityContributionHint(); + const scalar_type reflectance = hlsl::dot(impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))), throughputWeights); return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * DG1.projectedLightMeasure; } else @@ -373,9 +373,9 @@ struct SCookTorrance spectral_type quo; NBL_IF_CONSTEXPR(IsBSDF) { - spectral_type prefixThroughputWeights = interaction.getPrefixThroughputWeights(); + spectral_type throughputWeights = interaction.getLuminosityContributionHint(); spectral_type reflectance = impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))); - const scalar_type scaled_reflectance = hlsl::dot(reflectance, prefixThroughputWeights); + const scalar_type scaled_reflectance = hlsl::dot(reflectance, throughputWeights); quo = hlsl::mix(reflectance / scaled_reflectance, (hlsl::promote(1.0) - reflectance) / (scalar_type(1.0) - scaled_reflectance), cache.isTransmission()) * G2_over_G1; } diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 58597aa43e..ebad0a925c 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -195,7 +195,6 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getNdotV2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getPathOrigin()), ::nbl::hlsl::is_same_v, PathOrigin)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getLuminosityContributionHint()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getPrefixThroughputWeights()), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(normV,normN)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) ); @@ -223,7 +222,6 @@ struct SIsotropic retval.NdotV = nbl::hlsl::dot(retval.N, retval.V.getDirection()); retval.NdotV2 = retval.NdotV * retval.NdotV; retval.luminosityContributionHint = hlsl::promote(1.0); - retval.throughputWeights = hlsl::promote(1.0); return retval; } @@ -238,11 +236,6 @@ struct SIsotropic PathOrigin getPathOrigin() NBL_CONST_MEMBER_FUNC { return PathOrigin::PO_SENSOR; } spectral_type getLuminosityContributionHint() NBL_CONST_MEMBER_FUNC { return luminosityContributionHint; } - spectral_type getPrefixThroughputWeights() NBL_CONST_MEMBER_FUNC - { - spectral_type prefixThroughputWeights = luminosityContributionHint * throughputWeights; - return prefixThroughputWeights / math::lpNorm(prefixThroughputWeights); - } RayDirInfo V; vector3_type N; @@ -250,7 +243,6 @@ struct SIsotropic scalar_type NdotV2; spectral_type luminosityContributionHint; - spectral_type throughputWeights; // product of all quotients so far }; #define NBL_CONCEPT_NAME Anisotropic @@ -337,7 +329,6 @@ struct SAnisotropic scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return isotropic.getNdotV2(); } PathOrigin getPathOrigin() NBL_CONST_MEMBER_FUNC { return isotropic.getPathOrigin(); } spectral_type getLuminosityContributionHint() NBL_CONST_MEMBER_FUNC { return isotropic.getLuminosityContributionHint(); } - spectral_type getPrefixThroughputWeights() NBL_CONST_MEMBER_FUNC { return isotropic.getPrefixThroughputWeights(); } vector3_type getT() NBL_CONST_MEMBER_FUNC { return T; } vector3_type getB() NBL_CONST_MEMBER_FUNC { return B; }