diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl new file mode 100644 index 0000000000..d473439351 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -0,0 +1,384 @@ +// 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_COOK_TORRANCE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_COOK_TORRANCE_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/bxdf/config.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf.hlsl" +#include "nbl/builtin/hlsl/bxdf/fresnel.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ + +namespace impl +{ +template +struct __implicit_promote; + +template +struct __implicit_promote +{ + static T __call(const T v) + { + return v; + } +}; + +template +struct __implicit_promote::scalar_type, 1> > +{ + static T __call(const vector::scalar_type, 1> v) + { + return hlsl::promote(v[0]); + } +}; + +template +struct quant_query_helper; + +template +struct quant_query_helper +{ + using quant_query_type = typename N::quant_query_type; + + template + static quant_query_type __call(NBL_REF_ARG(N) ndf, NBL_CONST_REF_ARG(F) fresnel, NBL_CONST_REF_ARG(C) cache) + { + return ndf.template createQuantQuery(cache, fresnel.orientedEta.value[0]); + } +}; + +template +struct quant_query_helper +{ + using quant_query_type = typename N::quant_query_type; + + template + static quant_query_type __call(NBL_REF_ARG(N) ndf, NBL_CONST_REF_ARG(F) fresnel, NBL_CONST_REF_ARG(C) cache) + { + typename N::scalar_type dummy; + return ndf.template createQuantQuery(cache, dummy); + } +}; + +template +struct checkValid; + +template +struct checkValid +{ + using scalar_type = typename F::scalar_type; + + template + static bool __call(NBL_CONST_REF_ARG(F) fresnel, NBL_CONST_REF_ARG(Sample) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + return _sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min; + } +}; + +template +struct checkValid +{ + using vector_type = typename F::vector_type; // expect monochrome + + template + static bool __call(NBL_CONST_REF_ARG(F) fresnel, NBL_CONST_REF_ARG(Sample) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + return cache.isValid(fresnel.getRefractionOrientedEta()); + } +}; + +template +struct getOrientedFresnel; + +template +struct getOrientedFresnel +{ + static F __call(NBL_CONST_REF_ARG(F) fresnel, typename F::scalar_type NdotV) + { + // expect conductor fresnel + return fresnel; + } +}; + +template +NBL_PARTIAL_REQ_TOP(fresnel::TwoSidedFresnel) +struct getOrientedFresnel) > +{ + using scalar_type = typename F::scalar_type; + + static F __call(NBL_CONST_REF_ARG(F) fresnel, scalar_type NdotV) + { + return fresnel.getReorientedFresnel(NdotV); + } +}; + +template +struct overwrite_DG +{ + using scalar_type = typename N::scalar_type; + using quant_type = typename N::quant_type; + using quant_query_type = typename N::quant_query_type; + using g2g1_query_type = typename N::g2g1_query_type; + NBL_CONSTEXPR_STATIC_INLINE bool HasOverwrite = ndf::NDF_CanOverwriteDG; + + template > + static enable_if_t __call(NBL_REF_ARG(scalar_type) DG, N ndf, NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + } + template > + static enable_if_t __call(NBL_REF_ARG(scalar_type) DG, N ndf, NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + quant_type dg = ndf.template Dcorrelated(query, quant_query, _sample, interaction, cache); + DG = dg.projectedLightMeasure; + } +}; +} + +// N (NDF), F (fresnel) +template && ndf::NDF && fresnel::Fresnel) +struct SCookTorrance +{ + MICROFACET_BXDF_CONFIG_TYPE_ALIASES(Config); + + using this_t = SCookTorrance; + using quant_type = typename N::quant_type; + using ndf_type = N; + using fresnel_type = F; + + NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = ndf_type::IsAnisotropic; + NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = ndf_type::SupportedPaths != ndf::MTT_REFLECT; + NBL_HLSL_BXDF_ANISOTROPIC_COND_DECLS(IsAnisotropic); + + template, + class MicrofacetCache=conditional_t + NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) + spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); + if (!impl::checkValid::template __call(_f, _sample, interaction, cache)) + return hlsl::promote(0.0); + + using quant_query_type = typename ndf_type::quant_query_type; + quant_query_type qq = impl::quant_query_helper::template __call(ndf, _f, cache); + + using g2g1_query_type = typename ndf_type::g2g1_query_type; + g2g1_query_type gq = ndf.template createG2G1Query(_sample, interaction); + + quant_type D = ndf.template D(qq, _sample, interaction, cache); + scalar_type DG = D.projectedLightMeasure; + if (D.microfacetMeasure < bit_cast(numeric_limits::infinity)) + DG *= ndf.template correlated(gq, _sample, interaction); + else + return hlsl::promote(0.0); + + impl::overwrite_DG::__call(DG, ndf, gq, qq, _sample, interaction, cache); + + scalar_type clampedVdotH = cache.getVdotH(); + NBL_IF_CONSTEXPR(IsBSDF) + clampedVdotH = hlsl::abs(clampedVdotH); + return impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; + } + + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) + { + { + const scalar_type NdotV = interaction.getNdotV(); + if (NdotV < numeric_limits::min) + return sample_type::createInvalid(); + assert(NdotV != bit_cast(numeric_limits::quiet_NaN)); + } + + const vector3_type localV = interaction.getTangentSpaceV(); + const vector3_type localH = ndf.generateH(localV, u); + const scalar_type VdotH = hlsl::dot(localV, localH); + assert(VdotH >= scalar_type(0.0)); // VNDF sampling guarantees VdotH has same sign as NdotV (should be positive for BRDF) + + const scalar_type NdotL = scalar_type(2.0) * VdotH * localH.z - localV.z; + ray_dir_info_type L; + if (NdotL > 0) // compiler's Common Subexpression Elimination pass should re-use 2*VdotH later + { + ray_dir_info_type V = interaction.getV(); + const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); + struct reflect_wrapper // so we don't recalculate VdotH + { + vector3_type operator()() NBL_CONST_MEMBER_FUNC + { + return r(VdotH); + } + bxdf::Reflect r; + scalar_type VdotH; + }; + reflect_wrapper rw; + rw.r = bxdf::Reflect::create(V.getDirection(), H); + rw.VdotH = VdotH; + L = V.template reflect(rw); + + cache = anisocache_type::createForReflection(localV, localH); + + const vector3_type T = interaction.getT(); + const vector3_type B = interaction.getB(); + + return sample_type::create(L, T, B, NdotL); + } + else // fail if samples have invalid paths + return sample_type::createInvalid(); // should check if sample direction is invalid + } + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(anisocache_type) cache) + { + const vector3_type localV = interaction.getTangentSpaceV(); + const scalar_type NdotV = localV.z; + + fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, NdotV); + fresnel::OrientedEtaRcps rcpEta = _f.getOrientedEtaRcps(); + + const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative(localV, hlsl::promote(NdotV)); + const vector3_type localH = ndf.generateH(upperHemisphereV, u.xy); + const scalar_type VdotH = hlsl::dot(localV, localH); + + assert(NdotV * VdotH > scalar_type(0.0)); + const scalar_type reflectance = _f(hlsl::abs(VdotH))[0]; + + scalar_type rcpChoiceProb; + scalar_type z = u.z; + bool transmitted = math::partitionRandVariable(reflectance, z, rcpChoiceProb); + + ray_dir_info_type V = interaction.getV(); + const vector3_type _N = interaction.getN(); + const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); + Refract r = Refract::create(V.getDirection(), H); + const scalar_type LdotH = hlsl::mix(VdotH, r.getNdotT(rcpEta.value2[0]), transmitted); + + // fail if samples have invalid paths + const scalar_type viewShortenFactor = hlsl::mix(scalar_type(1.0), rcpEta.value[0], transmitted); + const scalar_type NdotL = localH.z * (VdotH * viewShortenFactor + LdotH) - NdotV * viewShortenFactor; + // VNDF sampling guarantees that `VdotH` has same sign as `NdotV` + // and `transmitted` controls the sign of `LdotH` relative to `VdotH` by construction (reflect -> same sign, or refract -> opposite sign) + if ((ComputeMicrofacetNormal::isTransmissionPath(NdotV, NdotL) != transmitted)) + return sample_type::createInvalid(); // should check if sample direction is invalid + + cache = anisocache_type::createPartial(VdotH, LdotH, localH.z, transmitted, rcpEta); + assert(cache.isValid()); + + struct reflect_refract_wrapper // so we don't recalculate LdotH + { + vector3_type operator()(const bool doRefract, const scalar_type rcpOrientedEta) NBL_CONST_MEMBER_FUNC + { + return rr(NdotTorR, rcpOrientedEta); + } + bxdf::ReflectRefract rr; + scalar_type NdotTorR; + }; + bxdf::ReflectRefract rr; // rr.getNdotTorR() and calls to mix as well as a good part of the computations should CSE with our computation of NdotL above + rr.refract = r; + reflect_refract_wrapper rrw; + rrw.rr = rr; + rrw.NdotTorR = LdotH; + ray_dir_info_type L = V.template reflectRefract(rrw, transmitted, rcpEta.value[0]); + + const vector3_type T = interaction.getT(); + const vector3_type B = interaction.getB(); + + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(fresnel.getRefractionOrientedEta())); + assert(ComputeMicrofacetNormal::isValidMicrofacet(transmitted,cache.getVdotL(),localH.z,orientedEta)); + cache.fillTangents(T, B, H); + + return sample_type::create(L, T, B, NdotL); + } + template > + enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const conditional_t u, NBL_REF_ARG(isocache_type) cache) + { + anisocache_type aniso_cache; + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, aniso_cache); + cache = aniso_cache.iso_cache; + return s; + } + + template + scalar_type __pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + using quant_query_type = typename ndf_type::quant_query_type; + using dg1_query_type = typename ndf_type::dg1_query_type; + + dg1_query_type dq = ndf.template createDG1Query(interaction, cache); + + fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); + quant_query_type qq = impl::quant_query_helper::template __call(ndf, _f, cache); + quant_type DG1 = ndf.template DG1(dq, qq, _sample, interaction); + + NBL_IF_CONSTEXPR(IsBSDF) + { + const scalar_type reflectance = _f(hlsl::abs(cache.getVdotH()))[0]; + return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * DG1.projectedLightMeasure; + } + else + { + return DG1.projectedLightMeasure; + } + } + template, + class MicrofacetCache=conditional_t + NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); + if (!impl::checkValid::template __call(_f, _sample, interaction, cache)) + return scalar_type(0.0); + + scalar_type _pdf = __pdf(_sample, interaction, cache); + return hlsl::mix(scalar_type(0.0), _pdf, _pdf < bit_cast(numeric_limits::infinity)); + } + + template, + class MicrofacetCache=conditional_t + NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + if (!_sample.isValid()) + return quotient_pdf_type::create(scalar_type(0.0), scalar_type(0.0)); // set pdf=0 when quo=0 because we don't want to give high weight to sampling strategy that yields 0 contribution + + scalar_type _pdf = __pdf(_sample, interaction, cache); + fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); + + const bool valid = impl::checkValid::template __call(_f, _sample, interaction, cache); + assert(valid); + + scalar_type G2_over_G1 = scalar_type(1.0); + if (_pdf < bit_cast(numeric_limits::infinity)) + { + using g2g1_query_type = typename N::g2g1_query_type; + g2g1_query_type gq = ndf.template createG2G1Query(_sample, interaction); + G2_over_G1 = ndf.template G2_over_G1(gq, _sample, interaction, cache); + } + + spectral_type quo; + NBL_IF_CONSTEXPR(IsBSDF) + quo = hlsl::promote(G2_over_G1); + else + { + const scalar_type VdotH = cache.getVdotH(); + assert(VdotH > scalar_type(0.0)); + quo = _f(VdotH) * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + + ndf_type ndf; + fresnel_type fresnel; // always front-facing +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index 2ea840f217..5565a6b4c4 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -29,26 +29,40 @@ struct SLambertianBase { return hlsl::promote(_sample.getNdotL(_clamp) * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF)); } + spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return eval(_sample, interaction.isotropic); + } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) { - // static_assert(!IsBSDF); ray_dir_info_type L; - L.direction = sampling::ProjectedHemisphere::generate(u); + L.setDirection(sampling::ProjectedHemisphere::generate(u)); return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } - - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u) + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u) { - // static_assert(IsBSDF); + vector3_type _u = u; ray_dir_info_type L; - L.direction = sampling::ProjectedSphere::generate(u); + L.setDirection(sampling::ProjectedSphere::generate(_u)); return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } + template > + enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u) + { + return generate(anisotropic_interaction_type::create(interaction), u); + } + template > + enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector3_type u) + { + return generate(anisotropic_interaction_type::create(interaction), u); + } scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample) { - if (IsBSDF) + NBL_IF_CONSTEXPR (IsBSDF) return sampling::ProjectedSphere::pdf(_sample.getNdotL(_clamp)); else return sampling::ProjectedHemisphere::pdf(_sample.getNdotL(_clamp)); @@ -57,12 +71,16 @@ struct SLambertianBase quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) { sampling::quotient_and_pdf qp; - if (IsBSDF) + NBL_IF_CONSTEXPR (IsBSDF) qp = sampling::ProjectedSphere::template quotient_and_pdf(_sample.getNdotL(_clamp)); else qp = sampling::ProjectedHemisphere::template quotient_and_pdf(_sample.getNdotL(_clamp)); return quotient_pdf_type::create(qp.quotient[0], qp.pdf); } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return quotient_and_pdf(_sample, interaction.isotropic); + } }; } diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index fdf4684cf7..aaf1ac27fe 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -64,22 +64,36 @@ struct SOrenNayarBase query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); return __eval(query, _sample, interaction); } + spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return eval(_sample, interaction.isotropic); + } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) { - // static_assert(!IsBSDF); ray_dir_info_type L; - L.direction = sampling::ProjectedHemisphere::generate(u); + L.setDirection(sampling::ProjectedHemisphere::generate(u)); return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } - - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u) + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u) { - // static_assert(IsBSDF); + vector3_type _u = u; ray_dir_info_type L; - L.direction = sampling::ProjectedSphere::generate(u); + L.setDirection(sampling::ProjectedSphere::generate(_u)); return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } + template > + enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u) + { + return generate(anisotropic_interaction_type::create(interaction), u); + } + template > + enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector3_type u) + { + return generate(anisotropic_interaction_type::create(interaction), u); + } scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample) { @@ -102,6 +116,10 @@ struct SOrenNayarBase query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); return __quotient_and_pdf(query, _sample, interaction); } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return quotient_and_pdf(_sample, interaction.isotropic); + } scalar_type A2; vector2_type AB; diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 2d115652a8..ace68c9efc 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -11,6 +11,7 @@ #include "nbl/builtin/hlsl/ieee754.hlsl" #include "nbl/builtin/hlsl/tgmath.hlsl" #include "nbl/builtin/hlsl/math/functions.hlsl" +// #include "nbl/builtin/hlsl/glsl_compat/core.hlsl" #include "nbl/builtin/hlsl/cpp_compat/promote.hlsl" #include "nbl/builtin/hlsl/bxdf/fresnel.hlsl" #include "nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl" @@ -31,7 +32,7 @@ namespace ray_dir_info #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (rdirinfo, T) -#define NBL_CONCEPT_PARAM_1 (N, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_1 (v, typename T::vector3_type) #define NBL_CONCEPT_PARAM_2 (rcpEta, typename T::scalar_type) #define NBL_CONCEPT_PARAM_3 (m, typename T::matrix3x3_type) #define NBL_CONCEPT_PARAM_4 (rfl, Reflect) @@ -40,7 +41,7 @@ namespace ray_dir_info #define NBL_CONCEPT_PARAM_7 (rr, ReflectRefract) NBL_CONCEPT_BEGIN(8) #define rdirinfo NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define N NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define v NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define rcpEta NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define m NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 #define rfl NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 @@ -51,6 +52,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) ((NBL_CONCEPT_REQ_TYPE)(T::matrix3x3_type)) + ((NBL_CONCEPT_REQ_EXPR)(rdirinfo.setDirection(v))) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.getDirection()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.transmit()), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.reflect(rfl)), ::nbl::hlsl::is_same_v, T)) @@ -58,6 +60,8 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.reflectTransmit(rfl, t)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.reflectRefract(rr, t, rcpEta)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.transform(m)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.isValid()), ::nbl::hlsl::is_same_v, bool)) + ((NBL_CONCEPT_REQ_EXPR)(rdirinfo.makeInvalid())) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(is_scalar_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(is_vector_v, typename T::vector3_type)) ); @@ -67,7 +71,7 @@ NBL_CONCEPT_END( #undef rfl #undef m #undef rcpEta -#undef N +#undef v #undef rdirinfo #include @@ -78,6 +82,7 @@ struct SBasic using vector3_type = vector; using matrix3x3_type = matrix; + void setDirection(const vector3_type v) { direction = v; } vector3_type getDirection() NBL_CONST_MEMBER_FUNC { return direction; } SBasic transmit() NBL_CONST_MEMBER_FUNC @@ -87,28 +92,32 @@ struct SBasic return retval; } - SBasic reflect(NBL_CONST_REF_ARG(Reflect) r) NBL_CONST_MEMBER_FUNC + template > + SBasic reflect(NBL_CONST_REF_ARG(R) r) NBL_CONST_MEMBER_FUNC { SBasic retval; retval.direction = r(); return retval; } - SBasic refract(NBL_CONST_REF_ARG(Refract) r, scalar_type rcpOrientedEta) NBL_CONST_MEMBER_FUNC + template > + SBasic refract(NBL_CONST_REF_ARG(R) r, scalar_type rcpOrientedEta) NBL_CONST_MEMBER_FUNC { SBasic retval; retval.direction = r(rcpOrientedEta); return retval; } - SBasic reflectTransmit(NBL_CONST_REF_ARG(Reflect) r, bool transmitted) NBL_CONST_MEMBER_FUNC + template > + SBasic reflectTransmit(NBL_CONST_REF_ARG(R) r, bool transmitted) NBL_CONST_MEMBER_FUNC { SBasic retval; retval.direction = hlsl::mix(r(), -direction, transmitted); return retval; } - SBasic reflectRefract(NBL_CONST_REF_ARG(ReflectRefract) rr, bool transmitted, scalar_type rcpOrientedEta) NBL_CONST_MEMBER_FUNC + template > + SBasic reflectRefract(NBL_CONST_REF_ARG(R) rr, bool transmitted, scalar_type rcpOrientedEta) NBL_CONST_MEMBER_FUNC { SBasic retval; retval.direction = rr(transmitted, rcpOrientedEta); @@ -128,6 +137,13 @@ struct SBasic return retval; } + void makeInvalid() + { + direction = vector3_type(0,0,0); + } + + bool isValid() NBL_CONST_MEMBER_FUNC { return hlsl::any >(hlsl::glsl::notEqual(direction, hlsl::promote(0.0))); } + vector3_type direction; }; // more to come! @@ -324,13 +340,15 @@ struct SAnisotropic #define NBL_CONCEPT_PARAM_3 (pV, typename T::vector3_type) #define NBL_CONCEPT_PARAM_4 (frame, typename T::matrix3x3_type) #define NBL_CONCEPT_PARAM_5 (clampMode, BxDFClampMode) -NBL_CONCEPT_BEGIN(6) +#define NBL_CONCEPT_PARAM_6 (pNdotL, typename T::scalar_type) +NBL_CONCEPT_BEGIN(7) #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define inter NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define rdirinfo NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define pV NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 #define frame NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 #define clampMode NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define pNdotL NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_type)) ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) @@ -343,13 +361,17 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getBdotL2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getNdotL(clampMode)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getNdotL2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.isValid()), ::nbl::hlsl::is_same_v, bool)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createFromTangentSpace(rdirinfo,frame)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pV)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pV,pV,pV)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pV,pV,pNdotL)), ::nbl::hlsl::is_same_v, T)) // ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create >(pV,inter)), ::nbl::hlsl::is_same_v, T)) // NOTE: temporarily commented out due to dxc bug https://github.com/microsoft/DirectXShaderCompiler/issues/7154 ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getTangentSpaceL()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createInvalid()), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) ); +#undef pNdotL #undef clampMode #undef frame #undef pV @@ -405,6 +427,18 @@ struct SLightSample return retval; } + static this_t create(NBL_CONST_REF_ARG(ray_dir_info_type) L, const vector3_type T, const vector3_type B, const scalar_type NdotL) + { + this_t retval; + + retval.L = L; + retval.TdotL = nbl::hlsl::dot(T,L.getDirection()); + retval.BdotL = nbl::hlsl::dot(B,L.getDirection()); + retval.NdotL = NdotL; + retval.NdotL2 = NdotL * NdotL; + + return retval; + } template) static this_t create(const vector3_type L, NBL_CONST_REF_ARG(SurfaceInteraction) interaction) @@ -435,7 +469,13 @@ struct SLightSample } scalar_type getNdotL2() NBL_CONST_MEMBER_FUNC { return NdotL2; } - bool isValid() NBL_CONST_MEMBER_FUNC { return !hlsl::all >(hlsl::glsl::equal(L.getDirection(), hlsl::promote(0.0))); } + static this_t createInvalid() + { + this_t retval; + retval.L.makeInvalid(); + return retval; + } + bool isValid() NBL_CONST_MEMBER_FUNC { return L.isValid(); } RayDirInfo L; @@ -451,8 +491,10 @@ struct SLightSample #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (cache, T) -NBL_CONCEPT_BEGIN(1) +#define NBL_CONCEPT_PARAM_1 (eta, typename T::scalar_type) +NBL_CONCEPT_BEGIN(2) #define cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define eta NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) @@ -463,7 +505,9 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getAbsNdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getNdotH2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.isTransmission()), ::nbl::hlsl::is_same_v, bool)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.isValid(eta)), ::nbl::hlsl::is_same_v, bool)) ); +#undef eta #undef cache #include @@ -620,6 +664,12 @@ struct SIsotropicMicrofacetCache // similar with BSDF sampling, as fresnel can be high while reflection can be invalid, or low while refraction would be invalid too bool isTransmission() NBL_CONST_MEMBER_FUNC { return getVdotHLdotH() < scalar_type(0.0); } + bool isValid(const scalar_type eta) NBL_CONST_MEMBER_FUNC + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(scalar_type(1.0), hlsl::promote(eta)); + return ComputeMicrofacetNormal::isValidMicrofacet(isTransmission(), VdotL, absNdotH, orientedEta); + } + scalar_type getVdotL() NBL_CONST_MEMBER_FUNC { return VdotL; } scalar_type getVdotH() NBL_CONST_MEMBER_FUNC { return VdotH; } scalar_type getLdotH() NBL_CONST_MEMBER_FUNC { return LdotH; } @@ -668,6 +718,8 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection > >,SLightSample > >(aniso,_sample)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,V,V,V,eta,V)), ::nbl::hlsl::is_same_v, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create > >,SLightSample > >(aniso,_sample,eta)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createPartial(pNdotL,pNdotL,pNdotL,b0,rcp_eta)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR)(cache.fillTangents(V,V,V))) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) ); #undef rcp_eta @@ -694,8 +746,8 @@ struct SAnisotropicMicrofacetCache static this_t createForReflection(const vector3_type tangentSpaceV, const vector3_type tangentSpaceH) { this_t retval; - retval.iso_cache.VdotH = nbl::hlsl::dot(tangentSpaceV,tangentSpaceH); + retval.iso_cache.VdotL = scalar_type(2.0) * retval.iso_cache.VdotH * retval.iso_cache.VdotH - scalar_type(1.0); retval.iso_cache.LdotH = retval.iso_cache.getVdotH(); assert(tangentSpaceH.z >= scalar_type(0.0)); retval.iso_cache.absNdotH = tangentSpaceH.z; @@ -717,6 +769,7 @@ struct SAnisotropicMicrofacetCache { Refract r = Refract::create(tangentSpaceV, tangentSpaceH); retval.iso_cache.LdotH = r.getNdotT(rcpOrientedEta.value2[0]); + retval.iso_cache.VdotL = retval.iso_cache.VdotH * (retval.iso_cache.LdotH - rcpOrientedEta.value[0] + retval.iso_cache.VdotH * rcpOrientedEta.value[0]); } return retval; @@ -767,6 +820,27 @@ struct SAnisotropicMicrofacetCache retval.BdotH = nbl::hlsl::dot(interaction.getB(),H); return retval; } + static this_t createPartial( + const scalar_type VdotH, const scalar_type LdotH, const scalar_type NdotH, + bool transmitted, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpOrientedEta + ) + { + this_t retval; + retval.iso_cache.VdotH = VdotH; + retval.iso_cache.LdotH = LdotH; + retval.iso_cache.VdotL = hlsl::mix(scalar_type(2.0) * retval.iso_cache.VdotH * retval.iso_cache.VdotH - scalar_type(1.0), + VdotH * (LdotH - rcpOrientedEta.value[0] + VdotH * rcpOrientedEta.value[0]), transmitted); + assert(NdotH > scalar_type(0.0)); + retval.iso_cache.absNdotH = hlsl::abs(NdotH); + retval.iso_cache.NdotH2 = NdotH * NdotH; + return retval; + } + + void fillTangents(const vector3_type T, const vector3_type B, const vector3_type H) + { + TdotH = hlsl::dot(T, H); + BdotH = hlsl::dot(B, H); + } scalar_type getVdotL() NBL_CONST_MEMBER_FUNC { return iso_cache.getVdotL(); } scalar_type getVdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.getVdotH(); } @@ -776,6 +850,11 @@ struct SAnisotropicMicrofacetCache scalar_type getNdotH2() NBL_CONST_MEMBER_FUNC { return iso_cache.getNdotH2(); } bool isTransmission() NBL_CONST_MEMBER_FUNC { return iso_cache.isTransmission(); } + bool isValid(const scalar_type eta) NBL_CONST_MEMBER_FUNC + { + return iso_cache.isValid(eta); + } + scalar_type getTdotH() NBL_CONST_MEMBER_FUNC { return TdotH; } scalar_type getTdotH2() NBL_CONST_MEMBER_FUNC { const scalar_type t = getTdotH(); return t*t; } scalar_type getBdotH() NBL_CONST_MEMBER_FUNC { return BdotH; } @@ -791,6 +870,23 @@ namespace bxdf_concepts { namespace impl { + +#define NBL_CONCEPT_NAME bxdf_common_typdefs +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (bxdf, T) +NBL_CONCEPT_BEGIN(1) +#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) +); +#undef bxdf +#include + #define NBL_CONCEPT_NAME bxdf_common #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -802,11 +898,7 @@ NBL_CONCEPT_BEGIN(3) #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) @@ -940,14 +1032,10 @@ NBL_CONCEPT_BEGIN(4) #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) @@ -977,7 +1065,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(iso, isocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) diff --git a/include/nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl deleted file mode 100644 index e95e7331c7..0000000000 --- a/include/nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl +++ /dev/null @@ -1,119 +0,0 @@ -// 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_COOK_TORRANCE_INCLUDED_ -#define _NBL_BUILTIN_HLSL_BXDF_COOK_TORRANCE_INCLUDED_ - -#include "nbl/builtin/hlsl/bxdf/common.hlsl" -#include "nbl/builtin/hlsl/bxdf/config.hlsl" -#include "nbl/builtin/hlsl/bxdf/ndf.hlsl" -#include "nbl/builtin/hlsl/bxdf/fresnel.hlsl" -#include "nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl" - -namespace nbl -{ -namespace hlsl -{ -namespace bxdf -{ - -// N (NDF), F (fresnel), MT (measure transform, using DualMeasureQuant) -template && ndf::NDF && fresnel::Fresnel) -struct SCookTorrance -{ - 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(isotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(sample_type, Config); - NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); - NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); - - scalar_type __D(NBL_CONST_REF_ARG(isocache_type) cache) - { - return ndf.template D(cache); - } - scalar_type __D(NBL_CONST_REF_ARG(anisocache_type) cache) - { - return ndf.template D(cache); - } - - template - MT __DG1(NBL_CONST_REF_ARG(Query) query) - { - MT measure_transform; - measure_transform.pdf = ndf.template DG1(query); - return measure_transform; - } - template - MT __DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(isocache_type) cache) - { - MT measure_transform; - measure_transform.pdf = ndf.template DG1(query, cache); - measure_transform.transmitted = cache.isTransmission(); - measure_transform.VdotH = cache.getVdotH(); - measure_transform.LdotH = cache.getLdotH(); - measure_transform.VdotHLdotH = cache.getVdotHLdotH(); - return measure_transform; - } - template - MT __DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(anisocache_type) cache) - { - MT measure_transform; - measure_transform.pdf = ndf.template DG1(query, cache); - measure_transform.transmitted = cache.isTransmission(); - measure_transform.VdotH = cache.getVdotH(); - measure_transform.LdotH = cache.getLdotH(); - measure_transform.VdotHLdotH = cache.getVdotHLdotH(); - return measure_transform; - } - - template - MT __DG(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) - { - MT measure_transform; - measure_transform.pdf = ndf.template D(cache); - NBL_IF_CONSTEXPR(MT::Type == ndf::MicrofacetTransformTypes::MTT_REFLECT_REFRACT) - measure_transform.transmitted = cache.isTransmission(); - NBL_IF_CONSTEXPR(MT::Type == ndf::MicrofacetTransformTypes::MTT_REFRACT) - { - measure_transform.VdotH = cache.getVdotH(); - measure_transform.LdotH = cache.getLdotH(); - measure_transform.VdotHLdotH = cache.getVdotHLdotH(); - } - if (any >(ndf.A > hlsl::promote(numeric_limits::min))) - { - measure_transform.pdf *= ndf.template correlated(query, _sample, interaction); - } - return measure_transform; - } - template - MT __DG(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) - { - MT measure_transform; - measure_transform.pdf = ndf.template D(cache); - NBL_IF_CONSTEXPR(MT::Type == ndf::MicrofacetTransformTypes::MTT_REFLECT_REFRACT) - measure_transform.transmitted = cache.isTransmission(); - NBL_IF_CONSTEXPR(MT::Type == ndf::MicrofacetTransformTypes::MTT_REFRACT) - { - measure_transform.VdotH = cache.getVdotH(); - measure_transform.LdotH = cache.getLdotH(); - measure_transform.VdotHLdotH = cache.getVdotHLdotH(); - } - if (any >(ndf.A > hlsl::promote(numeric_limits::min))) - { - measure_transform.pdf *= ndf.template correlated(query, _sample, interaction); - } - return measure_transform; - } - - N ndf; - F fresnel; -}; - -} -} -} - -#endif diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 6cb7a27fe3..bc6c31dcbc 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -161,7 +161,7 @@ struct ComputeMicrofacetNormal static bool isValidMicrofacet(const bool transmitted, const scalar_type VdotL, const scalar_type NdotH, NBL_CONST_REF_ARG(fresnel::OrientedEtas >) orientedEta) { - return !transmitted || (VdotL <= -hlsl::min(orientedEta.value, orientedEta.rcp) && NdotH >= nbl::hlsl::numeric_limits::min); + return !transmitted || (VdotL <= -hlsl::min(orientedEta.value[0], orientedEta.rcp[0]) && NdotH >= nbl::hlsl::numeric_limits::min); } vector_type V; @@ -312,6 +312,24 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel(cosTheta)), ::nbl::hlsl::is_same_v, typename T::vector_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel.getOrientedEtaRcps()), ::nbl::hlsl::is_same_v, OrientedEtaRcps)) +); +#undef cosTheta +#undef fresnel +#include + +#define NBL_CONCEPT_NAME TwoSidedFresnel +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (fresnel, T) +#define NBL_CONCEPT_PARAM_1 (cosTheta, typename T::scalar_type) +NBL_CONCEPT_BEGIN(2) +#define fresnel NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define cosTheta NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Fresnel, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel.getRefractionOrientedEta()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel.getReorientedFresnel(cosTheta)), ::nbl::hlsl::is_same_v, T)) ); #undef cosTheta #undef fresnel @@ -338,6 +356,15 @@ struct Schlick return F0 + (1.0 - F0) * x*x*x*x*x; } + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + { + const T sqrtF0 = hlsl::sqrt(F0); + OrientedEtaRcps rcpEta; + rcpEta.value = (hlsl::promote(1.0) - sqrtF0) / (hlsl::promote(1.0) + sqrtF0); + rcpEta.value2 = rcpEta.value * rcpEta.value; + return rcpEta; + } + T F0; }; @@ -351,8 +378,8 @@ struct Conductor { Conductor retval; retval.eta = eta; - retval.etak = etak; - retval.etak2 = etak*etak; + retval.etak2 = etak * etak; + retval.etaLen2 = eta * eta + retval.etak2; return retval; } @@ -360,8 +387,8 @@ struct Conductor { Conductor retval; retval.eta = eta.real(); - retval.etak = eta.imag(); - retval.etak2 = eta.imag()*eta.imag(); + retval.etak2 = eta.imag() * eta.imag(); + retval.etaLen2 = eta * eta + retval.etak2; return retval; } @@ -370,7 +397,6 @@ struct Conductor const scalar_type cosTheta2 = clampedCosTheta * clampedCosTheta; //const float sinTheta2 = 1.0 - cosTheta2; - const T etaLen2 = eta * eta + etak2; assert(hlsl::all(etaLen2 > hlsl::promote(hlsl::exp2(-numeric_limits::digits)))); const T etaCosTwice = eta * clampedCosTheta * 2.0f; @@ -383,9 +409,17 @@ struct Conductor return (rs2 + rp2) * 0.5f; } + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC + { + OrientedEtaRcps rcpEta; + rcpEta.value = hlsl::promote(1.0) / eta; + rcpEta.value2 = rcpEta.value * rcpEta.value; + return rcpEta; + } + T eta; - T etak; T etak2; + T etaLen2; }; template) @@ -421,6 +455,15 @@ struct Dielectric return __call(orientedEta2, clampedCosTheta); } + scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return orientedEta.value[0]; } // expect T to be monochrome? + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { return orientedEta.getReciprocals(); } + + Dielectric getReorientedFresnel(const scalar_type NdotI) NBL_CONST_MEMBER_FUNC + { + OrientedEtas eta = OrientedEtas::create(NdotI, orientedEta.value); + return Dielectric::create(eta); + } + OrientedEtas orientedEta; T orientedEta2; }; diff --git a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl index 9189d1baec..170b1e9194 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl @@ -6,6 +6,7 @@ #include "nbl/builtin/hlsl/concepts.hlsl" #include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include namespace nbl { @@ -21,51 +22,96 @@ namespace dummy_impl using sample_t = SLightSample >; using interaction_t = surface_interactions::SAnisotropic > >; using cache_t = SAnisotropicMicrofacetCache >; -struct MetaQuery // nonsense struct, just put in all the functions to pass the ndf query concepts -{ - using scalar_type = float; - - scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return 0; } - - scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type getOrientedEta() NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return 0; } - BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return BxDFClampMode::BCM_NONE; } -}; } #define NBL_CONCEPT_NAME NDF #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (ndf, T) -#define NBL_CONCEPT_PARAM_1 (query, dummy_impl::MetaQuery) +#define NBL_CONCEPT_PARAM_1 (quant_query, typename T::quant_query_type) #define NBL_CONCEPT_PARAM_2 (_sample, dummy_impl::sample_t) #define NBL_CONCEPT_PARAM_3 (interaction, dummy_impl::interaction_t) #define NBL_CONCEPT_PARAM_4 (cache, dummy_impl::cache_t) -NBL_CONCEPT_BEGIN(5) +#define NBL_CONCEPT_PARAM_5 (dg1_query, typename T::dg1_query_type) +#define NBL_CONCEPT_PARAM_6 (g2_query, typename T::g2g1_query_type) +NBL_CONCEPT_BEGIN(7) #define ndf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define quant_query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 #define cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define dg1_query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define g2_query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template D(cache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template DG1(query)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template DG1(query, cache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template correlated(query, _sample, interaction)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template G2_over_G1(query, _sample, interaction, cache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quant_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::dg1_query_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::g2g1_query_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quant_query_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template D(quant_query, _sample, interaction, cache)), ::nbl::hlsl::is_same_v, typename T::quant_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template DG1(dg1_query, quant_query, _sample, interaction)), ::nbl::hlsl::is_same_v, typename T::quant_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template correlated(g2_query, _sample, interaction)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template G2_over_G1(g2_query, _sample, interaction, cache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ); +#undef g2_query +#undef dg1_query #undef cache #undef interaction #undef _sample -#undef query +#undef quant_query #undef ndf #include +#define NBL_CONCEPT_NAME NDF_CanOverwriteDG +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (ndf, T) +#define NBL_CONCEPT_PARAM_1 (quant_query, typename T::quant_query_type) +#define NBL_CONCEPT_PARAM_2 (_sample, dummy_impl::sample_t) +#define NBL_CONCEPT_PARAM_3 (interaction, dummy_impl::interaction_t) +#define NBL_CONCEPT_PARAM_4 (cache, dummy_impl::cache_t) +#define NBL_CONCEPT_PARAM_5 (g2_query, typename T::g2g1_query_type) +NBL_CONCEPT_BEGIN(6) +#define ndf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define quant_query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define g2_query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(NDF, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template Dcorrelated(g2_query, quant_query, _sample, interaction, cache)), ::nbl::hlsl::is_same_v, typename T::quant_type)) +); +#undef g2_query +#undef cache +#undef interaction +#undef _sample +#undef quant_query +#undef ndf +#include + + +#define NBL_HLSL_BXDF_ANISOTROPIC_COND_DECLS(IS_ANISO) template\ +NBL_CONSTEXPR_STATIC_INLINE bool RequiredInteraction = IS_ANISO ? surface_interactions::Anisotropic : surface_interactions::Isotropic;\ +template\ +NBL_CONSTEXPR_STATIC_INLINE bool RequiredMicrofacetCache = IS_ANISO ? AnisotropicMicrofacetCache : ReadableIsotropicMicrofacetCache;\ + +#define NBL_HLSL_NDF_CONSTEXPR_DECLS(ANISO,REFLECT_REFRACT) NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = ANISO;\ +NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes SupportedPaths = REFLECT_REFRACT;\ +NBL_CONSTEXPR_STATIC_INLINE bool SupportsTransmission = REFLECT_REFRACT != MTT_REFLECT;\ +NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = SupportsTransmission ? BxDFClampMode::BCM_ABS : BxDFClampMode::BCM_NONE;\ +NBL_HLSL_BXDF_ANISOTROPIC_COND_DECLS(IsAnisotropic);\ + +#define NBL_HLSL_NDF_TYPE_ALIASES(...) using this_t = BOOST_PP_REMOVE_PARENS(BOOST_PP_SEQ_ELEM(0, __VA_ARGS__));\ +using scalar_type = T;\ +using base_type = BOOST_PP_REMOVE_PARENS(BOOST_PP_SEQ_ELEM(1, __VA_ARGS__));\ +using quant_type = SDualMeasureQuant;\ +using vector2_type = vector;\ +using vector3_type = vector;\ +using dg1_query_type = BOOST_PP_REMOVE_PARENS(BOOST_PP_SEQ_ELEM(2, __VA_ARGS__));\ +using g2g1_query_type = BOOST_PP_REMOVE_PARENS(BOOST_PP_SEQ_ELEM(3, __VA_ARGS__));\ +using quant_query_type = BOOST_PP_REMOVE_PARENS(BOOST_PP_SEQ_ELEM(4, __VA_ARGS__));\ + } } } diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl index 8076102d94..daf2e387eb 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl @@ -6,6 +6,7 @@ #include "nbl/builtin/hlsl/limits.hlsl" #include "nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf.hlsl" namespace nbl { @@ -47,119 +48,213 @@ NBL_CONCEPT_END( #include } -template -struct Beckmann; +namespace impl +{ template -NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) -struct Beckmann) > +struct SBeckmannDG1Query { using scalar_type = T; - template) - scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) - { - scalar_type nom = exp2((cache.getNdotH2() - scalar_type(1.0)) / (log(2.0) * a2 * cache.getNdotH2())); - scalar_type denom = a2 * cache.getNdotH2() * cache.getNdotH2(); - return numbers::inv_pi * nom / denom; - } + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - // brdf - template) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query) - { - return query.getNdf() / (scalar_type(1.0) + query.getLambdaV()); - } + scalar_type ndf; + scalar_type lambda_V; +}; - // bsdf - template && ReadableIsotropicMicrofacetCache) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache) - { - return DG1(query); - } +template +struct SBeckmannG2overG1Query +{ + using scalar_type = T; - scalar_type G1(scalar_type lambda) - { - return scalar_type(1.0) / (scalar_type(1.0) + lambda); - } + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - scalar_type C2(scalar_type NdotX2) - { - return NdotX2 / (a2 * (scalar_type(1.0) - NdotX2)); - } + scalar_type lambda_L; + scalar_type lambda_V; +}; - scalar_type Lambda(scalar_type c2) +template +struct BeckmannBase +{ + //conversion between alpha and Phong exponent, Walter et.al. + static T PhongExponentToAlpha2(T _n) { - scalar_type c = sqrt(c2); - scalar_type nom = scalar_type(1.0) - scalar_type(1.259) * c + scalar_type(0.396) * c2; - scalar_type denom = scalar_type(2.181) * c2 + scalar_type(3.535) * c; - return hlsl::mix(scalar_type(0.0), nom / denom, c < scalar_type(1.6)); + return T(2.0) / (_n + T(2.0)); } - - scalar_type LambdaC2(scalar_type NdotX2) + //+INF for a2==0.0 + static T Alpha2ToPhongExponent(T a2) { - return Lambda(C2(NdotX2)); + return T(2.0) / a2 - T(2.0); } +}; + +template +struct BeckmannCommon; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) +struct BeckmannCommon) > : BeckmannBase +{ + using scalar_type = T; - template && LightSample && surface_interactions::Isotropic) - scalar_type correlated(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + template) + scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) { - return scalar_type(1.0) / (scalar_type(1.0) + query.getLambdaV() + query.getLambdaL()); + if (a2 < numeric_limits::min) + return bit_cast(numeric_limits::infinity); + scalar_type NdotH2 = cache.getNdotH2(); + scalar_type nom = exp2((NdotH2 - scalar_type(1.0)) / (log(2.0) * a2 * NdotH2)); + scalar_type denom = a2 * NdotH2 * NdotH2; + return numbers::inv_pi * nom / denom; } - template && LightSample && surface_interactions::Isotropic && ReadableIsotropicMicrofacetCache) - scalar_type G2_over_G1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + scalar_type C2(scalar_type NdotX2) { - scalar_type onePlusLambda_V = scalar_type(1.0) + query.getLambdaV(); - return onePlusLambda_V * hlsl::mix(scalar_type(1.0)/(onePlusLambda_V + query.getLambdaL()), bxdf::beta(onePlusLambda_V, scalar_type(1.0) + query.getLambdaL()), cache.isTransmission()); + assert(NdotX2 >= scalar_type(0.0)); + return NdotX2 / (a2 * (scalar_type(1.0) - NdotX2)); } - vector A; scalar_type a2; }; - template NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) -struct Beckmann) > +struct BeckmannCommon) > : BeckmannBase { using scalar_type = T; template) scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) { - scalar_type nom = exp(-(cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2) / cache.getNdotH2()); - scalar_type denom = A.x * A.y * cache.getNdotH2() * cache.getNdotH2(); + if (a2 < numeric_limits::min) + return bit_cast(numeric_limits::infinity); + scalar_type NdotH2 = cache.getNdotH2(); + scalar_type nom = exp(-(cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2) / NdotH2); + scalar_type denom = a2 * NdotH2 * NdotH2; return numbers::inv_pi * nom / denom; } - template) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query) + scalar_type C2(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) { - Beckmann beckmann; - scalar_type dg = beckmann.template DG1(query); - return dg; + return NdotX2 / (TdotX2 * ax2 + BdotX2 * ay2); } - template && AnisotropicMicrofacetCache) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache) + scalar_type ax2; + scalar_type ay2; + scalar_type a2; +}; + +template +struct BeckmannGenerateH +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + + vector3_type __call(const vector3_type localV, const vector2_type u) { - Beckmann beckmann; - scalar_type dg = beckmann.template DG1(query, cache.iso_cache); - return dg; + //stretch + vector3_type V = nbl::hlsl::normalize(vector3_type(ax * localV.x, ay * localV.y, localV.z)); + + vector2_type slope; + if (V.z > 0.9999)//V.z=NdotV=cosTheta in tangent space + { + scalar_type r = sqrt(-log(1.0 - u.x)); + scalar_type sinPhi = sin(2.0 * numbers::pi * u.y); + scalar_type cosPhi = cos(2.0 * numbers::pi * u.y); + slope = (vector2_type)r * vector2_type(cosPhi,sinPhi); + } + else + { + scalar_type cosTheta = V.z; + scalar_type sinTheta = sqrt(1.0 - cosTheta * cosTheta); + scalar_type tanTheta = sinTheta / cosTheta; + scalar_type cotTheta = 1.0 / tanTheta; + + scalar_type a = -1.0; + scalar_type c = erf(cosTheta); + scalar_type sample_x = max(u.x, 1.0e-6); + scalar_type theta = acos(cosTheta); + scalar_type fit = 1.0 + theta * (-0.876 + theta * (0.4265 - 0.0594*theta)); + scalar_type b = c - (1.0 + c) * pow(1.0-sample_x, fit); + + scalar_type normalization = 1.0 / (1.0 + c + numbers::inv_sqrtpi * tanTheta * exp(-cosTheta*cosTheta)); + + const int ITER_THRESHOLD = 10; + const float MAX_ACCEPTABLE_ERR = 1.0e-5; + int it = 0; + float value=1000.0; + while (++it < ITER_THRESHOLD && nbl::hlsl::abs(value) > MAX_ACCEPTABLE_ERR) + { + if (!(b >= a && b <= c)) + b = 0.5 * (a + c); + + float invErf = erfInv(b); + value = normalization * (1.0 + b + numbers::inv_sqrtpi * tanTheta * exp(-invErf * invErf)) - sample_x; + float derivative = normalization * (1.0 - invErf * cosTheta); + + if (value > 0.0) + c = b; + else + a = b; + + b -= value/derivative; + } + // TODO: investigate if we can replace these two erf^-1 calls with a box muller transform + slope.x = erfInv(b); + slope.y = erfInv(2.0 * max(u.y, 1.0e-6) - 1.0); + } + + scalar_type sinTheta = sqrt(1.0 - V.z*V.z); + scalar_type cosPhi = sinTheta==0.0 ? 1.0 : clamp(V.x/sinTheta, -1.0, 1.0); + scalar_type sinPhi = sinTheta==0.0 ? 0.0 : clamp(V.y/sinTheta, -1.0, 1.0); + //rotate + scalar_type tmp = cosPhi*slope.x - sinPhi*slope.y; + slope.y = sinPhi*slope.x + cosPhi*slope.y; + slope.x = tmp; + + //unstretch + slope = vector2_type(ax, ay) * slope; + + return nbl::hlsl::normalize(vector3_type(-slope, 1.0)); } - scalar_type G1(scalar_type lambda) + scalar_type ax; + scalar_type ay; +}; +} + + +template) +struct Beckmann +{ + NBL_HLSL_NDF_CONSTEXPR_DECLS(_IsAnisotropic,reflect_refract); + NBL_HLSL_NDF_TYPE_ALIASES(((Beckmann))((impl::BeckmannCommon))((impl::SBeckmannDG1Query))((impl::SBeckmannG2overG1Query))((DualMeasureQuantQuery))); + + template > + static enable_if_t create(scalar_type A) { - return scalar_type(1.0) / (scalar_type(1.0) + lambda); + this_t retval; + retval.__ndf_base.a2 = A*A; + retval.__generate_base.ax = A; + retval.__generate_base.ay = A; + return retval; } - - scalar_type C2(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) + template > + static enable_if_t create(scalar_type ax, scalar_type ay) { - return NdotX2 / (TdotX2 * ax2 + BdotX2 * ay2); + this_t retval; + retval.__ndf_base.ax2 = ax*ax; + retval.__ndf_base.ay2 = ay*ay; + retval.__ndf_base.a2 = ax*ay; + retval.__generate_base.ax = ax; + retval.__generate_base.ay = ay; + return retval; } - scalar_type Lambda(scalar_type c2) + static scalar_type Lambda(scalar_type c2) { scalar_type c = sqrt(c2); scalar_type nom = scalar_type(1.0) - scalar_type(1.259) * c + scalar_type(0.396) * c2; @@ -167,27 +262,86 @@ struct Beckmann) > return hlsl::mix(scalar_type(0.0), nom / denom, c < scalar_type(1.6)); } - scalar_type LambdaC2(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) + template) + quant_query_type createQuantQuery(NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta) + { + quant_query_type quant_query; // only has members for refraction + NBL_IF_CONSTEXPR(SupportsTransmission) + { + quant_query.VdotHLdotH = cache.getVdotHLdotH(); + const scalar_type VdotH_etaLdotH = cache.getVdotH() + orientedEta * cache.getLdotH(); + quant_query.neg_rcp2_VdotH_etaLdotH = scalar_type(-1.0) / (VdotH_etaLdotH * VdotH_etaLdotH); + } + return quant_query; + } + template NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) + enable_if_t createDG1Query(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + dg1_query_type dg1_query; + dg1_query.ndf = __ndf_base.template D(cache); + dg1_query.lambda_V = Lambda(__ndf_base.C2(interaction.getNdotV2())); + return dg1_query; + } + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction) + enable_if_t createG2G1Query(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + { + g2g1_query_type g2_query; + g2_query.lambda_L = Lambda(__ndf_base.C2(_sample.getNdotL2())); + g2_query.lambda_V = Lambda(__ndf_base.C2(interaction.getNdotV2())); + return g2_query; + } + template NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) + enable_if_t createDG1Query(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + dg1_query_type dg1_query; + dg1_query.ndf = __ndf_base.template D(cache); + dg1_query.lambda_V = Lambda(__ndf_base.C2(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2())); + return dg1_query; + } + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction) + enable_if_t createG2G1Query(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) { - return Lambda(C2(TdotX2, BdotX2, NdotX2)); + g2g1_query_type g2_query; + g2_query.lambda_L = Lambda(__ndf_base.C2(_sample.getTdotL2(), _sample.getBdotL2(), _sample.getNdotL2())); + g2_query.lambda_V = Lambda(__ndf_base.C2(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2())); + return g2_query; } - template && LightSample && surface_interactions::Anisotropic) - scalar_type correlated(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + vector3_type generateH(const vector3_type localV, const vector2_type u) + { + return __generate_base.__call(localV, u); + } + + template && RequiredInteraction && RequiredMicrofacetCache) + quant_type D(NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + scalar_type d = __ndf_base.template D(cache); + return createDualMeasureQuantity(d, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query); + } + + template && RequiredInteraction) + quant_type DG1(NBL_CONST_REF_ARG(dg1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + { + scalar_type dg1 = query.getNdf() / (scalar_type(1.0) + query.getLambdaV()); + return createDualMeasureQuantity(dg1, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query); + } + + template && RequiredInteraction) + scalar_type correlated(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) { return scalar_type(1.0) / (scalar_type(1.0) + query.getLambdaV() + query.getLambdaL()); } - template && LightSample && surface_interactions::Anisotropic && AnisotropicMicrofacetCache) - scalar_type G2_over_G1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + template && RequiredInteraction && RequiredMicrofacetCache) + scalar_type G2_over_G1(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { scalar_type onePlusLambda_V = scalar_type(1.0) + query.getLambdaV(); - return onePlusLambda_V * hlsl::mix(scalar_type(1.0)/(onePlusLambda_V + query.getLambdaL()), bxdf::beta(onePlusLambda_V, scalar_type(1.0) + query.getLambdaL()), cache.isTransmission()); + scalar_type lambda_L = query.getLambdaL(); + return onePlusLambda_V * hlsl::mix(scalar_type(1.0)/(onePlusLambda_V + lambda_L), bxdf::beta(onePlusLambda_V, scalar_type(1.0) + lambda_L), cache.isTransmission()); } - vector A; - scalar_type ax2; - scalar_type ay2; + base_type __ndf_base; + impl::BeckmannGenerateH __generate_base; }; } diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index eb49bd38f0..c3d99743ff 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -6,6 +6,7 @@ #include "nbl/builtin/hlsl/limits.hlsl" #include "nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf.hlsl" namespace nbl { @@ -18,7 +19,7 @@ namespace ndf namespace ggx_concepts { -#define NBL_CONCEPT_NAME DG1BrdfQuery +#define NBL_CONCEPT_NAME DG1Query #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (query, T) @@ -26,28 +27,13 @@ NBL_CONCEPT_BEGIN(1) #define query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getNdf()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getNdfwoNumerator()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getG1over2NdotV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ); #undef query #include -#define NBL_CONCEPT_NAME DG1BsdfQuery -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -#define NBL_CONCEPT_TPLT_PRM_NAMES (T) -#define NBL_CONCEPT_PARAM_0 (query, T) -NBL_CONCEPT_BEGIN(1) -#define query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getNdf()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getG1over2NdotV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getOrientedEta()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) -); -#undef query -#include - -#define NBL_CONCEPT_NAME G2XQuery +#define NBL_CONCEPT_NAME G2overG1Query #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (query, T) @@ -57,183 +43,290 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getDevshV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getDevshL()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getClampMode()), ::nbl::hlsl::is_same_v, BxDFClampMode)) ); #undef query #include } -template -struct GGX; +namespace impl +{ template +struct SGGXDG1Query +{ + using scalar_type = T; + + scalar_type getNdfwoNumerator() 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; +}; + +template +struct SGGXG2XQuery +{ + using scalar_type = T; + + 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; +}; + +template +struct GGXCommon; + +template NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) -struct GGX) > +struct GGXCommon) > { using scalar_type = T; + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = SupportsTransmission ? BxDFClampMode::BCM_ABS : BxDFClampMode::BCM_MAX; + // trowbridge-reitz template) scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) { + if (a2 < numeric_limits::min) + return bit_cast(numeric_limits::infinity); scalar_type denom = scalar_type(1.0) - one_minus_a2 * cache.getNdotH2(); return a2 * numbers::inv_pi / (denom * denom); } - template) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query) - { - return scalar_type(0.5) * query.getNdf() * query.getG1over2NdotV(); - } - - template && ReadableIsotropicMicrofacetCache) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache) - { - scalar_type NG = query.getNdf() * query.getG1over2NdotV(); - scalar_type factor = scalar_type(0.5); - if (cache.isTransmission()) - { - const scalar_type VdotH_etaLdotH = (cache.getVdotH() + query.getOrientedEta() * cache.getLdotH()); - // VdotHLdotH is negative under transmission, so this factor is negative - factor *= -scalar_type(2.0) * cache.getVdotHLdotH() / (VdotH_etaLdotH * VdotH_etaLdotH); - } - return NG * factor; - } - scalar_type devsh_part(scalar_type NdotX2) { assert(a2 >= numeric_limits::min); return sqrt(a2 + one_minus_a2 * NdotX2); } - scalar_type G1_wo_numerator(scalar_type absNdotX, scalar_type NdotX2) - { - return scalar_type(1.0) / (absNdotX + devsh_part(NdotX2)); - } + scalar_type a2; + scalar_type one_minus_a2; +}; - scalar_type G1_wo_numerator_devsh_part(scalar_type absNdotX, scalar_type devsh_part) - { - // numerator is 2 * NdotX - return scalar_type(1.0) / (absNdotX + devsh_part); - } +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) +struct GGXCommon) > +{ + using scalar_type = T; - // without numerator, numerator is 2 * NdotV * NdotL, we factor out 4 * NdotV * NdotL, hence 0.5 - template && LightSample && surface_interactions::Isotropic) - scalar_type correlated(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) - { - BxDFClampMode _clamp = query.getClampMode(); - assert(_clamp != BxDFClampMode::BCM_NONE); + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = SupportsTransmission ? BxDFClampMode::BCM_ABS : BxDFClampMode::BCM_MAX; - scalar_type Vterm = _sample.getNdotL(_clamp) * query.getDevshV(); - scalar_type Lterm = interaction.getNdotV(_clamp) * query.getDevshL(); - return scalar_type(0.5) / (Vterm + Lterm); - } - - template && LightSample && surface_interactions::Isotropic && ReadableIsotropicMicrofacetCache) - scalar_type G2_over_G1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + template) + scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) { - BxDFClampMode _clamp = query.getClampMode(); - assert(_clamp != BxDFClampMode::BCM_NONE); + if (a2 < numeric_limits::min) + return bit_cast(numeric_limits::infinity); + scalar_type denom = cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2 + cache.getNdotH2(); + return numbers::inv_pi / (a2 * denom * denom); + } - scalar_type G2_over_G1; - scalar_type NdotV = interaction.getNdotV(_clamp); - scalar_type NdotL = _sample.getNdotL(_clamp); - scalar_type devsh_v = query.getDevshV(); - scalar_type devsh_l = query.getDevshL(); - if (cache.isTransmission()) - { - if (NdotV < 1e-7 || NdotL < 1e-7) - return 0.0; - scalar_type onePlusLambda_V = scalar_type(0.5) * (devsh_v / NdotV + scalar_type(1.0)); - scalar_type onePlusLambda_L = scalar_type(0.5) * (devsh_l / NdotL + scalar_type(1.0)); - G2_over_G1 = bxdf::beta_wo_check(onePlusLambda_L, onePlusLambda_V) * onePlusLambda_V; - } - else - { - G2_over_G1 = NdotL * (devsh_v + NdotV); // alternative `Vterm+NdotL*NdotV /// NdotL*NdotV could come as a parameter - G2_over_G1 /= NdotV * devsh_l + NdotL * devsh_v; - } + // TODO: potential idea for making GGX spin using covariance matrix of sorts: https://www.desmos.com/3d/weq2ginq9o - return G2_over_G1; + scalar_type devsh_part(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) + { + assert(ax2 >= numeric_limits::min && ay2 >= numeric_limits::min); + return sqrt(TdotX2 * ax2 + BdotX2 * ay2 + NdotX2); } - vector A; + scalar_type ax2; + scalar_type ay2; scalar_type a2; - scalar_type one_minus_a2; }; template -NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) -struct GGX) > +struct GGXGenerateH { using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; - template) - scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) + vector3_type __call(const vector3_type localV, const vector2_type u) { - scalar_type denom = cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2 + cache.getNdotH2(); - return numbers::inv_pi / (a2 * denom * denom); + vector3_type V = hlsl::normalize(vector3_type(ax * localV.x, ay * localV.y, localV.z));//stretch view vector so that we're sampling as if roughness=1.0 + + scalar_type lensq = V.x*V.x + V.y*V.y; + vector3_type T1 = lensq > 0.0 ? vector3_type(-V.y, V.x, 0.0) * hlsl::rsqrt(lensq) : vector3_type(1.0,0.0,0.0); + vector3_type T2 = cross(V,T1); + + scalar_type r = sqrt(u.x); + scalar_type phi = 2.0 * numbers::pi * u.y; + scalar_type t1 = r * cos(phi); + scalar_type t2 = r * sin(phi); + scalar_type s = 0.5 * (1.0 + V.z); + t2 = (1.0 - s)*sqrt(1.0 - t1*t1) + s*t2; + + //reprojection onto hemisphere + //found cases where t1*t1+t2*t2>1.0 due to fp32 precision issues, hence the max + vector3_type H = t1*T1 + t2*T2 + hlsl::sqrt(hlsl::max(scalar_type(0.0), scalar_type(1.0)-t1*t1-t2*t2))*V; + //unstretch + return hlsl::normalize(vector3_type(ax*H.x, ay*H.y, H.z)); } - // burley - scalar_type D(scalar_type a2, scalar_type TdotH, scalar_type BdotH, scalar_type NdotH, scalar_type anisotropy) + scalar_type ax; + scalar_type ay; +}; +} + + +template) +struct GGX +{ + NBL_HLSL_NDF_CONSTEXPR_DECLS(_IsAnisotropic,reflect_refract); + NBL_HLSL_NDF_TYPE_ALIASES(((GGX))((impl::GGXCommon))((impl::SGGXDG1Query))((impl::SGGXG2XQuery))((DualMeasureQuantQuery))); + + template > + static enable_if_t create(scalar_type A) { - scalar_type antiAniso = scalar_type(1.0) - anisotropy; - scalar_type atab = a2 * antiAniso; - scalar_type anisoTdotH = antiAniso * TdotH; - scalar_type anisoNdotH = antiAniso * NdotH; - scalar_type w2 = antiAniso/(BdotH * BdotH + anisoTdotH * anisoTdotH + anisoNdotH * anisoNdotH * a2); - return w2 * w2 * atab * numbers::inv_pi; + this_t retval; + retval.__ndf_base.a2 = A*A; + retval.__ndf_base.one_minus_a2 = scalar_type(1.0) - A*A; + retval.__generate_base.ax = A; + retval.__generate_base.ay = A; + return retval; } - - template) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query) + template > + static enable_if_t create(scalar_type ax, scalar_type ay) { - GGX ggx; - return ggx.template DG1(query); + this_t retval; + retval.__ndf_base.ax2 = ax*ax; + retval.__ndf_base.ay2 = ay*ay; + retval.__ndf_base.a2 = ax*ay; + retval.__generate_base.ax = ax; + retval.__generate_base.ay = ay; + return retval; } - template && AnisotropicMicrofacetCache) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache) + static scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type devsh_part) { - GGX ggx; - return ggx.template DG1(query, cache.iso_cache); + return scalar_type(1.0) / (NdotX + devsh_part); } - scalar_type devsh_part(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) + template) + quant_query_type createQuantQuery(NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta) { - assert(ax2 >= numeric_limits::min && ay2 >= numeric_limits::min); - return sqrt(TdotX2 * ax2 + BdotX2 * ay2 + NdotX2); + quant_query_type quant_query; // only has members for refraction + NBL_IF_CONSTEXPR (SupportsTransmission) + { + quant_query.VdotHLdotH = cache.getVdotHLdotH(); + const scalar_type VdotH_etaLdotH = cache.getVdotH() + orientedEta * cache.getLdotH(); + quant_query.neg_rcp2_VdotH_etaLdotH = scalar_type(-1.0) / (VdotH_etaLdotH * VdotH_etaLdotH); + } + return quant_query; + } + template NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) + enable_if_t createDG1Query(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + dg1_query_type dg1_query; + dg1_query.ndf = __ndf_base.template D(cache); + scalar_type clampedNdotV = interaction.getNdotV(_clamp); + dg1_query.G1_over_2NdotV = G1_wo_numerator(clampedNdotV, __ndf_base.devsh_part(interaction.getNdotV2())); + return dg1_query; + } + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction) + enable_if_t createG2G1Query(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + { + g2g1_query_type g2_query; + g2_query.devsh_l = __ndf_base.devsh_part(_sample.getNdotL2()); + g2_query.devsh_v = __ndf_base.devsh_part(interaction.getNdotV2()); + return g2_query; + } + template NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) + enable_if_t createDG1Query(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + dg1_query_type dg1_query; + dg1_query.ndf = __ndf_base.template D(cache); + scalar_type clampedNdotV = interaction.getNdotV(_clamp); + dg1_query.G1_over_2NdotV = G1_wo_numerator(clampedNdotV, __ndf_base.devsh_part(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2())); + return dg1_query; + } + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction) + enable_if_t createG2G1Query(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + { + g2g1_query_type g2_query; + g2_query.devsh_l = __ndf_base.devsh_part(_sample.getTdotL2(), _sample.getBdotL2(), _sample.getNdotL2()); + g2_query.devsh_v = __ndf_base.devsh_part(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2()); + return g2_query; } - scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) + vector3_type generateH(const vector3_type localV, const vector2_type u) { - return scalar_type(1.0) / (NdotX + devsh_part(TdotX2, BdotX2, NdotX2)); + return __generate_base.__call(localV, u); } - scalar_type G1_wo_numerator_devsh_part(scalar_type NdotX, scalar_type devsh_part) + template && RequiredInteraction && RequiredMicrofacetCache) + quant_type D(NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - return scalar_type(1.0) / (NdotX + devsh_part); + scalar_type d = __ndf_base.template D(cache); + return createDualMeasureQuantity(d, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query); } - // without numerator - template && LightSample && surface_interactions::Anisotropic) - scalar_type correlated(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + template && RequiredInteraction) + quant_type DG1(NBL_CONST_REF_ARG(dg1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) { - BxDFClampMode _clamp = query.getClampMode(); - assert(_clamp != BxDFClampMode::BCM_NONE); + scalar_type dg1_over_2NdotV = query.getNdfwoNumerator() * query.getG1over2NdotV(); + quant_type dmq; + dmq.microfacetMeasure = scalar_type(2.0) * interaction.getNdotV(_clamp) * dg1_over_2NdotV; + + NBL_IF_CONSTEXPR(SupportsTransmission) + { + const scalar_type VdotHLdotH = quant_query.getVdotHLdotH(); + const bool transmitted = reflect_refract==MTT_REFRACT || (reflect_refract!=MTT_REFLECT && VdotHLdotH < scalar_type(0.0)); + dmq.projectedLightMeasure = hlsl::mix(scalar_type(0.5),scalar_type(2.0),transmitted) * dg1_over_2NdotV; + if (transmitted) + dmq.projectedLightMeasure *= VdotHLdotH * quant_query.getNeg_rcp2_VdotH_etaLdotH(); + } + else + dmq.projectedLightMeasure = scalar_type(0.5) * dg1_over_2NdotV; + return dmq; + } + template && RequiredInteraction) + scalar_type correlated_wo_numerator(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + { + // without numerator, numerator is 2 * NdotV * NdotL, we factor out 4 * NdotV * NdotL, hence 0.5 scalar_type Vterm = _sample.getNdotL(_clamp) * query.getDevshV(); scalar_type Lterm = interaction.getNdotV(_clamp) * query.getDevshL(); return scalar_type(0.5) / (Vterm + Lterm); } - template && LightSample && surface_interactions::Anisotropic && AnisotropicMicrofacetCache) - scalar_type G2_over_G1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + template && RequiredInteraction) + scalar_type correlated(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + { + return scalar_type(4.0) * interaction.getNdotV(_clamp) * _sample.getNdotL(_clamp) * correlated_wo_numerator(query, _sample, interaction); + } + + template && RequiredInteraction && RequiredMicrofacetCache) + quant_type Dcorrelated(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) { - BxDFClampMode _clamp = query.getClampMode(); - assert(_clamp != BxDFClampMode::BCM_NONE); + scalar_type dg = __ndf_base.template D(cache); + if (dg < bit_cast(numeric_limits::infinity)) + dg *= correlated_wo_numerator(query, _sample, interaction); + else + dg = scalar_type(0.0); + + quant_type dmq; + dmq.microfacetMeasure = dg; + + NBL_IF_CONSTEXPR(SupportsTransmission) + { + const scalar_type VdotHLdotH = quant_query.getVdotHLdotH(); + const bool transmitted = reflect_refract==MTT_REFRACT || (reflect_refract!=MTT_REFLECT && VdotHLdotH < scalar_type(0.0)); + scalar_type NdotL_over_denominator = _sample.getNdotL(BxDFClampMode::BCM_ABS); + if (transmitted) + NdotL_over_denominator *= scalar_type(4.0) * VdotHLdotH * quant_query.getNeg_rcp2_VdotH_etaLdotH(); + dmq.projectedLightMeasure = dg * NdotL_over_denominator; + } + else + dmq.projectedLightMeasure = dg * _sample.getNdotL(BxDFClampMode::BCM_ABS); + return dmq; + } + template && RequiredInteraction && RequiredMicrofacetCache) + scalar_type G2_over_G1(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { scalar_type G2_over_G1; scalar_type NdotV = interaction.getNdotV(_clamp); scalar_type NdotL = _sample.getNdotL(_clamp); @@ -256,19 +349,20 @@ struct GGX) > return G2_over_G1; } - vector A; - scalar_type ax2; - scalar_type ay2; - scalar_type a2; + base_type __ndf_base; + impl::GGXGenerateH __generate_base; }; - namespace impl { template struct is_ggx : bool_constant< - is_same >::value || - is_same >::value + is_same >::value || + is_same >::value || + is_same >::value || + is_same >::value || + is_same >::value || + is_same >::value > {}; } diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl index d4118aaec4..73149ab553 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl @@ -18,186 +18,90 @@ namespace ndf enum MicrofacetTransformTypes : uint16_t { - MTT_REFLECT = 0b01, - MTT_REFRACT = 0b10, - MTT_REFLECT_REFRACT = 0b11 + MTT_REFLECT = 0b01, + MTT_REFRACT = 0b10, + MTT_REFLECT_REFRACT = 0b11 }; -template -struct SDualMeasureQuant; - -template -struct SDualMeasureQuant +namespace microfacet_transform_concepts { - using this_t = SDualMeasureQuant; - using scalar_type = T; - - NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFLECT; - - scalar_type getMicrofacetMeasure() - { - return pdf; - } - - // this computes the max(NdotL,0)/(4*max(NdotV,0)*max(NdotL,0)) factor which transforms PDFs in the f in projected microfacet f * NdotH measure to projected light measure f * NdotL - scalar_type getProjectedLightMeasure() - { - return scalar_type(0.25) * pdf / maxNdotV; - } - - scalar_type pdf; - scalar_type maxNdotV; -}; +#define NBL_CONCEPT_NAME QuantQuery +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (query, T) +NBL_CONCEPT_BEGIN(1) +#define query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getVdotHLdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getNeg_rcp2_VdotH_etaLdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) +); +#undef query +#include +} template -struct SDualMeasureQuant +struct DualMeasureQuantQuery { - using this_t = SDualMeasureQuant; - using scalar_type = T; - - NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFLECT; - - scalar_type getMicrofacetMeasure() - { - return pdf; - } + using scalar_type = T; - // this computes the max(NdotL,0)/(4*max(NdotV,0)*max(NdotL,0)) factor which transforms PDFs in the f in projected microfacet f * NdotH measure to projected light measure f * NdotL - scalar_type getProjectedLightMeasure() - { - return pdf * maxNdotL; - } + // note in pbrt it's `abs(VdotH)*abs(LdotH)` + // we leverage the fact that under transmission the sign must always be negative and rest of the code already accounts for that + scalar_type getVdotHLdotH() NBL_CONST_MEMBER_FUNC { return VdotHLdotH; } + scalar_type getNeg_rcp2_VdotH_etaLdotH () NBL_CONST_MEMBER_FUNC { return neg_rcp2_VdotH_etaLdotH ; } - scalar_type pdf; - scalar_type maxNdotL; + scalar_type VdotHLdotH; + scalar_type neg_rcp2_VdotH_etaLdotH; }; -template -struct SDualMeasureQuant -{ - using this_t = SDualMeasureQuant; - using scalar_type = T; - - NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFRACT; - - scalar_type getMicrofacetMeasure() - { - return pdf; - } - - scalar_type getProjectedLightMeasure() - { - const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); - // VdotHLdotH is negative under transmission, so thats denominator is negative - scalar_type denominator = absNdotV * (-VdotH_etaLdotH * VdotH_etaLdotH); - return pdf * VdotHLdotH / denominator; - } - - scalar_type pdf; - scalar_type absNdotV; - scalar_type VdotH; - scalar_type LdotH; - scalar_type VdotHLdotH; - scalar_type orientedEta; -}; template -struct SDualMeasureQuant +struct SDualMeasureQuant { - using this_t = SDualMeasureQuant; - using scalar_type = T; - - NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFRACT; - - scalar_type getMicrofacetMeasure() - { - return pdf; - } - - scalar_type getProjectedLightMeasure() - { - const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); - // VdotHLdotH is negative under transmission, so thats denominator is negative - scalar_type denominator = absNdotL * (-scalar_type(4.0) * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH)); - return pdf * denominator; - } - - scalar_type pdf; - scalar_type absNdotL; - scalar_type VdotH; - scalar_type LdotH; - scalar_type VdotHLdotH; - scalar_type orientedEta; + using value_type = T; + + T microfacetMeasure; + T projectedLightMeasure; }; -template -struct SDualMeasureQuant +namespace impl { - using this_t = SDualMeasureQuant; - using scalar_type = T; - - NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFLECT_REFRACT; - - scalar_type getMicrofacetMeasure() - { - return pdf; - } - - scalar_type getProjectedLightMeasure() - { - scalar_type denominator = absNdotV; - if (transmitted) - { - const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); - // VdotHLdotH is negative under transmission, so thats denominator is negative - denominator *= -VdotH_etaLdotH * VdotH_etaLdotH; - } - return pdf * (transmitted ? VdotHLdotH : scalar_type(0.25)) / denominator; - } - - scalar_type pdf; - scalar_type absNdotV; - bool transmitted; - scalar_type VdotH; - scalar_type LdotH; - scalar_type VdotHLdotH; - scalar_type orientedEta; +template +struct createDualMeasureQuantity_helper +{ + using scalar_type = typename vector_traits::scalar_type; + + static SDualMeasureQuant __call(const T microfacetMeasure, scalar_type clampedNdotV, scalar_type clampedNdotL, scalar_type VdotHLdotH, scalar_type neg_rcp2_VdotH_etaLdotH) + { + assert(clampedNdotV >= scalar_type(0.0) && clampedNdotL >= scalar_type(0.0)); + SDualMeasureQuant retval; + retval.microfacetMeasure = microfacetMeasure; + // do constexpr booleans first so optimizer picks up this and short circuits + const bool transmitted = reflect_refract==MTT_REFRACT || (reflect_refract!=MTT_REFLECT && VdotHLdotH < scalar_type(0.0)); + retval.projectedLightMeasure = microfacetMeasure * hlsl::mix(scalar_type(0.25),VdotHLdotH*neg_rcp2_VdotH_etaLdotH,transmitted)/clampedNdotV; + // VdotHLdotH is negative under transmission, so thats denominator is negative + return retval; + } }; +} +// specialMeasure meaning the measure defined by the specialization of createDualMeasureQuantity_helper; note that GGX redefines it somewhat template -struct SDualMeasureQuant +SDualMeasureQuant createDualMeasureQuantity(const T specialMeasure, typename vector_traits::scalar_type clampedNdotV, typename vector_traits::scalar_type clampedNdotL) { - using this_t = SDualMeasureQuant; - using scalar_type = T; - - NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFLECT_REFRACT; - - scalar_type getMicrofacetMeasure() - { - return pdf; - } - - scalar_type getProjectedLightMeasure() - { - scalar_type denominator = absNdotL; - if (transmitted) - { - const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); - // VdotHLdotH is negative under transmission, so thats denominator is negative - denominator *= -scalar_type(4.0) * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH); - } - return pdf * denominator; - } - - scalar_type pdf; - scalar_type absNdotL; - bool transmitted; - scalar_type VdotH; - scalar_type LdotH; - scalar_type VdotHLdotH; - scalar_type orientedEta; -}; - + typename vector_traits::scalar_type dummy; + return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,dummy,dummy); +} +template +SDualMeasureQuant createDualMeasureQuantity(const T specialMeasure, typename vector_traits::scalar_type clampedNdotV, typename vector_traits::scalar_type clampedNdotL, typename vector_traits::scalar_type VdotHLdotH, typename vector_traits::scalar_type neg_rcp2_VdotH_etaLdotH) +{ + return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,VdotHLdotH,neg_rcp2_VdotH_etaLdotH); +} +template +SDualMeasureQuant createDualMeasureQuantity(const T specialMeasure, typename vector_traits::scalar_type clampedNdotV, typename vector_traits::scalar_type clampedNdotL, NBL_CONST_REF_ARG(Query) query) +{ + return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,query.getVdotHLdotH(),query.getNeg_rcp2_VdotH_etaLdotH()); +} } } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl index 267a8bad99..f37d0d9fd8 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl @@ -8,7 +8,7 @@ #include "nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl" #include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" #include "nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl" -#include "nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl" +#include "nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl" namespace nbl { @@ -19,406 +19,11 @@ namespace bxdf namespace reflection { -template -struct SBeckmannAnisotropic; - -template) -struct SBeckmannIsotropic -{ - using this_t = SBeckmannIsotropic; - 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::Beckmann; - using fresnel_type = fresnel::Conductor; - using measure_transform_type = ndf::SDualMeasureQuant; - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - - struct SCreationParams - { - scalar_type A; - spectral_type ior0; - spectral_type ior1; - }; - using creation_type = SCreationParams; - - struct SBeckmannQuery - { - using scalar_type = scalar_type; - - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - using query_type = SBeckmannQuery; - - static this_t create(scalar_type A, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1) - { - this_t retval; - retval.__base.ndf.A = vector2_type(A, A); - retval.__base.ndf.a2 = A*A; - retval.__base.fresnel.eta = ior0; - retval.__base.fresnel.etak = ior1; - retval.__base.fresnel.etak2 = ior1*ior1; - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.A, params.ior0, params.ior1); - } - - query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - query_type query; - ndf_type beckmann_ndf = __base.ndf; - query.lambda_L = beckmann_ndf.LambdaC2(_sample.getNdotL2()); - query.lambda_V = beckmann_ndf.LambdaC2(interaction.getNdotV2()); - 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 (interaction.getNdotV() > numeric_limits::min) - { - struct SBeckmannG2overG1Query - { - using scalar_type = scalar_type; - - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - - SBeckmannG2overG1Query g2_query; - g2_query.lambda_L = query.getLambdaL(); - g2_query.lambda_V = query.getLambdaV(); - - measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); - dualMeasure.maxNdotV = interaction.getNdotV(_clamp); - scalar_type DG = dualMeasure.getProjectedLightMeasure(); - return __base.fresnel(cache.getVdotH()) * 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) - { - SBeckmannAnisotropic beckmann_aniso = SBeckmannAnisotropic::create(__base.ndf.A.x, __base.ndf.A.y, __base.fresnel.eta, __base.fresnel.etak); - anisocache_type anisocache; - sample_type s = beckmann_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 SBeckmannDG1Query - { - using scalar_type = scalar_type; - - scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type ndf; - scalar_type lambda_V; - }; - - ndf_type beckmann_ndf = __base.ndf; - - SBeckmannDG1Query dg1_query; - dg1_query.ndf = __base.__D(cache); - dg1_query.lambda_V = query.getLambdaV(); - - measure_transform_type dualMeasure = __base.template __DG1(dg1_query); - dualMeasure.maxNdotV = interaction.getNdotV(_clamp); - return dualMeasure.getProjectedLightMeasure(); - } - - 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 SBeckmannG2overG1Query - { - using scalar_type = scalar_type; - - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - - ndf_type beckmann_ndf = __base.ndf; - SBeckmannG2overG1Query g2_query; - g2_query.lambda_L = query.getLambdaL(); - g2_query.lambda_V = query.getLambdaV(); - scalar_type G2_over_G1 = beckmann_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); - const spectral_type reflectance = __base.fresnel(cache.getVdotH()); - quo = reflectance * G2_over_G1; - } - - return quotient_pdf_type::create(quo, _pdf); - } - - SCookTorrance __base; -}; - template -NBL_PARTIAL_REQ_TOP(config_concepts::MicrofacetConfiguration) -struct SBeckmannAnisotropic) > -{ - using this_t = SBeckmannAnisotropic; - 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::Beckmann; - using fresnel_type = fresnel::Conductor; - using measure_transform_type = ndf::SDualMeasureQuant; - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - - struct SCreationParams - { - scalar_type ax; - scalar_type ay; - spectral_type ior0; - spectral_type ior1; - }; - using creation_type = SCreationParams; - - struct SBeckmannQuery - { - using scalar_type = scalar_type; +using SBeckmannIsotropic = SCookTorrance, fresnel::Conductor >; - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - using query_type = SBeckmannQuery; - - static this_t create(scalar_type ax, scalar_type ay, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1) - { - this_t retval; - retval.__base.ndf.A = vector2_type(ax, ay); - retval.__base.ndf.ax2 = ax*ax; - retval.__base.ndf.ay2 = ay*ay; - retval.__base.fresnel.eta = ior0; - retval.__base.fresnel.etak = ior1; - retval.__base.fresnel.etak2 = ior1*ior1; - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.ax, params.ay, params.ior0, params.ior1); - } - - query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - query_type query; - ndf_type beckmann_ndf = __base.ndf; - query.lambda_L = beckmann_ndf.LambdaC2(_sample.getTdotL2(), _sample.getBdotL2(), _sample.getNdotL2()); - query.lambda_V = beckmann_ndf.LambdaC2(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2()); - return query; - } - - spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) - { - if (interaction.getNdotV() > numeric_limits::min) - { - struct SBeckmannG2overG1Query - { - using scalar_type = scalar_type; - - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - - SBeckmannG2overG1Query g2_query; - g2_query.lambda_L = query.getLambdaL(); - g2_query.lambda_V = query.getLambdaV(); - - measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); - dualMeasure.maxNdotV = interaction.getNdotV(_clamp); - scalar_type DG = dualMeasure.getProjectedLightMeasure(); - return __base.fresnel(cache.getVdotH()) * DG; - } - else - return hlsl::promote(0.0); - } - - vector3_type __generate(NBL_CONST_REF_ARG(vector3_type) localV, const vector2_type u) - { - vector2_type A = __base.ndf.A; - //stretch - vector3_type V = nbl::hlsl::normalize(vector3_type(A.x * localV.x, A.y * localV.y, localV.z)); - - vector2_type slope; - if (V.z > 0.9999)//V.z=NdotV=cosTheta in tangent space - { - scalar_type r = sqrt(-log(1.0 - u.x)); - scalar_type sinPhi = sin(2.0 * numbers::pi * u.y); - scalar_type cosPhi = cos(2.0 * numbers::pi * u.y); - slope = (vector2_type)r * vector2_type(cosPhi,sinPhi); - } - else - { - scalar_type cosTheta = V.z; - scalar_type sinTheta = sqrt(1.0 - cosTheta * cosTheta); - scalar_type tanTheta = sinTheta / cosTheta; - scalar_type cotTheta = 1.0 / tanTheta; - - scalar_type a = -1.0; - scalar_type c = erf(cosTheta); - scalar_type sample_x = max(u.x, 1.0e-6); - scalar_type theta = acos(cosTheta); - scalar_type fit = 1.0 + theta * (-0.876 + theta * (0.4265 - 0.0594*theta)); - scalar_type b = c - (1.0 + c) * pow(1.0-sample_x, fit); - - scalar_type normalization = 1.0 / (1.0 + c + numbers::inv_sqrtpi * tanTheta * exp(-cosTheta*cosTheta)); - - const int ITER_THRESHOLD = 10; - const float MAX_ACCEPTABLE_ERR = 1.0e-5; - int it = 0; - float value=1000.0; - while (++it < ITER_THRESHOLD && nbl::hlsl::abs(value) > MAX_ACCEPTABLE_ERR) - { - if (!(b >= a && b <= c)) - b = 0.5 * (a + c); - - float invErf = erfInv(b); - value = normalization * (1.0 + b + numbers::inv_sqrtpi * tanTheta * exp(-invErf * invErf)) - sample_x; - float derivative = normalization * (1.0 - invErf * cosTheta); - - if (value > 0.0) - c = b; - else - a = b; - - b -= value/derivative; - } - // TODO: investigate if we can replace these two erf^-1 calls with a box muller transform - slope.x = erfInv(b); - slope.y = erfInv(2.0 * max(u.y, 1.0e-6) - 1.0); - } - - scalar_type sinTheta = sqrt(1.0 - V.z*V.z); - scalar_type cosPhi = sinTheta==0.0 ? 1.0 : clamp(V.x/sinTheta, -1.0, 1.0); - scalar_type sinPhi = sinTheta==0.0 ? 0.0 : clamp(V.y/sinTheta, -1.0, 1.0); - //rotate - scalar_type tmp = cosPhi*slope.x - sinPhi*slope.y; - slope.y = sinPhi*slope.x + cosPhi*slope.y; - slope.x = tmp; - - //unstretch - slope = vector2_type(A.x,A.y)*slope; - - return nbl::hlsl::normalize(vector3_type(-slope, 1.0)); - } - - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) - { - const vector3_type localV = interaction.getTangentSpaceV(); - const vector3_type H = __generate(localV, u); - - cache = anisocache_type::createForReflection(localV, H); - ray_dir_info_type localL; - bxdf::Reflect r = bxdf::Reflect::create(localV, H); - localL.direction = r(cache.iso_cache.getVdotH()); - - return sample_type::createFromTangentSpace(localL, interaction.getFromTangentSpace()); - } - - scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) - { - struct SBeckmannDG1Query - { - using scalar_type = scalar_type; - - scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type ndf; - scalar_type lambda_V; - }; - - ndf_type beckmann_ndf = __base.ndf; - - SBeckmannDG1Query dg1_query; - dg1_query.ndf = __base.__D(cache); - dg1_query.lambda_V = query.getLambdaV(); - - measure_transform_type dualMeasure = __base.template __DG1(dg1_query); - dualMeasure.maxNdotV = interaction.getNdotV(_clamp); - return dualMeasure.getProjectedLightMeasure(); - } - - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_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 SBeckmannG2overG1Query - { - using scalar_type = scalar_type; - - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - - ndf_type beckmann_ndf = __base.ndf; - SBeckmannG2overG1Query g2_query; - g2_query.lambda_L = query.getLambdaL(); - g2_query.lambda_V = query.getLambdaV(); - scalar_type G2_over_G1 = beckmann_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); - const spectral_type reflectance = __base.fresnel(cache.getVdotH()); - quo = reflectance * G2_over_G1; - } - - return quotient_pdf_type::create(quo, _pdf); - } - - SCookTorrance __base; -}; +template +using SBeckmannAnisotropic = SCookTorrance, fresnel::Conductor >; } @@ -426,6 +31,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; @@ -434,6 +40,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index 9f64c48890..c9091112ef 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -74,6 +74,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl index f552e8f51d..049480afab 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl @@ -8,7 +8,7 @@ #include "nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl" #include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" #include "nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl" -#include "nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl" +#include "nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl" namespace nbl { @@ -19,378 +19,11 @@ namespace bxdf namespace reflection { -template -struct SGGXAnisotropic; - -template) -struct SGGXIsotropic -{ - using this_t = SGGXIsotropic; - 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::Conductor; - using measure_transform_type = ndf::SDualMeasureQuant; - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - - struct SCreationParams - { - scalar_type A; - spectral_type ior0; - spectral_type ior1; - }; - using creation_type = SCreationParams; - - struct SGGXQuery - { - 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 = SGGXQuery; - - static this_t create(scalar_type A, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1) - { - 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.eta = ior0; - retval.__base.fresnel.etak = ior1; - retval.__base.fresnel.etak2 = ior1*ior1; - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.A, params.ior0, params.ior1); - } - - 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.ndf; - 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(); - return __base.fresnel(cache.getVdotH()) * 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.eta, __base.fresnel.etak); - 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.ndf; - 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.ndf; - - 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); - - const spectral_type reflectance = __base.fresnel(cache.getVdotH()); - quo = reflectance * G2_over_G1; - } - - return quotient_pdf_type::create(quo, _pdf); - } - - SCookTorrance __base; -}; - template -NBL_PARTIAL_REQ_TOP(config_concepts::MicrofacetConfiguration) -struct SGGXAnisotropic) > -{ - using this_t = SGGXAnisotropic; - 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::Conductor; - using measure_transform_type = ndf::SDualMeasureQuant; +using SGGXIsotropic = SCookTorrance, fresnel::Conductor >; - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - - struct SCreationParams - { - scalar_type ax; - scalar_type ay; - spectral_type ior0; - spectral_type ior1; - }; - using creation_type = SCreationParams; - - struct SGGXQuery - { - 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 = SGGXQuery; - - static this_t create(scalar_type ax, scalar_type ay, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1) - { - this_t retval; - retval.__base.ndf.A = vector2_type(ax, ay); - retval.__base.ndf.ax2 = ax*ax; - retval.__base.ndf.ay2 = ay*ay; - retval.__base.ndf.a2 = ax*ay; - retval.__base.fresnel.eta = ior0; - retval.__base.fresnel.etak = ior1; - retval.__base.fresnel.etak2 = ior1*ior1; - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.ax, params.ay, params.ior0, params.ior1); - } - - query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - query_type query; - ndf_type ggx_ndf = __base.ndf; - query.devsh_v = ggx_ndf.devsh_part(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2()); - query.devsh_l = ggx_ndf.devsh_part(_sample.getTdotL2(), _sample.getBdotL2(), _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(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_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(); - return __base.fresnel(cache.getVdotH()) * DG; - } - else - return hlsl::promote(0.0); - } - - vector3_type __generate(NBL_CONST_REF_ARG(vector3_type) localV, const vector2_type u) - { - vector2_type A = __base.ndf.A; - vector3_type V = nbl::hlsl::normalize(vector3_type(A.x*localV.x, A.y*localV.y, localV.z));//stretch view vector so that we're sampling as if roughness=1.0 - - scalar_type lensq = V.x*V.x + V.y*V.y; - vector3_type T1 = lensq > 0.0 ? vector3_type(-V.y, V.x, 0.0) * rsqrt(lensq) : vector3_type(1.0,0.0,0.0); - vector3_type T2 = cross(V,T1); - - scalar_type r = sqrt(u.x); - scalar_type phi = 2.0 * numbers::pi * u.y; - scalar_type t1 = r * cos(phi); - scalar_type t2 = r * sin(phi); - scalar_type s = 0.5 * (1.0 + V.z); - t2 = (1.0 - s)*sqrt(1.0 - t1*t1) + s*t2; - - //reprojection onto hemisphere - //TODO try it wothout the max(), not sure if -t1*t1-t2*t2>-1.0 - vector3_type H = t1*T1 + t2*T2 + sqrt(max(0.0, 1.0-t1*t1-t2*t2))*V; - //unstretch - return nbl::hlsl::normalize(vector3_type(A.x*H.x, A.y*H.y, H.z)); - } - - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) - { - const vector3_type localV = interaction.getTangentSpaceV(); - const vector3_type H = __generate(localV, u); - - cache = anisocache_type::createForReflection(localV, H); - ray_dir_info_type localL; - bxdf::Reflect r = bxdf::Reflect::create(localV, H); - localL.direction = r(cache.iso_cache.getVdotH()); - - return sample_type::createFromTangentSpace(localL, interaction.getFromTangentSpace()); - } - - scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_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.ndf; - 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(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_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.ndf; - - SGGXG2XQuery g2_query; - g2_query.devsh_v = query.getDevshV(); - g2_query.devsh_l = query.getDevshL(); - g2_query._clamp = BxDFClampMode::BCM_MAX; - const scalar_type G2_over_G1 = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); - - const spectral_type reflectance = __base.fresnel(cache.getVdotH()); - quo = reflectance * G2_over_G1; - } - - return quotient_pdf_type::create(quo, _pdf); - } - - SCookTorrance __base; -}; +template +using SGGXAnisotropic = SCookTorrance, fresnel::Conductor >; } @@ -398,6 +31,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; @@ -406,6 +40,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl index 04902fec58..0e0e6bebb0 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl @@ -16,48 +16,8 @@ namespace bxdf namespace reflection { -template) -struct SLambertian -{ - using this_t = SLambertian; - BXDF_CONFIG_TYPE_ALIASES(Config); - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - return __base.eval(_sample, interaction); - } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - return __base.eval(_sample, interaction.isotropic); - } - - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u) - { - return __base.generate(anisotropic_interaction_type::create(interaction), u); - } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) - { - return __base.generate(interaction, u); - } - - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample) - { - return __base.pdf(_sample); - } - - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - return __base.quotient_and_pdf(_sample, interaction); - } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - return __base.quotient_and_pdf(_sample, interaction.isotropic); - } - - base::SLambertianBase __base; -}; +template +using SLambertian = base::SLambertianBase; } @@ -65,6 +25,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl index 5a70eb140a..df0e6ebc19 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl @@ -16,58 +16,8 @@ namespace bxdf namespace reflection { -template) -struct SOrenNayar -{ - using this_t = SOrenNayar; - BXDF_CONFIG_TYPE_ALIASES(Config); - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - - using base_type = base::SOrenNayarBase; - using creation_type = typename base_type::creation_type; - - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - this_t retval; - retval.__base = base_type::create(params); - return retval; - } - - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - return __base.eval(_sample, interaction); - } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - return __base.eval(_sample, interaction.isotropic); - } - - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u) - { - return __base.generate(anisotropic_interaction_type::create(interaction), u); - } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) - { - return __base.generate(interaction, u); - } - - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample) - { - return __base.pdf(_sample); - } - - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - return __base.quotient_and_pdf(_sample, interaction); - } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - return __base.quotient_and_pdf(_sample, interaction.isotropic); - } - - base_type __base; -}; +template +using SOrenNayar = base::SOrenNayarBase; } @@ -75,6 +25,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl index 1dc6d85f73..fa315b40ea 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl @@ -8,7 +8,7 @@ #include "nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl" #include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" #include "nbl/builtin/hlsl/bxdf/reflection.hlsl" -#include "nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl" +#include "nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl" namespace nbl { @@ -19,357 +19,11 @@ namespace bxdf namespace transmission { -template -struct SBeckmannDielectricAnisotropic; - -template) -struct SBeckmannDielectricIsotropic -{ - using this_t = SBeckmannDielectricIsotropic; - 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::SBeckmannIsotropic; - - using ndf_type = ndf::Beckmann; - using fresnel_type = fresnel::Dielectric; - using measure_transform_type = ndf::SDualMeasureQuant; - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - - struct SCreationParams - { - scalar_type A; - fresnel::OrientedEtas orientedEta; - }; - using creation_type = SCreationParams; - - struct SBeckmannQuery - { - using scalar_type = scalar_type; - - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - using query_type = SBeckmannQuery; - - static this_t create(NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, scalar_type A) - { - this_t retval; - retval.__base.ndf.A = vector2_type(A, A); - retval.__base.ndf.a2 = A*A; - retval.__base.fresnel.orientedEta = orientedEta; - retval.__base.fresnel.orientedEta2 = orientedEta.value * orientedEta.value; - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.orientedEta, params.A); - } - - query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - query_type query; - ndf_type beckmann_ndf = __base.ndf; - query.lambda_L = beckmann_ndf.LambdaC2(_sample.getNdotL2()); - query.lambda_V = beckmann_ndf.LambdaC2(interaction.getNdotV2()); - 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 SBeckmannG2overG1Query - { - using scalar_type = scalar_type; - - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - - SBeckmannG2overG1Query g2_query; - g2_query.lambda_L = query.getLambdaL(); - g2_query.lambda_V = query.getLambdaV(); - - fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; - measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); - dualMeasure.absNdotV = interaction.getNdotV(_clamp); - dualMeasure.orientedEta = orientedEta.value[0]; - scalar_type DG = dualMeasure.getProjectedLightMeasure(); - - return hlsl::promote(__base.fresnel(hlsl::abs(cache.getVdotH()))[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) - { - SBeckmannDielectricAnisotropic beckmann_aniso = SBeckmannDielectricAnisotropic::create(__base.fresnel.orientedEta, __base.ndf.A.x, __base.ndf.A.y); - anisocache_type anisocache; - sample_type s = beckmann_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 SBeckmannDG1Query - { - using scalar_type = scalar_type; - - scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type ndf; - scalar_type lambda_V; - }; - - const scalar_type reflectance = __base.fresnel(hlsl::abs(cache.getVdotH()))[0]; - - SBeckmannDG1Query dg1_query; - dg1_query.ndf = __base.__D(cache); - dg1_query.lambda_V = query.getLambdaV(); - - fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; - measure_transform_type dualMeasure = __base.template __DG1(dg1_query, cache); - dualMeasure.absNdotV = interaction.getNdotV(_clamp); - dualMeasure.orientedEta = orientedEta.value[0]; - return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * dualMeasure.getProjectedLightMeasure(); - } - - 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); - - struct SBeckmannG2overG1Query - { - using scalar_type = scalar_type; - - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - - ndf_type beckmann_ndf = __base.ndf; - SBeckmannG2overG1Query g2_query; - g2_query.lambda_L = query.getLambdaL(); - g2_query.lambda_V = query.getLambdaV(); - scalar_type quo = beckmann_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); - - return quotient_pdf_type::create(quo, _pdf); - } - - SCookTorrance __base; -}; - template -NBL_PARTIAL_REQ_TOP(config_concepts::MicrofacetConfiguration) -struct SBeckmannDielectricAnisotropic) > -{ - using this_t = SBeckmannDielectricAnisotropic; - 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::SBeckmannAnisotropic; - - using ndf_type = ndf::Beckmann; - using fresnel_type = fresnel::Dielectric; - using measure_transform_type = ndf::SDualMeasureQuant; +using SBeckmannDielectricIsotropic = SCookTorrance, fresnel::Dielectric >; - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - - struct SCreationParams - { - scalar_type ax; - scalar_type ay; - fresnel::OrientedEtas orientedEta; - }; - using creation_type = SCreationParams; - - struct SBeckmannQuery - { - using scalar_type = scalar_type; - - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - using query_type = SBeckmannQuery; - - static this_t create(NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, scalar_type ax, scalar_type ay) - { - this_t retval; - retval.__base.ndf.A = vector2_type(ax, ay); - retval.__base.ndf.ax2 = ax*ax; - retval.__base.ndf.ay2 = ay*ay; - retval.__base.fresnel.orientedEta = orientedEta; - retval.__base.fresnel.orientedEta2 = orientedEta.value * orientedEta.value; - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.orientedEta, params.ax, params.ay); - } - - query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - query_type query; - ndf_type beckmann_ndf = __base.ndf; - query.lambda_L = beckmann_ndf.LambdaC2(_sample.getTdotL2(), _sample.getBdotL2(), _sample.getNdotL2()); - query.lambda_V = beckmann_ndf.LambdaC2(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2()); - return query; - } - - spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) - { - struct SBeckmannG2overG1Query - { - using scalar_type = scalar_type; - - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - - SBeckmannG2overG1Query g2_query; - g2_query.lambda_L = query.getLambdaL(); - g2_query.lambda_V = query.getLambdaV(); - - fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; - measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); - dualMeasure.absNdotV = interaction.getNdotV(_clamp); - dualMeasure.orientedEta = orientedEta.value[0]; - scalar_type DG = dualMeasure.getProjectedLightMeasure(); - - return hlsl::promote(__base.fresnel(hlsl::abs(cache.getVdotH()))[0]) * DG; - } - - sample_type __generate_wo_clamps(const vector3_type localV, const vector3_type H, NBL_CONST_REF_ARG(matrix3x3_type) m, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, NBL_REF_ARG(anisocache_type) cache) - { - const scalar_type localVdotH = nbl::hlsl::dot(localV,H); - const scalar_type reflectance = __base.fresnel(hlsl::abs(cache.getVdotH()))[0]; - - scalar_type rcpChoiceProb; - bool transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); - - cache = anisocache_type::createForReflection(localV, H); - - const scalar_type VdotH = cache.iso_cache.getVdotH(); - Refract r = Refract::create(localV, H); - cache.iso_cache.LdotH = hlsl::mix(VdotH, r.getNdotT(rcpEta.value2[0]), transmitted); - ray_dir_info_type localL; - bxdf::ReflectRefract rr; - rr.refract = r; - localL = localL.reflectRefract(rr, transmitted, rcpEta.value[0]); - - return sample_type::createFromTangentSpace(localL, m); - } - - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) - { - const vector3_type localV = interaction.getTangentSpaceV(); - - fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; - fresnel::OrientedEtaRcps rcpEta = orientedEta.getReciprocals(); - - const vector3_type upperHemisphereV = hlsl::mix(localV, -localV, interaction.getNdotV() < scalar_type(0.0)); - - spectral_type dummyior; - brdf_type beckmann = brdf_type::create(__base.ndf.A.x, __base.ndf.A.y, dummyior, dummyior); - const vector3_type H = beckmann.__generate(upperHemisphereV, u.xy); - - return __generate_wo_clamps(localV, H, interaction.getFromTangentSpace(), u, rcpEta, cache); - } - - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) - { - anisocache_type dummycache; - return generate(interaction, u, dummycache); - } - - scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) - { - struct SBeckmannDG1Query - { - using scalar_type = scalar_type; - - scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type ndf; - scalar_type lambda_V; - }; - - const scalar_type reflectance = __base.fresnel(hlsl::abs(cache.getVdotH()))[0]; - - SBeckmannDG1Query dg1_query; - dg1_query.ndf = __base.__D(cache); - dg1_query.lambda_V = query.getLambdaV(); - - fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; - measure_transform_type dualMeasure = __base.template __DG1(dg1_query, cache); - dualMeasure.absNdotV = interaction.getNdotV(_clamp); - dualMeasure.orientedEta = orientedEta.value[0]; - return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * dualMeasure.getProjectedLightMeasure(); - } - - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) - { - scalar_type _pdf = pdf(query, interaction, cache); - - struct SBeckmannG2overG1Query - { - using scalar_type = scalar_type; - - scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } - scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } - - scalar_type lambda_L; - scalar_type lambda_V; - }; - - ndf_type beckmann_ndf = __base.ndf; - SBeckmannG2overG1Query g2_query; - g2_query.lambda_L = query.getLambdaL(); - g2_query.lambda_V = query.getLambdaV(); - scalar_type quo = beckmann_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); - - return quotient_pdf_type::create(quo, _pdf); - } - - SCookTorrance __base; -}; +template +using SBeckmannDielectricAnisotropic = SCookTorrance, fresnel::Dielectric >; } @@ -377,6 +31,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; @@ -385,6 +40,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index 60b4137d6d..4dcbcdc1bc 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -71,6 +71,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl index 2819c84570..51f096532b 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl @@ -8,7 +8,7 @@ #include "nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl" #include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" #include "nbl/builtin/hlsl/bxdf/reflection.hlsl" -#include "nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl" +#include "nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl" namespace nbl { @@ -19,383 +19,11 @@ namespace bxdf namespace transmission { -template -struct SGGXDielectricAnisotropic; - -template) -struct SGGXDielectricIsotropic -{ - using this_t = SGGXDielectricIsotropic; - 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::Dielectric; - using measure_transform_type = ndf::SDualMeasureQuant; - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - - struct SCreationParams - { - scalar_type A; - fresnel::OrientedEtas orientedEta; - }; - using creation_type = SCreationParams; - - struct SGGXQuery - { - 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 = SGGXQuery; - - static this_t create(NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, scalar_type A) - { - 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.orientedEta = orientedEta; - retval.__base.fresnel.orientedEta2 = orientedEta.value * orientedEta.value; - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.orientedEta, params.A); - } - - 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.ndf; - 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 = __base.fresnel.orientedEta; - 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(); - - return hlsl::promote(__base.fresnel(hlsl::abs(cache.getVdotH()))[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) - { - SGGXDielectricAnisotropic ggx_aniso = SGGXDielectricAnisotropic::create(__base.fresnel.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 = __base.fresnel.orientedEta; - dg1_query.orientedEta = orientedEta.value[0]; - - const scalar_type reflectance = __base.fresnel(hlsl::abs(cache.getVdotH()))[0]; - - ndf_type ggx_ndf = __base.ndf; - 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.ndf; - 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 -NBL_PARTIAL_REQ_TOP(config_concepts::MicrofacetConfiguration) -struct SGGXDielectricAnisotropic) > -{ - using this_t = SGGXDielectricAnisotropic; - 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::SGGXAnisotropic; - - using ndf_type = ndf::GGX; - using fresnel_type = fresnel::Dielectric; - using measure_transform_type = ndf::SDualMeasureQuant; - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; +using SGGXDielectricIsotropic = SCookTorrance, fresnel::Dielectric >; - struct SCreationParams - { - scalar_type ax; - scalar_type ay; - fresnel::OrientedEtas orientedEta; - }; - using creation_type = SCreationParams; - - struct SGGXQuery - { - 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 = SGGXQuery; - - static this_t create(NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, scalar_type ax, scalar_type ay) - { - this_t retval; - retval.__base.ndf.A = vector2_type(ax, ay); - retval.__base.ndf.ax2 = ax*ax; - retval.__base.ndf.ay2 = ay*ay; - retval.__base.ndf.a2 = ax*ay; - retval.__base.fresnel.orientedEta = orientedEta; - retval.__base.fresnel.orientedEta2 = orientedEta.value * orientedEta.value; - return retval; - } - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - return create(params.orientedEta, params.ax, params.ay); - } - - query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - query_type query; - ndf_type ggx_ndf = __base.ndf; - query.devsh_v = ggx_ndf.devsh_part(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2()); - query.devsh_l = ggx_ndf.devsh_part(_sample.getTdotL2(), _sample.getBdotL2(), _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(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_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 = __base.fresnel.orientedEta; - 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(); - - return hlsl::promote(__base.fresnel(hlsl::abs(cache.getVdotH()))[0]) * DG; - } - - sample_type __generate_wo_clamps(const vector3_type localV, const vector3_type H, NBL_CONST_REF_ARG(matrix3x3_type) m, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, NBL_REF_ARG(anisocache_type) cache) - { - const scalar_type localVdotH = nbl::hlsl::dot(localV,H); - const scalar_type reflectance = __base.fresnel(hlsl::abs(cache.getVdotH()))[0]; - - scalar_type rcpChoiceProb; - bool transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); - - cache = anisocache_type::createForReflection(localV, H); - - const scalar_type VdotH = cache.iso_cache.getVdotH(); - Refract r = Refract::create(localV, H); - cache.iso_cache.LdotH = hlsl::mix(VdotH, r.getNdotT(rcpEta.value2[0]), transmitted); - ray_dir_info_type localL; - bxdf::ReflectRefract rr; - rr.refract = r; - localL = localL.reflectRefract(rr, transmitted, rcpEta.value[0]); - - return sample_type::createFromTangentSpace(localL, m); - } - - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) - { - const vector3_type localV = interaction.getTangentSpaceV(); - - fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; - fresnel::OrientedEtaRcps rcpEta = orientedEta.getReciprocals(); - - const vector3_type upperHemisphereV = hlsl::mix(localV, -localV, interaction.getNdotV() < scalar_type(0.0)); - - spectral_type dummyior; - brdf_type ggx = brdf_type::create(__base.ndf.A.x, __base.ndf.A.y, dummyior, dummyior); - const vector3_type H = ggx.__generate(upperHemisphereV, u.xy); - - return __generate_wo_clamps(localV, H, interaction.getFromTangentSpace(), u, rcpEta, cache); - } - - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) - { - anisocache_type dummycache; - return generate(interaction, u, dummycache); - } - - scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_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 = __base.fresnel.orientedEta; - dg1_query.orientedEta = orientedEta.value[0]; - - const scalar_type reflectance = __base.fresnel(hlsl::abs(cache.getVdotH()))[0]; - - ndf_type ggx_ndf = __base.ndf; - 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(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_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.ndf; - SGGXG2XQuery g2_query; - g2_query.devsh_v = query.getDevshV(); - g2_query.devsh_l = query.getDevshL(); - g2_query._clamp = _clamp; - - scalar_type quo; - quo = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); - - return quotient_pdf_type::create(quo, _pdf); - } - - SCookTorrance __base; -}; +template +using SGGXDielectricAnisotropic = SCookTorrance, fresnel::Dielectric >; } @@ -403,6 +31,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; @@ -411,6 +40,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl index 21fb48f317..6ea67a65fd 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl @@ -16,48 +16,8 @@ namespace bxdf namespace transmission { -template) -struct SLambertian -{ - using this_t = SLambertian; - BXDF_CONFIG_TYPE_ALIASES(Config); - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - return __base.eval(_sample, interaction); - } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - return __base.eval(_sample, interaction.isotropic); - } - - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector3_type u) - { - return __base.generate(anisotropic_interaction_type::create(interaction), u); - } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u) - { - return __base.generate(interaction, u); - } - - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample) - { - return __base.pdf(_sample); - } - - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - return __base.quotient_and_pdf(_sample, interaction); - } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - return __base.quotient_and_pdf(_sample, interaction.isotropic); - } - - base::SLambertianBase __base; -}; +template +using SLambertian = base::SLambertianBase; } @@ -65,6 +25,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/oren_nayar.hlsl index a7640fd8d7..b39d48d2bf 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/oren_nayar.hlsl @@ -16,65 +16,16 @@ namespace bxdf namespace transmission { -template) -struct SOrenNayar -{ - using this_t = SOrenNayar; - BXDF_CONFIG_TYPE_ALIASES(Config); - - NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - - using base_type = base::SOrenNayarBase; - using creation_type = typename base_type::creation_type; - - static this_t create(NBL_CONST_REF_ARG(creation_type) params) - { - this_t retval; - retval.__base = base_type::create(params); - return retval; - } - - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - return __base.eval(_sample, interaction); - } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - return __base.eval(_sample, interaction.isotropic); - } - - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector3_type u) - { - return __base.generate(anisotropic_interaction_type::create(interaction), u); - } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u) - { - return __base.generate(interaction, u); - } - - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample) - { - return __base.pdf(_sample); - } - - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) - { - return __base.quotient_and_pdf(_sample, interaction); - } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) - { - return __base.quotient_and_pdf(_sample, interaction.isotropic); - } - - base_type __base; -}; +template +using SOrenNayar = base::SOrenNayarBase; } template struct traits > { - NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index b640bc2f18..9b0dc6e4e1 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -21,16 +21,7 @@ template; - 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(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); + BXDF_CONFIG_TYPE_ALIASES(Config); NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; @@ -68,6 +59,7 @@ struct SSmoothDielectric return 0; } + // smooth BxDFs are isotropic by definition quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) { return quotient_pdf_type::create(1.0, bit_cast(numeric_limits::infinity)); @@ -97,19 +89,14 @@ struct SThinSmoothDielectric NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - static this_t create(NBL_CONST_REF_ARG(fresnel::Dielectric) f, NBL_CONST_REF_ARG(spectral_type) luminosityContributionHint) + static this_t create(NBL_CONST_REF_ARG(fresnel::Dielectric) f) { + static_assert(vector_traits::Dimension == 3); this_t retval; retval.fresnel = f; - retval.luminosityContributionHint = luminosityContributionHint; + retval.luminosityContributionHint = spectral_type(0.2126, 0.7152, 0.0722); // rec709 return retval; } - static this_t create(NBL_CONST_REF_ARG(fresnel::Dielectric) f) - { - static_assert(vector_traits::Dimension == 3); - const spectral_type rec709 = spectral_type(0.2126, 0.7152, 0.0722); - return create(f, rec709); - } spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) { @@ -159,7 +146,7 @@ struct SThinSmoothDielectric return 0; } - // isotropic only? + // smooth BxDFs are isotropic by definition quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) { const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(interaction.getNdotV(), _sample.getNdotL()); @@ -186,6 +173,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; @@ -194,6 +182,7 @@ template struct traits > { NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; }; diff --git a/include/nbl/builtin/hlsl/cpp_compat/vector.hlsl b/include/nbl/builtin/hlsl/cpp_compat/vector.hlsl index 3658c838e3..3512079eef 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/vector.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/vector.hlsl @@ -12,6 +12,7 @@ #include #include "nbl/core/hash/blake.h" +#include "nbl/core/algorithm/utility.h" namespace nbl::hlsl { diff --git a/include/nbl/builtin/hlsl/glsl_compat/core.hlsl b/include/nbl/builtin/hlsl/glsl_compat/core.hlsl index 9c124c6ae3..7e92cbf282 100644 --- a/include/nbl/builtin/hlsl/glsl_compat/core.hlsl +++ b/include/nbl/builtin/hlsl/glsl_compat/core.hlsl @@ -242,6 +242,9 @@ namespace impl template struct equal_helper; +template +struct notEqual_helper; + #ifdef __HLSL_VERSION template @@ -268,6 +271,30 @@ struct equal_helper +NBL_PARTIAL_REQ_TOP(spirv::NotEqualIntrinsicCallable && concepts::Vectorial && concepts::Integral) +struct notEqual_helper && concepts::Vectorial && concepts::Integral) > +{ + using return_t = vector::Dimension>; + + static return_t __call(const Vectorial lhs, const Vectorial rhs) + { + return spirv::INotEqual(lhs, rhs); + } +}; + +template +NBL_PARTIAL_REQ_TOP(spirv::NotEqualIntrinsicCallable && concepts::Vectorial && concepts::FloatingPoint) +struct notEqual_helper && concepts::Vectorial && concepts::FloatingPoint) > +{ + using return_t = vector::Dimension>; + + static return_t __call(const Vectorial lhs, const Vectorial rhs) + { + return spirv::FOrdNotEqual(lhs, rhs); + } +}; + #else template @@ -290,6 +317,26 @@ struct equal_helper } }; +template +NBL_PARTIAL_REQ_TOP(concepts::Vectorial) +struct notEqual_helper) > +{ + using return_t = vector::Dimension>; + + static return_t __call(const Vectorial lhs, const Vectorial rhs) + { + using traits = hlsl::vector_traits; + array_get getter; + array_set setter; + + return_t output; + for (uint32_t i = 0; i < traits::Dimension; ++i) + setter(output, i, getter(lhs, i) != getter(rhs, i)); + + return output; + } +}; + #endif } @@ -299,6 +346,12 @@ inline vector::Dimension> equal(NBL_CONST_REF_ARG(T) x, NB return impl::equal_helper::__call(x, y); } +template +inline vector::Dimension> notEqual(NBL_CONST_REF_ARG(T) x, NBL_CONST_REF_ARG(T) y) +{ + return impl::notEqual_helper::__call(x, y); +} + } } } diff --git a/include/nbl/builtin/hlsl/ieee754.hlsl b/include/nbl/builtin/hlsl/ieee754.hlsl index 6bdfcf2514..307a11101f 100644 --- a/include/nbl/builtin/hlsl/ieee754.hlsl +++ b/include/nbl/builtin/hlsl/ieee754.hlsl @@ -159,7 +159,7 @@ struct flipSign_helper(asUint ^ spirv::select(AsUint(0ull), ieee754::traits::signMask, flip)); + return bit_cast(asUint ^ spirv::select(flip, ieee754::traits::signMask, AsUint(0ull))); #else return bit_cast(asUint ^ (flip ? ieee754::traits::signMask : AsUint(0ull))); #endif @@ -203,6 +203,40 @@ struct flipSign_helper +struct flipSignIfRHSNegative_helper; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeScalar) +struct flipSignIfRHSNegative_helper) > +{ + static FloatingPoint __call(FloatingPoint val, FloatingPoint flip) + { + using AsFloat = typename float_of_size::type; + using AsUint = typename unsigned_integer_of_size::type; + const AsUint asUint = ieee754::impl::bitCastToUintType(val); + return bit_cast(asUint ^ (ieee754::traits::signMask & ieee754::impl::bitCastToUintType(flip))); + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial) +struct flipSignIfRHSNegative_helper) > +{ + static Vectorial __call(Vectorial val, Vectorial flip) + { + using traits_v = hlsl::vector_traits; + array_get getter_v; + array_set setter; + + Vectorial output; + for (uint32_t i = 0; i < traits_v::Dimension; ++i) + setter(output, i, flipSignIfRHSNegative_helper::__call(getter_v(val, i), getter_v(flip, i))); + + return output; + } +}; } template @@ -211,6 +245,12 @@ NBL_CONSTEXPR_INLINE_FUNC T flipSign(T val, U flip) return impl::flipSign_helper::__call(val, flip); } +template +NBL_CONSTEXPR_INLINE_FUNC T flipSignIfRHSNegative(T val, T flip) +{ + return impl::flipSignIfRHSNegative_helper::__call(val, flip); +} + } } } diff --git a/include/nbl/builtin/hlsl/math/polar.hlsl b/include/nbl/builtin/hlsl/math/polar.hlsl new file mode 100644 index 0000000000..7b30e3bb8f --- /dev/null +++ b/include/nbl/builtin/hlsl/math/polar.hlsl @@ -0,0 +1,53 @@ +// 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_MATH_POLAR_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MATH_POLAR_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/numbers.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ + +template +struct Polar +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + + // should be normalized + static Polar createFromCartesian(const vector3_type coords) + { + Polar retval; + retval.theta = hlsl::acos(coords.z); + retval.phi = hlsl::atan2(coords.y, coords.x); + return retval; + } + + static vector3_type ToCartesian(const scalar_type theta, const scalar_type phi) + { + return vector(hlsl::cos(phi) * hlsl::cos(theta), + hlsl::sin(phi) * hlsl::cos(theta), + hlsl::sin(theta)); + } + + vector3_type getCartesian() + { + return ToCartesian(theta, phi); + } + + scalar_type theta; + scalar_type phi; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl b/include/nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl index 80a3cd2c52..9f95bf2ee5 100644 --- a/include/nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl +++ b/include/nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl @@ -54,7 +54,7 @@ struct ProjectedSphere using vector_t3 = vector; using hemisphere_t = ProjectedHemisphere; - static vector_t3 generate(vector_t3 _sample) + static vector_t3 generate(NBL_REF_ARG(vector_t3) _sample) { vector_t3 retval = hemisphere_t::generate(_sample.xy); const bool chooseLower = _sample.z > T(0.5); diff --git a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl index 94abdaff28..a7614469dd 100644 --- a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl +++ b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl @@ -364,6 +364,20 @@ NBL_VALID_EXPRESSION(FOrdEqualIsCallable, (T), FOrdEqual(experimental::declva template NBL_BOOL_CONCEPT EqualIntrinsicCallable = IEqualIsCallable || FOrdEqualIsCallable; +template || concepts::IntVector) +[[vk::ext_instruction(spv::OpINotEqual)]] +conditional_t, vector::Dimension>, bool> INotEqual(T lhs, T rhs); + +template || concepts::FloatingPointVector) +[[vk::ext_instruction(spv::OpFOrdNotEqual)]] +conditional_t, vector::Dimension>, bool> FOrdNotEqual(T lhs, T rhs); + +NBL_VALID_EXPRESSION(INotEqualIsCallable, (T), INotEqual(experimental::declval(),experimental::declval())); +NBL_VALID_EXPRESSION(FOrdNotEqualIsCallable, (T), FOrdNotEqual(experimental::declval(),experimental::declval())); + +template +NBL_BOOL_CONCEPT NotEqualIntrinsicCallable = INotEqualIsCallable || FOrdNotEqualIsCallable; + template && (!concepts::Vector || (concepts::Vector && vector_traits::Dimension==vector_traits::Dimension))) [[vk::ext_instruction(spv::OpSelect)]] T select(U a, T x, T y); diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index cc81b093a2..fc75243f6b 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -223,6 +223,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/linalg/transform.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/functions.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/geometry.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/intutil.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/polar.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/angle_adding.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/quadratic.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/cubic.hlsl") @@ -266,7 +267,6 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/common.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/fresnel.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/ndf.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/config.hlsl") -LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/cook_torrance_base.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/bxdf_traits.hlsl") @@ -275,6 +275,7 @@ 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/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") 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")