diff --git a/examples_tests b/examples_tests index eebde787c2..cbfac2681a 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit eebde787c233367ade8eb0580bc79c0d562e97aa +Subproject commit cbfac2681a7747a0bed52da6a30843947696f6cc 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 8ed0d6ac92..f91a41e82b 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -192,11 +192,12 @@ struct SCookTorrance NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache && !IsBSDF) value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction) NBL_CONST_MEMBER_FUNC { - const MicrofacetCache cache = MicrofacetCache::template createForReflection(interaction, _sample); + const MicrofacetCache cache = MicrofacetCache::template createForReflection(interaction, _sample); return evalAndWeight(_sample, interaction, cache); } - template, + class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache && IsBSDF) value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction) NBL_CONST_MEMBER_FUNC { @@ -204,7 +205,7 @@ struct SCookTorrance using oriented_etas_t = fresnel::OrientedEtas; oriented_etas_t orientedEta = oriented_etas_t::create(interaction.getNdotV(), hlsl::promote(eta)); vector3_type dummyH; - MicrofacetCache cache = MicrofacetCache::template create(interaction, _sample, orientedEta, dummyH); + MicrofacetCache cache = MicrofacetCache::template create(interaction, _sample, orientedEta, dummyH); return evalAndWeight(_sample, interaction, cache); } diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index fd841efab3..6c270f1a36 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -72,7 +72,7 @@ struct SLambertianBase } scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return forwardPdf(_sample, interaction.isotropic); + return forwardPdf(_sample, interaction.isotropic, _cache); } quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index d9631d415f..46b7a51ae3 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -616,7 +616,7 @@ struct SIsotropicMicrofacetCache // not coming from the medium (reflected) OR // exiting at the macro scale AND ( (not L outside the cone of possible directions given IoR with constraint VdotH*LdotH<0.0) OR (microfacet not facing toward the macrosurface, i.e. non heightfield profile of microsurface) ) - const bool valid = ComputeMicrofacetNormal::isValidMicrofacet(transmitted, VdotL, retval.absNdotH, fresnel::OrientedEtas::create(1.0, computeMicrofacetNormal.orientedEta)); + const bool valid = ComputeMicrofacetNormal::isValidMicrofacet(transmitted, VdotL, retval.absNdotH, fresnel::OrientedEtas::create(1.0, hlsl::promote(computeMicrofacetNormal.orientedEta))); if (valid) { retval.VdotH = hlsl::dot(computeMicrofacetNormal.V,H); diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 6bc40a831a..6ce2368773 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -46,7 +46,7 @@ struct OrientedEtaRcps { using scalar_type = typename vector_traits::scalar_type; - static OrientedEtaRcps create(scalar_type NdotI, T eta) + static OrientedEtaRcps create(const scalar_type NdotI, const T eta) { OrientedEtaRcps retval; const bool backside = NdotI < scalar_type(0.0); @@ -73,7 +73,7 @@ struct OrientedEtas { using scalar_type = typename vector_traits::scalar_type; - static OrientedEtas create(scalar_type NdotI, T eta) + static OrientedEtas create(const scalar_type NdotI, const T eta) { OrientedEtas retval; const bool backside = NdotI < scalar_type(0.0); @@ -117,8 +117,8 @@ struct ComputeMicrofacetNormal ComputeMicrofacetNormal retval; retval.V = V; retval.L = L; - fresnel::OrientedEtas orientedEtas = fresnel::OrientedEtas::create(VdotH, eta); - retval.orientedEta = orientedEtas.value; + fresnel::OrientedEtas orientedEtas = fresnel::OrientedEtas::create(VdotH, hlsl::promote(eta)); + retval.orientedEta = orientedEtas.value[0]; return retval; } @@ -138,7 +138,7 @@ struct ComputeMicrofacetNormal // VdotH <= 1-orientedEta2 for orientedEta<1 -> VdotH<0 // VdotH <= 0 for orientedEta>1 // so for transmission VdotH<=0, H needs to be flipped to be consistent with oriented eta - vector_type unnormalized(const bool _refract) + vector_type unnormalized(const bool _refract) NBL_CONST_MEMBER_FUNC { assert(hlsl::dot(V, L) <= -hlsl::min(orientedEta, scalar_type(1.0) / orientedEta)); const scalar_type etaFactor = hlsl::mix(scalar_type(1.0), orientedEta, _refract); @@ -148,7 +148,7 @@ struct ComputeMicrofacetNormal } // returns normalized vector, but NaN when result is length 0 - vector_type normalized(const bool _refract) + vector_type normalized(const bool _refract) NBL_CONST_MEMBER_FUNC { const vector_type H = unnormalized(_refract); return hlsl::normalize(H); diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_normal_shadowing.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_normal_shadowing.hlsl new file mode 100644 index 0000000000..7b8860bf7d --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_normal_shadowing.hlsl @@ -0,0 +1,102 @@ +// Copyright (C) 2018-2026 - 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_NDF_MICROFACET_NORMAL_SHADOWING_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_NDF_MICROFACET_NORMAL_SHADOWING_INCLUDED_ + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace ndf +{ + +enum PerturbedNormalShadowing : uint16_t +{ + PNS_SCHUSSLER, + PNS_YINING +}; + +template +struct ShadowingMethod; + +// based on Microfacet-based Normal Mapping for Robust Monte Carlo Path Tracing: https://jo.dreggn.org/home/2017_normalmap.pdf +template +struct ShadowingMethod +{ + using scalar_type = T; + using vector3_type = vector; + using matrix3x3_type = matrix; + + static scalar_type G1(const scalar_type clampedNdotL, const scalar_type NdotNp, const scalar_type clampedNpdotL, const scalar_type clampedNtdotL, const bool isTangentFacet=false) + { + const scalar_type sinThetaNp = hlsl::sqrt(hlsl::max(1.0 - NdotNp * NdotNp, 0.0)); + return hlsl::min(scalar_type(1.0), + clampedNdotL * hlsl::max(scalar_type(0.0), NdotNp) + / (clampedNpdotL + clampedNtdotL * sinThetaNp) + ); + } + + static scalar_type lambdaP(const scalar_type NdotNp, const scalar_type clampedNpdotV, const scalar_type clampedNtdotV) + { + const scalar_type sinThetaNp = hlsl::sqrt(hlsl::max(1.0 - NdotNp * NdotNp, 0.0)); + return clampedNpdotV / (clampedNpdotV + clampedNtdotV * sinThetaNp); + } + + static vector3_type computeNt(const vector3_type Np, const matrix3x3_type shadingBasis) + { + const vector3_type local_Np = hlsl::mul(shadingBasis, Np); + const vector3_type local_Nt = hlsl::normalize(-vector3_type(local_Np.xy, 0.0)); + return hlsl::mul(hlsl::transpose(shadingBasis), local_Nt); + } +}; + +// based on Taming the Shadow Terminator: https://www.yiningkarlli.com/projects/shadowterminator/shadow_terminator_v1_1.pdf +template +struct ShadowingMethod +{ + using scalar_type = T; + using vector3_type = vector; + using matrix3x3_type = matrix; + + static scalar_type G1(const scalar_type clampedNdotL, const scalar_type NdotNp, const scalar_type clampedNpdotL, const scalar_type clampedNtdotL, const bool isTangentFacet=false) + { + const scalar_type g = hlsl::min(scalar_type(1.0), + clampedNdotL / (hlsl::mix(clampedNpdotL, clampedNtdotL, isTangentFacet) * NdotNp) + ); + const scalar_type g2 = g * g; + return -g2 * g + g2 + g; + } + + // TODO: verify maths + // since Nt is now perpendicular to Np and not N + // total area of surface = 1 = hypotenuse of right triangle + // area of perturbed facet = cos(Np) = NdotNp + // area of tangent facet = cos(Nt) = sin(Np) + // projected area of Np onto V = area * NpdotV + static scalar_type lambdaP(const scalar_type NdotNp, const scalar_type clampedNpdotV, const scalar_type clampedNtdotV) + { + const scalar_type sinThetaNp = hlsl::sqrt(hlsl::max(1.0 - NdotNp * NdotNp, 0.0)); + const scalar_type ap = clampedNpdotV * NdotNp; + if (ap < numeric_limits::min) + return scalar_type(0.0); + const scalar_type at = clampedNtdotV * sinThetaNp; + return ap / (ap + at); + } + + static vector3_type computeNt(const vector3_type Np, const matrix3x3_type shadingBasis) + { + const vector3_type local_Np = hlsl::mul(shadingBasis, Np); + const vector3_type local_Nt = hlsl::normalize(vector3_type(local_Np.xy * -local_Np.z, 1.0 - local_Np.z*local_Np.z)); + return hlsl::mul(hlsl::transpose(shadingBasis), local_Nt); + } +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl index c5d4b019c8..d0cb829039 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl @@ -10,6 +10,7 @@ #include "nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl" #include "nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl" #include "nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection/microfacet_normals.hlsl" namespace nbl { diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index 1acdf211b5..6bbcb9d0c6 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -59,11 +59,11 @@ struct SDeltaDistribution scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return 0; + return bit_cast(numeric_limits::infinity); } scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return 0; + return bit_cast(numeric_limits::infinity); } quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/microfacet_normals.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/microfacet_normals.hlsl new file mode 100644 index 0000000000..e549c21e50 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/microfacet_normals.hlsl @@ -0,0 +1,656 @@ +// 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_MICROFACET_NORMALS_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_MICROFACET_NORMALS_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/bxdf/config.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf/microfacet_normal_shadowing.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template +struct SMicrofacetNormals; + +template +NBL_PARTIAL_REQ_TOP(config_concepts::BasicConfiguration) +struct SMicrofacetNormals) > +{ + using this_t = SMicrofacetNormals; + BXDF_CONFIG_TYPE_ALIASES(Config); + + NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = false; + using bxdf_type = BRDF; + using random_type = conditional_t; + struct Cache + { + typename bxdf_type::isocache_type iso_cache; + typename bxdf_type::anisocache_type aniso_cache; + bool sampleFromNt; + bool sampleIsShadowed; + }; + using isocache_type = Cache; + using anisocache_type = Cache; + using matrix3x3_type = matrix; + + using shadowing_method_type = ndf::ShadowingMethod; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; + + // perturbed normal Np stored in interaction + // shading normal N (geometric normal in paper) stored in bxdf + // tangent normal Nt derived as needed + template // TODO: concept for accessor + anisotropic_interaction_type buildInteraction(NBL_CONST_REF_ARG(NormalsTexAccessor) normalMap, const vector2_type uv, const matrix object_to_world, const ray_dir_info_type V) NBL_CONST_MEMBER_FUNC + { + const matrix TBN = hlsl::transpose(object_to_world); + vector3_type localN; + normalMap.get(localN, TBN[2], TBN[0], TBN[1]); + // normalMap.get(localN, uv); + localN = hlsl::promote(2.0) * localN - hlsl::promote(1.0); + + const vector3_type N = hlsl::normalize(hlsl::mul(object_to_world, localN)); + isotropic_interaction_type interaction = isotropic_interaction_type::create(V, N); + interaction.luminosityContributionHint = colorspace::scRGBtoXYZ[1]; + return anisotropic_interaction_type::create(interaction); + } + + template::IsMicrofacet> NBL_FUNC_REQUIRES(C::value && !traits::IsMicrofacet) + static typename bxdf_type::anisocache_type __createChildCache(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + typename bxdf_type::anisocache_type cache; + return cache; + } + template::IsMicrofacet> NBL_FUNC_REQUIRES(C::value && traits::IsMicrofacet) + static typename bxdf_type::anisocache_type __createChildCache(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return bxdf_type::anisocache_type::template createForReflection(interaction, _sample); + } + + static spectral_type __calculateQuotient(const spectral_type quo, const spectral_type quo_other, const scalar_type choiceProb, const scalar_type pdf, const scalar_type pdf_other) + { + const scalar_type weight = pdf / (pdf + pdf_other); // balance heuristic + return (quo * weight / choiceProb) / (scalar_type(1.0) + pdf_other * (scalar_type(1.0) - choiceProb) / (pdf * choiceProb)) + + (quo_other * (scalar_type(1.0) - weight)) / (pdf_other * (scalar_type(1.0) - choiceProb) + (pdf * choiceProb)); + } + + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + return evalAndWeight(_sample, anisotropic_interaction_type::create(interaction)); + } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + const sample_type sample_N = sample_type::create(_sample.getL(), shadingNormal); + return nested_brdf.evalAndWeight(sample_N, interaction_N); + } + + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type NpdotV = interaction.getNdotV(); + const scalar_type NtdotV = hlsl::dot(Nt, V.getDirection()); + spectral_type eval = hlsl::promote(0.0); + + const vector3_type L = _sample.getL().getDirection(); + const scalar_type clampedNdotNp = hlsl::max(scalar_type(0.0), NdotNp); + const scalar_type NdotL = hlsl::dot(shadingNormal, L); + const scalar_type NpdotL = _sample.getNdotL(BxDFClampMode::BCM_MAX); + const scalar_type NtdotL = hlsl::dot(Nt, L); + const scalar_type lambda_p = shadowing_method_type::lambdaP(NdotNp, hlsl::max(scalar_type(0.0), NpdotV), hlsl::max(scalar_type(0.0), NtdotV)); + const scalar_type shadowing = shadowing_method_type::G1(hlsl::max(scalar_type(0.0), NdotL), clampedNdotNp, + NpdotL, hlsl::max(scalar_type(0.0), NtdotL)); + + // i -> p -> o + { + value_weight_type eval_single_p = nested_brdf.evalAndWeight(_sample, interaction); + eval += eval_single_p.value() * lambda_p * shadowing; + } + + // i -> p -> t -> o + if (NtdotL > scalar_type(0.0)) + { + Reflect reflectL = Reflect::create(L, Nt); + ray_dir_info_type L_reflected; + L_reflected.setDirection(hlsl::normalize(reflectL(NtdotL))); + sample_type sample_double_p = sample_type::create(L_reflected, Np); + + const scalar_type notShadowedNpMirror = scalar_type(1.0) - shadowing_method_type::G1(hlsl::max(scalar_type(0.0), hlsl::dot(L_reflected.getDirection(), shadingNormal)), clampedNdotNp, + sample_double_p.getNdotL(BxDFClampMode::BCM_MAX), hlsl::max(scalar_type(0.0), hlsl::dot(L_reflected.getDirection(), Nt))); + const scalar_type shadowing_t = shadowing_method_type::G1(hlsl::max(scalar_type(0.0), NdotL), hlsl::max(scalar_type(0.0), hlsl::dot(shadingNormal, Nt)), + NpdotL, hlsl::max(scalar_type(0.0), NtdotL)); + + value_weight_type eval_double_p = nested_brdf.evalAndWeight(sample_double_p, interaction); + eval += eval_double_p.value() * (lambda_p * notShadowedNpMirror * shadowing_t); + } + + // i -> t -> p -> o + if (NtdotV > scalar_type(0.0)) + { + Reflect reflectV = Reflect::create(V.getDirection(), Nt); + ray_dir_info_type V_reflected; + V_reflected.setDirection(hlsl::normalize(reflectV(NtdotV))); + + typename bxdf_type::isotropic_interaction_type iso_reflected = bxdf_type::isotropic_interaction_type::create(V_reflected, Np); + iso_reflected.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_reflected = bxdf_type::anisotropic_interaction_type::create(iso_reflected); + + value_weight_type eval_double_t = nested_brdf.evalAndWeight(_sample, interaction_reflected); + eval += eval_double_t.value() * (scalar_type(1.0) - lambda_p) * shadowing; + } + + anisocache_type _cache; + _cache.aniso_cache = __createChildCache(_sample, interaction); + return value_weight_type::create(eval, forwardPdf(_sample, interaction, _cache)); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + _cache.sampleIsShadowed = false; + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + return nested_brdf.generate(interaction_N, u, _cache.aniso_cache); + } + + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type clampedNdotNp = hlsl::max(scalar_type(0.0), NdotNp); + const scalar_type NpdotV = interaction.getNdotV(); + const scalar_type NtdotV = hlsl::dot(Nt, V.getDirection()); + + sample_type s; + if (u.x < shadowing_method_type::lambdaP(NdotNp, hlsl::max(scalar_type(0.0), NpdotV), hlsl::max(scalar_type(0.0), NtdotV))) + { + // sample on Np + s = nested_brdf.generate(interaction, u, _cache.aniso_cache); + _cache.sampleFromNt = false; + + if (!s.isValid()) + return sample_type::createInvalid(); + + const vector3_type L = s.getL().getDirection(); + const scalar_type shadowed = shadowing_method_type::G1(hlsl::max(scalar_type(0.0), hlsl::dot(shadingNormal, L)), clampedNdotNp, + s.getNdotL(BxDFClampMode::BCM_MAX), hlsl::max(scalar_type(0.0), hlsl::dot(Nt, L))); + + if (u.y > shadowed) + { + // if sample dir shadowed, reflect on Nt + Reflect reflect_s = Reflect::create(-L, Nt); + ray_dir_info_type s_reflected; + s_reflected.setDirection(hlsl::normalize(reflect_s())); + s = sample_type::create(s_reflected, Np); + _cache.sampleIsShadowed = true; + _cache.aniso_cache = __createChildCache(s, interaction); + } + } + else + { + // do one reflection if we start at Nt + Reflect reflect_V = Reflect::create(-V.getDirection(), Nt); + const vector3_type V_negreflected = hlsl::normalize(reflect_V()); + _cache.sampleFromNt = true; + + // sample on Np + ray_dir_info_type Vnr; + Vnr.setDirection(V_negreflected); + typename bxdf_type::isotropic_interaction_type iso_negreflected = bxdf_type::isotropic_interaction_type::create(Vnr, Np); + iso_negreflected.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_negreflected = bxdf_type::anisotropic_interaction_type::create(iso_negreflected); + s = nested_brdf.generate(interaction_negreflected, u, _cache.aniso_cache); + _cache.sampleIsShadowed = true; + if (!s.isValid()) + return s; + } + if (hlsl::dot(shadingNormal, s.getL().getDirection()) < scalar_type(0.0)) + return sample_type::createInvalid(); // for reflection + return s; + } + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, _cache); + // _cache.iso_cache = _cache.aniso_cache.isotropic; // TODO: remove? + return s; + } + + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + return forwardPdf(_sample, anisotropic_interaction_type::create(interaction), _cache); + } + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + const sample_type sample_N = sample_type::create(_sample.getL(), shadingNormal); + return nested_brdf.forwardPdf(sample_N, interaction_N, __createChildCache(sample_N, interaction_N)); + } + + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type clampedNdotNp = hlsl::max(scalar_type(0.0), NdotNp); + const scalar_type NpdotV = interaction.getNdotV(); + const scalar_type NtdotV = hlsl::dot(Nt, V.getDirection()); + + scalar_type pdf = scalar_type(0.0); + const scalar_type lambda_p = shadowing_method_type::lambdaP(NdotNp, hlsl::max(scalar_type(0.0), NpdotV), hlsl::max(scalar_type(0.0), NtdotV)); + + if (lambda_p > scalar_type(0.0)) + { + const vector3_type L = _sample.getL().getDirection(); + const scalar_type NdotL = hlsl::dot(shadingNormal, L); + const scalar_type NpdotL = _sample.getNdotL(BxDFClampMode::BCM_MAX); + const scalar_type NtdotL = hlsl::dot(Nt, L); + pdf += lambda_p * nested_brdf.forwardPdf(_sample, interaction, __createChildCache(_sample, interaction)) * shadowing_method_type::G1(hlsl::max(scalar_type(0.0), NdotL), clampedNdotNp, + NpdotL, hlsl::max(scalar_type(0.0), NtdotL)); + + if (NtdotL > numeric_limits::min) + { + Reflect reflectL = Reflect::create(L, Nt); + ray_dir_info_type L_reflected; + L_reflected.setDirection(hlsl::normalize(reflectL(NtdotL))); + sample_type sample_reflected = sample_type::create(L_reflected, Np); + + pdf += lambda_p * nested_brdf.forwardPdf(sample_reflected, interaction, __createChildCache(sample_reflected, interaction)) * + (scalar_type(1.0) - shadowing_method_type::G1(hlsl::max(scalar_type(0.0), hlsl::dot(shadingNormal, L_reflected.getDirection())), clampedNdotNp, sample_reflected.getNdotL(BxDFClampMode::BCM_MAX), hlsl::max(scalar_type(0.0), NtdotL))); + } + } + + if (lambda_p < scalar_type(1.0) && NtdotV > numeric_limits::min) + { + Reflect reflectV = Reflect::create(V.getDirection(), Nt); + ray_dir_info_type V_reflected; + V_reflected.setDirection(hlsl::normalize(reflectV(NtdotV))); + + typename bxdf_type::isotropic_interaction_type iso_reflected = bxdf_type::isotropic_interaction_type::create(V_reflected, Np); + iso_reflected.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_reflected = bxdf_type::anisotropic_interaction_type::create(iso_reflected); + + pdf += (scalar_type(1.0) - lambda_p) * nested_brdf.forwardPdf(_sample, interaction_reflected, __createChildCache(_sample, interaction_reflected)); + } + + return pdf; + } + + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + return quotientAndWeight(_sample, anisotropic_interaction_type::create(interaction), _cache); + } + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + const sample_type sample_N = sample_type::create(_sample.getL(), shadingNormal); + typename bxdf_type::anisocache_type cache_N = __createChildCache(sample_N, interaction_N); + return nested_brdf.quotientAndWeight(sample_N, interaction_N, cache_N); + } + + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type NpdotV = interaction.getNdotV(); + const scalar_type NtdotV = hlsl::dot(Nt, V.getDirection()); + const scalar_type lambda_p = shadowing_method_type::lambdaP(NdotNp, hlsl::max(scalar_type(0.0), NpdotV), hlsl::max(scalar_type(0.0), NtdotV)); + spectral_type quo = hlsl::promote(1.0); + + if (_cache.sampleFromNt) + { + Reflect reflect_V = Reflect::create(-V.getDirection(), Nt); + const vector3_type V_negreflected = hlsl::normalize(reflect_V()); + + ray_dir_info_type Vnr; + Vnr.setDirection(V_negreflected); + typename bxdf_type::isotropic_interaction_type iso_negreflected = bxdf_type::isotropic_interaction_type::create(Vnr, Np); + iso_negreflected.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_negreflected = bxdf_type::anisotropic_interaction_type::create(iso_negreflected); + + quotient_weight_type qw = nested_brdf.quotientAndWeight(_sample, interaction_negreflected, __createChildCache(_sample, interaction_negreflected)); + value_weight_type vw_other = nested_brdf.evalAndWeight(_sample, interaction_negreflected); + quo *= __calculateQuotient(qw.quotient(), vw_other.value(), scalar_type(1.0) - lambda_p, qw.weight(), vw_other.weight()); + } + else + { + quotient_weight_type qw = nested_brdf.quotientAndWeight(_sample, interaction, _cache.aniso_cache); + value_weight_type vw_other = nested_brdf.evalAndWeight(_sample, interaction); + quo *= __calculateQuotient(qw.quotient(), vw_other.value(), lambda_p, qw.weight(), vw_other.weight()); + } + + if (_cache.sampleIsShadowed) + { + const vector3_type L = _sample.getL().getDirection(); + const scalar_type NpdotL = _sample.getNdotL(BxDFClampMode::BCM_MAX); + quo *= shadowing_method_type::G1(hlsl::max(scalar_type(0.0), hlsl::dot(shadingNormal, L)), hlsl::max(scalar_type(0.0), NdotNp), NpdotL, hlsl::max(scalar_type(0.0), hlsl::dot(Nt, L))); + } + + return quotient_weight_type::create(quo, forwardPdf(_sample, interaction, _cache)); + } + + bxdf_type nested_brdf; + vector3_type shadingNormal; + matrix3x3_type shadingBasis; +}; + +template +NBL_PARTIAL_REQ_TOP(config_concepts::BasicConfiguration) +struct SMicrofacetNormals) > +{ + using this_t = SMicrofacetNormals; + BXDF_CONFIG_TYPE_ALIASES(Config); + + NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = false; // TODO: could probably merge with transmitted 1st order + using bxdf_type = BRDF; + using random_type = conditional_t; + struct Cache + { + typename bxdf_type::isocache_type iso_cache; + typename bxdf_type::anisocache_type aniso_cache; + bool sampleFromNt; + bool sampleIsShadowed; + }; + using isocache_type = Cache; + using anisocache_type = Cache; + using matrix3x3_type = matrix; + + using shadowing_method_type = ndf::ShadowingMethod; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; + + // perturbed normal Np stored in interaction + // shading normal N (geometric normal in paper) stored in bxdf + // tangent normal Nt derived as needed + template // TODO: concept for accessor + anisotropic_interaction_type buildInteraction(NBL_CONST_REF_ARG(NormalsTexAccessor) normalMap, const vector2_type uv, const matrix object_to_world, const ray_dir_info_type V) NBL_CONST_MEMBER_FUNC + { + const matrix TBN = hlsl::transpose(object_to_world); + vector3_type localN; + normalMap.get(localN, TBN[2], TBN[0], TBN[1]); + // normalMap.get(localN, uv); + localN = hlsl::promote(2.0) * localN - hlsl::promote(1.0); + + const vector3_type N = hlsl::normalize(hlsl::mul(object_to_world, localN)); + isotropic_interaction_type interaction = isotropic_interaction_type::create(V, N); + interaction.luminosityContributionHint = colorspace::scRGBtoXYZ[1]; + return anisotropic_interaction_type::create(interaction); + } + + template::IsMicrofacet> NBL_FUNC_REQUIRES(C::value && !traits::IsMicrofacet) + static typename bxdf_type::anisocache_type __createChildCache(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + typename bxdf_type::anisocache_type cache; + return cache; + } + template::IsMicrofacet> NBL_FUNC_REQUIRES(C::value && traits::IsMicrofacet) + static typename bxdf_type::anisocache_type __createChildCache(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return bxdf_type::anisocache_type::template createForReflection(interaction, _sample); + } + + static spectral_type __calculateQuotient(const spectral_type quo, const spectral_type quo_other, const scalar_type choiceProb, const scalar_type pdf, const scalar_type pdf_other) + { + const scalar_type weight = pdf / (pdf + pdf_other); // balance heuristic + return (quo * weight / choiceProb) / (scalar_type(1.0) + pdf_other * (scalar_type(1.0) - choiceProb) / (pdf * choiceProb)) + + (quo_other * (scalar_type(1.0) - weight)) / (pdf_other * (scalar_type(1.0) - choiceProb) + (pdf * choiceProb)); + } + + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + return evalAndWeight(_sample, anisotropic_interaction_type::create(interaction)); + } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + const sample_type sample_N = sample_type::create(_sample.getL(), shadingNormal); + return nested_brdf.evalAndWeight(sample_N, interaction_N); + } + + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type NpdotV = interaction.getNdotV(); + const scalar_type NtdotV = hlsl::dot(Nt, V.getDirection()); + spectral_type eval = hlsl::promote(0.0); + + const vector3_type L = _sample.getL().getDirection(); + const scalar_type NdotL = hlsl::dot(shadingNormal, L); + const scalar_type NpdotL = _sample.getNdotL(BxDFClampMode::BCM_MAX); + const scalar_type NtdotL = hlsl::dot(Nt, L); + const scalar_type lambda_p = shadowing_method_type::lambdaP(NdotNp, hlsl::max(scalar_type(0.0), NpdotV), hlsl::max(scalar_type(0.0), NtdotV)); + const scalar_type shadowing = shadowing_method_type::G1(hlsl::max(scalar_type(0.0), NdotL), NdotNp, + NpdotL, hlsl::max(scalar_type(0.0), NtdotL)); + + // i -> p -> o + { + value_weight_type eval_p = nested_brdf.evalAndWeight(_sample, interaction); + eval += eval_p.value() * lambda_p * shadowing; + } + + // i -> t -> o + if (NtdotV > scalar_type(0.0)) + { + sample_type sample_t = sample_type::create(_sample.getL(), Nt); + + typename bxdf_type::isotropic_interaction_type iso_t = bxdf_type::isotropic_interaction_type::create(V, Nt); + iso_t.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_t = bxdf_type::anisotropic_interaction_type::create(iso_t); + + const scalar_type shadowing_t = shadowing_method_type::G1(hlsl::max(scalar_type(0.0), NdotL), hlsl::dot(shadingNormal, Nt), + NpdotL, hlsl::max(scalar_type(0.0), NtdotL), true); + + value_weight_type eval_t = nested_brdf.evalAndWeight(sample_t, interaction_t); + eval += eval_t.value() * (scalar_type(1.0) - lambda_p) * shadowing_t; + } + + anisocache_type _cache; + _cache.aniso_cache = __createChildCache(_sample, interaction); + return value_weight_type::create(eval, forwardPdf(_sample, interaction, _cache)); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + _cache.sampleIsShadowed = false; + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + return nested_brdf.generate(interaction_N, u, _cache.aniso_cache); + } + + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type NpdotV = interaction.getNdotV(); + const scalar_type NtdotV = hlsl::dot(Nt, V.getDirection()); + + sample_type s; + if (u.x < shadowing_method_type::lambdaP(NdotNp, hlsl::max(scalar_type(0.0), NpdotV), hlsl::max(scalar_type(0.0), NtdotV))) + { + // sample on Np + s = nested_brdf.generate(interaction, u, _cache.aniso_cache); + _cache.sampleFromNt = false; + } + else + { + // sample on Nt + typename bxdf_type::isotropic_interaction_type iso_t = bxdf_type::isotropic_interaction_type::create(V, Nt); + iso_t.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_t = bxdf_type::anisotropic_interaction_type::create(iso_t); + s = nested_brdf.generate(interaction_t, u, _cache.aniso_cache); + _cache.sampleFromNt = true; + _cache.sampleIsShadowed = true; + } + if (!s.isValid() || hlsl::dot(shadingNormal, s.getL().getDirection()) < scalar_type(0.0)) + return sample_type::createInvalid(); + return s; + } + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, _cache); + // _cache.iso_cache = _cache.aniso_cache.isotropic; // TODO: remove? + return s; + } + + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + return forwardPdf(_sample, anisotropic_interaction_type::create(interaction), _cache); + } + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + const sample_type sample_N = sample_type::create(_sample.getL(), shadingNormal); + return nested_brdf.forwardPdf(sample_N, interaction_N, __createChildCache(sample_N, interaction_N)); + } + + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type NpdotV = interaction.getNdotV(); + const scalar_type NtdotV = hlsl::dot(Nt, V.getDirection()); + + scalar_type pdf = scalar_type(0.0); + const scalar_type lambda_p = shadowing_method_type::lambdaP(NdotNp, hlsl::max(scalar_type(0.0), NpdotV), hlsl::max(scalar_type(0.0), NtdotV)); + + if (lambda_p > scalar_type(0.0)) + { + const vector3_type L = _sample.getL().getDirection(); + const scalar_type NdotL = hlsl::dot(shadingNormal, L); + const scalar_type NpdotL = _sample.getNdotL(BxDFClampMode::BCM_MAX); + const scalar_type NtdotL = hlsl::dot(Nt, L); + pdf += lambda_p * nested_brdf.forwardPdf(_sample, interaction, _cache.aniso_cache) + * shadowing_method_type::G1(hlsl::max(scalar_type(0.0), NdotL), NdotNp, NpdotL, hlsl::max(scalar_type(0.0), NtdotL)); + } + + if (lambda_p < scalar_type(1.0) && NtdotV > numeric_limits::min) + { + sample_type sample_t = sample_type::create(_sample.getL(), Nt); + + typename bxdf_type::isotropic_interaction_type iso_t = bxdf_type::isotropic_interaction_type::create(V, Nt); + iso_t.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_t = bxdf_type::anisotropic_interaction_type::create(iso_t); + + pdf += (scalar_type(1.0) - lambda_p) * nested_brdf.forwardPdf(sample_t, interaction_t, __createChildCache(sample_t, interaction_t)); + } + + return pdf; + } + + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + return quotientAndWeight(_sample, anisotropic_interaction_type::create(interaction), _cache); + } + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + const sample_type sample_N = sample_type::create(_sample.getL(), shadingNormal); + typename bxdf_type::anisocache_type cache_N = __createChildCache(sample_N, interaction_N); + return nested_brdf.quotientAndWeight(sample_N, interaction_N, cache_N); + } + + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type NpdotV = interaction.getNdotV(); + const scalar_type NtdotV = hlsl::dot(Nt, V.getDirection()); + const scalar_type lambda_p = shadowing_method_type::lambdaP(NdotNp, hlsl::max(scalar_type(0.0), NpdotV), hlsl::max(scalar_type(0.0), NtdotV)); + spectral_type quo = hlsl::promote(1.0); + + if (_cache.sampleFromNt) + { + typename bxdf_type::isotropic_interaction_type iso_t = bxdf_type::isotropic_interaction_type::create(V, Nt); + iso_t.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_t = bxdf_type::anisotropic_interaction_type::create(iso_t); + + quotient_weight_type qw = nested_brdf.quotientAndWeight(_sample, interaction_t, __createChildCache(_sample, interaction_t)); + value_weight_type vw_other = nested_brdf.evalAndWeight(_sample, interaction_t); + quo *= __calculateQuotient(qw.quotient(), vw_other.value(), scalar_type(1.0) - lambda_p, qw.weight(), vw_other.weight()); + } + else + { + quotient_weight_type qw = nested_brdf.quotientAndWeight(_sample, interaction, _cache.aniso_cache); + value_weight_type vw_other = nested_brdf.evalAndWeight(_sample, interaction); + quo *= __calculateQuotient(qw.quotient(), vw_other.value(), lambda_p, qw.weight(), vw_other.weight()); + } + + if (_cache.sampleIsShadowed) + { + const vector3_type L = _sample.getL().getDirection(); + const scalar_type NpdotL = _sample.getNdotL(BxDFClampMode::BCM_MAX); + quo *= shadowing_method_type::G1(hlsl::max(scalar_type(0.0), hlsl::dot(shadingNormal, L)), hlsl::max(scalar_type(0.0), hlsl::dot(shadingNormal, Nt)), NpdotL, hlsl::max(scalar_type(0.0), hlsl::dot(Nt, L)), true); + } + + return quotient_weight_type::create(quo, forwardPdf(_sample, interaction, _cache)); + } + + bxdf_type nested_brdf; + vector3_type shadingNormal; + matrix3x3_type shadingBasis; +}; + + +} + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; // should be microfacet? + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; + NBL_CONSTEXPR_STATIC_INLINE bool TractablePdf = true; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl index b5b6e101c1..e9ad84f7c5 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl @@ -11,6 +11,7 @@ #include "nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl" #include "nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission/microfacet_normals.hlsl" namespace nbl { diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index c2ece15686..149bb4f4cb 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -56,11 +56,11 @@ struct SDeltaDistribution scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return 0; + return bit_cast(numeric_limits::infinity); } scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return 0; + return bit_cast(numeric_limits::infinity); } quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/microfacet_normals.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/microfacet_normals.hlsl new file mode 100644 index 0000000000..9ba829665c --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/microfacet_normals.hlsl @@ -0,0 +1,322 @@ +// 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_MICROFACET_NORMALS_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_MICROFACET_NORMALS_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/bxdf/config.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf/microfacet_normal_shadowing.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template +struct SMicrofacetNormals; + +template +NBL_PARTIAL_REQ_TOP(config_concepts::BasicConfiguration) +struct SMicrofacetNormals) > +{ + using this_t = SMicrofacetNormals; + BXDF_CONFIG_TYPE_ALIASES(Config); + + NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = true; // TODO: might combine with brdf version? + using bxdf_type = BRDF; + using random_type = conditional_t; + struct Cache + { + typename bxdf_type::isocache_type iso_cache; + typename bxdf_type::anisocache_type aniso_cache; + bool sampleFromNt; + bool sampleIsShadowed; + }; + using isocache_type = Cache; + using anisocache_type = Cache; + using matrix3x3_type = matrix; + + using shadowing_method_type = ndf::ShadowingMethod; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; + + // perturbed normal Np stored in interaction + // shading normal N (geometric normal in paper) stored in bxdf + // tangent normal Nt derived as needed + template // TODO: concept for accessor + anisotropic_interaction_type buildInteraction(NBL_CONST_REF_ARG(NormalsTexAccessor) normalMap, const vector2_type uv, const matrix object_to_world, const ray_dir_info_type V) NBL_CONST_MEMBER_FUNC + { + const matrix TBN = hlsl::transpose(object_to_world); + vector3_type localN; + normalMap.get(localN, TBN[2], TBN[0], TBN[1]); + // normalMap.get(localN, uv); + localN = hlsl::promote(2.0) * localN - hlsl::promote(1.0); + + const vector3_type N = hlsl::normalize(hlsl::mul(object_to_world, localN)); + isotropic_interaction_type interaction = isotropic_interaction_type::create(V, N); + interaction.luminosityContributionHint = colorspace::scRGBtoXYZ[1]; + return anisotropic_interaction_type::create(interaction); + } + + template::IsMicrofacet> NBL_FUNC_REQUIRES(C::value && !traits::IsMicrofacet) + typename bxdf_type::anisocache_type __createChildCache(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + typename bxdf_type::anisocache_type cache; + return cache; + } + template::IsMicrofacet> NBL_FUNC_REQUIRES(C::value && traits::IsMicrofacet) + typename bxdf_type::anisocache_type __createChildCache(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + const scalar_type eta = nested_bsdf.fresnel.getRefractionOrientedEta(); + using oriented_etas_t = fresnel::OrientedEtas; + oriented_etas_t orientedEta = oriented_etas_t::create(scalar_type(1.0), hlsl::promote(eta)); + return bxdf_type::anisocache_type::template create(interaction, _sample, orientedEta); + } + + static spectral_type __calculateQuotient(const spectral_type quo, const spectral_type quo_other, const scalar_type choiceProb, const scalar_type pdf, const scalar_type pdf_other) + { + const scalar_type weight = pdf / (pdf + pdf_other); // balance heuristic + return (quo * weight / choiceProb) / (scalar_type(1.0) + pdf_other * (scalar_type(1.0) - choiceProb) / (pdf * choiceProb)) + + (quo_other * (scalar_type(1.0) - weight)) / (pdf_other * (scalar_type(1.0) - choiceProb) + (pdf * choiceProb)); + } + + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + return evalAndWeight(_sample, anisotropic_interaction_type::create(interaction)); + } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + const sample_type sample_N = sample_type::create(_sample.getL(), shadingNormal); + return nested_bsdf.evalAndWeight(sample_N, interaction_N); + } + + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type NpdotV = interaction.getNdotV(); + const scalar_type NtdotV = hlsl::dot(Nt, V.getDirection()); + spectral_type eval = hlsl::promote(0.0); + + const vector3_type L = _sample.getL().getDirection(); + const scalar_type NdotL = hlsl::dot(shadingNormal, L); + const scalar_type NpdotL = _sample.getNdotL(BxDFClampMode::BCM_ABS); + const scalar_type NtdotL = hlsl::dot(Nt, L); + const scalar_type lambda_p = shadowing_method_type::lambdaP(NdotNp, hlsl::abs(NpdotV), hlsl::abs(NtdotV)); + const scalar_type shadowing = shadowing_method_type::G1(hlsl::abs(NdotL), NdotNp, + NpdotL, hlsl::abs(NtdotL)); + + // i -> p -> o + { + value_weight_type eval_p = nested_bsdf.evalAndWeight(_sample, interaction); + eval += eval_p.value() * lambda_p * shadowing; + } + + // i -> t -> o + if (NtdotV > scalar_type(0.0)) + { + sample_type sample_t = sample_type::create(_sample.getL(), Nt); + + typename bxdf_type::isotropic_interaction_type iso_t = bxdf_type::isotropic_interaction_type::create(V, Nt); + iso_t.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_t = bxdf_type::anisotropic_interaction_type::create(iso_t); + + const scalar_type shadowing_t = shadowing_method_type::G1(hlsl::abs(NdotL), hlsl::dot(shadingNormal, Nt), + NpdotL, hlsl::abs(NtdotL), true); + + value_weight_type eval_t = nested_bsdf.evalAndWeight(sample_t, interaction_t); + eval += eval_t.value() * (scalar_type(1.0) - lambda_p) * shadowing_t; + } + + anisocache_type _cache; + _cache.aniso_cache = __createChildCache(_sample, interaction); + return value_weight_type::create(eval, forwardPdf(_sample, interaction, _cache)); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + _cache.sampleIsShadowed = false; + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + return nested_bsdf.generate(interaction_N, u, _cache.aniso_cache); + } + + const scalar_type NdotV = hlsl::dot(shadingNormal, V.getDirection()); + const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative(V.getDirection(), hlsl::promote(NdotV)); + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type NpdotV = hlsl::dot(Np, upperHemisphereV); + const scalar_type NtdotV = hlsl::dot(Nt, upperHemisphereV); + + sample_type s; + if (u.x < shadowing_method_type::lambdaP(NdotNp, hlsl::max(scalar_type(0.0), NpdotV), hlsl::max(scalar_type(0.0), NtdotV))) + { + // sample on Np + s = nested_bsdf.generate(interaction, u, _cache.aniso_cache); + _cache.sampleFromNt = false; + } + else + { + // sample on Nt + typename bxdf_type::isotropic_interaction_type iso_t = bxdf_type::isotropic_interaction_type::create(V, Nt); + iso_t.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_t = bxdf_type::anisotropic_interaction_type::create(iso_t); + s = nested_bsdf.generate(interaction_t, u, _cache.aniso_cache); + _cache.sampleFromNt = true; + _cache.sampleIsShadowed = true; + } + if (!s.isValid()) + return sample_type::createInvalid(); + return s; + } + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, _cache); + // _cache.iso_cache = _cache.aniso_cache.isotropic; // TODO: remove? + return s; + } + + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + return forwardPdf(_sample, anisotropic_interaction_type::create(interaction), _cache); + } + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + const sample_type sample_N = sample_type::create(_sample.getL(), shadingNormal); + return nested_bsdf.forwardPdf(sample_N, interaction_N, __createChildCache(sample_N, interaction_N)); + } + + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type NpdotV = interaction.getNdotV(); + const scalar_type NtdotV = hlsl::dot(Nt, V.getDirection()); + + scalar_type pdf = scalar_type(0.0); + const scalar_type lambda_p = shadowing_method_type::lambdaP(NdotNp, hlsl::abs(NpdotV), hlsl::abs(NtdotV)); + + if (lambda_p > scalar_type(0.0)) + { + const vector3_type L = _sample.getL().getDirection(); + const scalar_type NdotL = hlsl::dot(shadingNormal, L); + const scalar_type NpdotL = _sample.getNdotL(BxDFClampMode::BCM_ABS); + const scalar_type NtdotL = hlsl::dot(Nt, L); + pdf += lambda_p * nested_bsdf.forwardPdf(_sample, interaction, _cache.aniso_cache) + * shadowing_method_type::G1(hlsl::abs(NdotL), NdotNp, NpdotL, hlsl::abs(NtdotL)); + } + + if (lambda_p < scalar_type(1.0) && NtdotV > numeric_limits::min) + { + sample_type sample_t = sample_type::create(_sample.getL(), Nt); + + typename bxdf_type::isotropic_interaction_type iso_t = bxdf_type::isotropic_interaction_type::create(V, Nt); + iso_t.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_t = bxdf_type::anisotropic_interaction_type::create(iso_t); + + pdf += (scalar_type(1.0) - lambda_p) * nested_bsdf.forwardPdf(sample_t, interaction_t, __createChildCache(sample_t, interaction_t)); + } + + return pdf; + } + + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + return quotientAndWeight(_sample, anisotropic_interaction_type::create(interaction), _cache); + } + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + { + const vector3_type Np = interaction.getN(); + const scalar_type NdotNp = hlsl::dot(shadingNormal, Np); + + const ray_dir_info_type V = interaction.getV(); + if (NdotNp > scalar_type(1.0 - 1e-5)) + { + typename bxdf_type::isotropic_interaction_type iso = bxdf_type::isotropic_interaction_type::create(V, shadingNormal); + iso.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_N = bxdf_type::anisotropic_interaction_type::create(iso); + const sample_type sample_N = sample_type::create(_sample.getL(), shadingNormal); + typename bxdf_type::anisocache_type cache_N = __createChildCache(sample_N, interaction_N); + return nested_bsdf.quotientAndWeight(sample_N, interaction_N, cache_N); + } + + const vector3_type Nt = shadowing_method_type::computeNt(Np, shadingBasis); + const scalar_type NpdotV = interaction.getNdotV(); + const scalar_type NtdotV = hlsl::dot(Nt, V.getDirection()); + const scalar_type lambda_p = shadowing_method_type::lambdaP(NdotNp, hlsl::abs(NpdotV), hlsl::abs(NtdotV)); + spectral_type quo = hlsl::promote(1.0); + + if (_cache.sampleFromNt) + { + typename bxdf_type::isotropic_interaction_type iso_t = bxdf_type::isotropic_interaction_type::create(V, Nt); + iso_t.luminosityContributionHint = interaction.getLuminosityContributionHint(); + typename bxdf_type::anisotropic_interaction_type interaction_t = bxdf_type::anisotropic_interaction_type::create(iso_t); + + quotient_weight_type qw = nested_bsdf.quotientAndWeight(_sample, interaction_t, __createChildCache(_sample, interaction_t)); + value_weight_type vw_other = nested_bsdf.evalAndWeight(_sample, interaction_t); + quo *= __calculateQuotient(qw.quotient(), vw_other.value(), scalar_type(1.0) - lambda_p, qw.weight(), vw_other.weight()); + } + else + { + quotient_weight_type qw = nested_bsdf.quotientAndWeight(_sample, interaction, _cache.aniso_cache); + value_weight_type vw_other = nested_bsdf.evalAndWeight(_sample, interaction); + quo *= __calculateQuotient(qw.quotient(), vw_other.value(), lambda_p, qw.weight(), vw_other.weight()); + } + + if (_cache.sampleIsShadowed) + { + const vector3_type L = _sample.getL().getDirection(); + const scalar_type NpdotL = _sample.getNdotL(BxDFClampMode::BCM_ABS); + quo *= shadowing_method_type::G1(hlsl::abs(hlsl::dot(shadingNormal, L)), hlsl::abs(hlsl::dot(shadingNormal, Nt)), NpdotL, hlsl::abs(hlsl::dot(Nt, L)), true); + } + + return quotient_weight_type::create(quo, forwardPdf(_sample, interaction, _cache)); + } + + bxdf_type nested_bsdf; + vector3_type shadingNormal; + matrix3x3_type shadingBasis; +}; + + +} + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; // should be microfacet? + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; + NBL_CONSTEXPR_STATIC_INLINE bool TractablePdf = true; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index 06266b1f45..b44823dd88 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -64,11 +64,11 @@ struct SSmoothDielectric scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return 0; + return bit_cast(numeric_limits::infinity); } scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return 0; + return bit_cast(numeric_limits::infinity); } // smooth BxDFs are isotropic by definition @@ -153,11 +153,11 @@ struct SThinSmoothDielectric scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return 0; + return bit_cast(numeric_limits::infinity); } scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return 0; + return bit_cast(numeric_limits::infinity); } // smooth BxDFs are isotropic by definition diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 12b4af1bef..b2492c15fe 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -310,6 +310,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/bxdf_traits.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/ndf/beckmann.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/ndf/ggx.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/ndf/microfacet_normal_shadowing.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/base/lambertian.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/base/oren_nayar.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/base/cook_torrance_base.hlsl") @@ -319,6 +320,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/lambertian.hl 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/reflection/delta_distribution.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/microfacet_normals.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") @@ -326,6 +328,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/oren_nayar. 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") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/delta_distribution.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/microfacet_normals.hlsl") #path tracing LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/path_tracing/concepts.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/path_tracing/unidirectional.hlsl")