From 4b3224acd723dacea570a1f94642f2b61d110f42 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 26 Aug 2025 11:50:15 +0700 Subject: [PATCH 1/9] 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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 6/9] 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 7/9] 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 8/9] 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 9/9] 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 {};