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..5bcac8bdd4 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -0,0 +1,383 @@ +// 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 check_TIR_helper; + +template +struct check_TIR_helper +{ + template + static bool __call(NBL_CONST_REF_ARG(F) fresnel, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + return true; + } +}; + +template +struct check_TIR_helper +{ + template + static bool __call(NBL_CONST_REF_ARG(F) fresnel, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + return ComputeMicrofacetNormal::isValidMicrofacet(cache.isTransmission(), cache.getVdotL(), cache.getAbsNdotH(), fresnel.orientedEta); + } +}; + +template +struct getOrientedFresnel; + +template +struct getOrientedFresnel +{ + static F __call(NBL_CONST_REF_ARG(F) fresnel, typename F::scalar_type NdotV) + { + // expect conductor fresnel + return fresnel; + } +}; + +template +struct getOrientedFresnel +{ + using scalar_type = typename F::scalar_type; + + static F __call(NBL_CONST_REF_ARG(F) fresnel, scalar_type NdotV) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(NdotV, fresnel.orientedEta.value); + return F::create(orientedEta); + } +}; + +template +struct CookTorranceParams; + +template +struct CookTorranceParams +{ + using scalar_type = typename F::scalar_type; + using spectral_type = Spectral; + + scalar_type A; + F fresnel; +}; + +template +struct CookTorranceParams +{ + using scalar_type = typename F::scalar_type; + using spectral_type = Spectral; + + scalar_type ax; + scalar_type ay; + F fresnel; +}; +} + +// 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::IsBSDF; + + using creation_params_type = impl::CookTorranceParams; + + template > + static enable_if_t create(NBL_CONST_REF_ARG(creation_params_type) params) + { + this_t retval; + retval.ndf = ndf_type::create(params.A); + retval.fresnel = params.fresnel; + return retval; + } + template > + static enable_if_t create(NBL_CONST_REF_ARG(creation_params_type) params) + { + this_t retval; + retval.ndf = ndf_type::create(params.ax, params.ay); + retval.fresnel = params.fresnel; + return retval; + } + + template + 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()); + const bool notTIR = impl::check_TIR_helper::template __call(_f, cache); + if ((IsBSDF && notTIR) || (!IsBSDF && _sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min)) + { + using quant_query_type = typename ndf_type::quant_query_type; + using g2g1_query_type = typename ndf_type::g2g1_query_type; + + scalar_type dummy; + quant_query_type qq = ndf.template createQuantQuery(cache, dummy); + + quant_type D = ndf.template D(qq, _sample, interaction, cache); + scalar_type DG = D.projectedLightMeasure; + if (D.microfacetMeasure < bit_cast(numeric_limits::infinity)) + { + g2g1_query_type gq = ndf.template createG2G1Query(_sample, interaction); + DG *= ndf.template correlated(gq, _sample, interaction); + } + NBL_IF_CONSTEXPR(IsBSDF) + return impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))) * DG; + else + return impl::__implicit_promote::__call(_f(cache.getVdotH())) * DG; + } + else + return hlsl::promote(0.0); + } + template > + enable_if_t eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + return __eval(_sample, interaction, cache); + } + spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + return __eval(_sample, interaction, cache); + } + + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) + { + ray_dir_info_type localV_raydir = interaction.getV().transform(interaction.getToTangentSpace()); + const vector3_type localV = interaction.getTangentSpaceV(); + const vector3_type localH = ndf.generateH(localV, u); + + cache = anisocache_type::createForReflection(localV, localH); + struct reflect_wrapper + { + vector3_type operator()() NBL_CONST_MEMBER_FUNC + { + return r(VdotH); + } + bxdf::Reflect r; + scalar_type VdotH; + }; + reflect_wrapper r; + r.r = bxdf::Reflect::create(localV, localH); + r.VdotH = cache.getVdotH(); + ray_dir_info_type localL = localV_raydir.template reflect(r); + + // fail if samples have invalid paths + if (localL.getDirection().z < scalar_type(0.0)) // NdotL<0 + localL.makeInvalid(); // should check if sample direction is invalid + + return sample_type::createFromTangentSpace(localL, interaction.getFromTangentSpace()); + } + template > + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(anisocache_type) cache) + { + fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); + fresnel::OrientedEtaRcps rcpEta = _f.getOrientedEtaRcps(); + + ray_dir_info_type V = interaction.getV(); + const vector3_type localV = interaction.getTangentSpaceV(); + const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative(localV, hlsl::promote(interaction.getNdotV())); + const vector3_type localH = ndf.generateH(upperHemisphereV, u.xy); + const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); + + const scalar_type VdotH = hlsl::dot(V.getDirection(), H); + const scalar_type reflectance = _f(hlsl::abs(VdotH))[0]; + + scalar_type rcpChoiceProb; + scalar_type z = u.z; + bool transmitted = math::partitionRandVariable(reflectance, z, rcpChoiceProb); + + Refract r = Refract::create(V.getDirection(), H); + bxdf::ReflectRefract rr; + rr.refract = r; + ray_dir_info_type L = V.reflectRefract(rr, transmitted, rcpEta.value[0]); + + const vector3_type T = interaction.getT(); + const vector3_type B = interaction.getB(); + const vector3_type _N = interaction.getN(); + + // fail if samples have invalid paths + const vector3_type Ldir = L.getDirection(); + const scalar_type LdotH = hlsl::dot(Ldir, H); + if ((ComputeMicrofacetNormal::isTransmissionPath(VdotH, LdotH) != transmitted) || (LdotH * hlsl::dot(_N, Ldir) < scalar_type(0.0))) + L.makeInvalid(); // should check if sample direction is invalid + else + cache = anisocache_type::create(VdotH, Ldir, H, T, B, _N, transmitted); + + return sample_type::create(L, T, B, _N); + } + template, typename D=bool_constant > + enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type 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, typename D=bool_constant > + enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector3_type 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 > + enable_if_t pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + if (IsBSDF || (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min)) + { + scalar_type _pdf = __pdf(_sample, interaction, cache); + return hlsl::mix(scalar_type(0.0), _pdf, _pdf < bit_cast(numeric_limits::infinity)); + } + else + return scalar_type(0.0); + } + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + if (IsBSDF || (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min)) + { + scalar_type _pdf = __pdf(_sample, interaction, cache); + return hlsl::mix(scalar_type(0.0), _pdf, _pdf < bit_cast(numeric_limits::infinity)); + } + else + return scalar_type(0.0); + } + + template + 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) + { + scalar_type _pdf = __pdf(_sample, interaction, cache); + fresnel_type _f = impl::getOrientedFresnel::__call(fresnel, interaction.getNdotV()); + + spectral_type quo = hlsl::promote(0.0); + const bool notTIR = impl::check_TIR_helper::template __call(_f, cache); + if (notTIR) + { + using g2g1_query_type = typename N::g2g1_query_type; + g2g1_query_type gq = ndf.template createG2G1Query(_sample, interaction); + scalar_type G2_over_G1 = ndf.template G2_over_G1(gq, _sample, interaction, cache); + NBL_IF_CONSTEXPR(IsBSDF) + quo = hlsl::promote(G2_over_G1); + else + quo = _f(cache.getVdotH()) * G2_over_G1; + } + + // set pdf=0 when quo=0 because we don't want to give high weight to sampling strategy that yields 0 contribution + _pdf = hlsl::mix(_pdf, scalar_type(0.0), hlsl::all(quo < hlsl::promote(numeric_limits::min))); + return quotient_pdf_type::create(quo, _pdf); + } + template > + enable_if_t quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + return __quotient_and_pdf(_sample, interaction, cache); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + return __quotient_and_pdf(_sample, interaction, cache); + } + + 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..f0245c9849 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -29,26 +29,39 @@ 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); 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 +70,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..87f95e79d8 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -64,22 +64,35 @@ 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); 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 +115,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..605641056c 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! @@ -343,6 +359,7 @@ 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)) @@ -435,7 +452,7 @@ 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))); } + bool isValid() NBL_CONST_MEMBER_FUNC { return L.isValid(); } RayDirInfo L; @@ -767,6 +784,22 @@ struct SAnisotropicMicrofacetCache retval.BdotH = nbl::hlsl::dot(interaction.getB(),H); return retval; } + static this_t create( + const scalar_type VdotH, const vector3_type L, const vector3_type H, + const vector3_type T, const vector3_type B, const vector3_type N, bool transmitted + ) + { + this_t retval; + retval.iso_cache.VdotH = VdotH; + retval.iso_cache.LdotH = hlsl::mix(VdotH, hlsl::dot(L, H), transmitted); + scalar_type NdotH = hlsl::dot(N, H); + assert(NdotH > scalar_type(0.0)); + retval.iso_cache.absNdotH = hlsl::abs(NdotH); + retval.iso_cache.NdotH2 = NdotH * NdotH; + retval.TdotH = hlsl::dot(T, H); + retval.BdotH = hlsl::dot(B, H); + return retval; + } scalar_type getVdotL() NBL_CONST_MEMBER_FUNC { return iso_cache.getVdotL(); } scalar_type getVdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.getVdotH(); } @@ -791,6 +824,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 +852,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 +986,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 +1019,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..37e786ab31 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; @@ -351,8 +351,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 +360,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 +370,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; @@ -384,8 +383,8 @@ struct Conductor } T eta; - T etak; T etak2; + T etaLen2; }; template) @@ -421,6 +420,8 @@ struct Dielectric return __call(orientedEta2, clampedCosTheta); } + OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { return orientedEta.getReciprocals(); } + OrientedEtas orientedEta; T orientedEta2; }; diff --git a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl index 9189d1baec..09265a2cf0 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl @@ -16,53 +16,63 @@ namespace bxdf namespace ndf { -namespace dummy_impl +namespace 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 +template +struct NDFQuantQuery { - using scalar_type = float; + using scalar_type = T; - 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 getVdotHLdotH() NBL_CONST_MEMBER_FUNC { return VdotHLdotH; } + scalar_type getVdotH_etaLdotH() NBL_CONST_MEMBER_FUNC { return VdotH_etaLdotH; } - 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; } + scalar_type VdotHLdotH; + scalar_type VdotH_etaLdotH; }; } +namespace dummy_impl +{ +using sample_t = SLightSample >; +using interaction_t = surface_interactions::SAnisotropic > >; +using cache_t = SAnisotropicMicrofacetCache >; +} + #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 diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl index 8076102d94..37a7318000 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,48 +48,63 @@ NBL_CONCEPT_END( #include } +namespace impl +{ + +template +struct SBeckmannDG1Query +{ + using scalar_type = T; + + 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; +}; + +template +struct SBeckmannG2overG1Query +{ + using scalar_type = T; + + 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; +}; + template -struct Beckmann; +struct BeckmannCommon; template NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) -struct Beckmann) > +struct BeckmannCommon) > { 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 NdotH2 = cache.getNdotH2(); + scalar_type nom = exp2((NdotH2 - scalar_type(1.0)) / (log(2.0) * a2 * NdotH2)); + scalar_type denom = a2 * NdotH2 * NdotH2; + return hlsl::mix(bit_cast(numeric_limits::infinity), numbers::inv_pi * nom / denom, a2 > numeric_limits::min); } - // brdf template) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query) + static scalar_type DG1(NBL_CONST_REF_ARG(Query) query) { return query.getNdf() / (scalar_type(1.0) + query.getLambdaV()); } - // bsdf - template && ReadableIsotropicMicrofacetCache) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache) - { - return DG1(query); - } - - scalar_type G1(scalar_type lambda) - { - return scalar_type(1.0) / (scalar_type(1.0) + lambda); - } - scalar_type C2(scalar_type NdotX2) { return NdotX2 / (a2 * (scalar_type(1.0) - NdotX2)); } - 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; @@ -101,65 +117,52 @@ struct Beckmann) > return Lambda(C2(NdotX2)); } - 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) + static scalar_type correlated(NBL_CONST_REF_ARG(Query) query) { return scalar_type(1.0) / (scalar_type(1.0) + query.getLambdaV() + query.getLambdaL()); } - 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 && ReadableIsotropicMicrofacetCache) + static scalar_type G2_over_G1(NBL_CONST_REF_ARG(Query) query, 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 a2; }; - template NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) -struct Beckmann) > +struct BeckmannCommon) > { 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(); - return numbers::inv_pi * nom / denom; + scalar_type NdotH2 = cache.getNdotH2(); + scalar_type nom = exp(-(cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2) / NdotH2); + scalar_type denom = a2 * NdotH2 * NdotH2; + return hlsl::mix(bit_cast(numeric_limits::infinity), numbers::inv_pi * nom / denom, a2 > numeric_limits::min); } template) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query) - { - Beckmann beckmann; - scalar_type dg = beckmann.template DG1(query); - return dg; - } - - template && AnisotropicMicrofacetCache) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache) + static scalar_type DG1(NBL_CONST_REF_ARG(Query) query) { - Beckmann beckmann; - scalar_type dg = beckmann.template DG1(query, cache.iso_cache); + BeckmannCommon beckmann; + scalar_type dg = beckmann.DG1(query); return dg; } - scalar_type G1(scalar_type lambda) - { - return scalar_type(1.0) / (scalar_type(1.0) + lambda); - } - scalar_type C2(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) { return NdotX2 / (TdotX2 * ax2 + BdotX2 * ay2); } - 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; @@ -172,22 +175,255 @@ struct Beckmann) > return Lambda(C2(TdotX2, BdotX2, NdotX2)); } - 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) + static scalar_type correlated(NBL_CONST_REF_ARG(Query) query) { 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 && AnisotropicMicrofacetCache) + static scalar_type G2_over_G1(NBL_CONST_REF_ARG(Query) query, 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; + 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) + { + //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 ax; + scalar_type ay; +}; +} + + +template) +struct Beckmann +{ + using this_t = Beckmann; + using scalar_type = T; + using base_type = impl::BeckmannCommon; + using quant_type = SDualMeasureQuant; + using vector2_type = vector; + using vector3_type = vector; + + using dg1_query_type = impl::SBeckmannDG1Query; + using g2g1_query_type = impl::SBeckmannG2overG1Query; + using quant_query_type = impl::NDFQuantQuery; + + NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = _IsAnisotropic; + NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = reflect_refract != MTT_REFLECT; + template + NBL_CONSTEXPR_STATIC_INLINE bool RequiredInteraction = IsAnisotropic ? surface_interactions::Anisotropic : surface_interactions::Isotropic; + template + NBL_CONSTEXPR_STATIC_INLINE bool RequiredMicrofacetCache = IsAnisotropic ? AnisotropicMicrofacetCache : ReadableIsotropicMicrofacetCache; + + template > + static enable_if_t create(scalar_type A) + { + this_t retval; + retval.__ndf_base.a2 = A*A; + retval.__generate_base.ax = A; + retval.__generate_base.ay = A; + return retval; + } + template > + static enable_if_t create(scalar_type ax, scalar_type ay) + { + 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 NBL_FUNC_REQUIRES(RequiredMicrofacetCache) + enable_if_t createQuantQuery(NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta) + { + quant_query_type dummy; // brdfs don't make use of this + return dummy; + } + template NBL_FUNC_REQUIRES(RequiredMicrofacetCache) + enable_if_t createQuantQuery(NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta) + { + quant_query_type quant_query; + quant_query.VdotHLdotH = cache.getVdotHLdotH(); + quant_query.VdotH_etaLdotH = cache.getVdotH() + orientedEta * cache.getLdotH(); + 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 = __ndf_base.LambdaC2(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 = __ndf_base.LambdaC2(_sample.getNdotL2()); + g2_query.lambda_V = __ndf_base.LambdaC2(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 = __ndf_base.LambdaC2(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.lambda_L = __ndf_base.LambdaC2(_sample.getTdotL2(), _sample.getBdotL2(), _sample.getNdotL2()); + g2_query.lambda_V = __ndf_base.LambdaC2(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2()); + return g2_query; + } + + vector3_type generateH(const vector3_type localV, const vector2_type u) + { + return __generate_base.__call(localV, u); + } + + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction && RequiredMicrofacetCache) + enable_if_t 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_MAX), _sample.getNdotL(BxDFClampMode::BCM_MAX)); + } + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction && RequiredMicrofacetCache) + enable_if_t 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.getVdotHLdotH(), quant_query.getVdotH_etaLdotH()); + } + + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction) + enable_if_t 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 = base_type::template DG1(query); + return createDualMeasureQuantity(dg1, interaction.getNdotV(BxDFClampMode::BCM_MAX), _sample.getNdotL(BxDFClampMode::BCM_MAX)); + } + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction) + enable_if_t 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 = base_type::template DG1(query); + return createDualMeasureQuantity(dg1, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query.getVdotHLdotH(), quant_query.getVdotH_etaLdotH()); + } + + 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 base_type::template correlated(query); + } + + 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) + { + return base_type::template G2_over_G1(query, cache); + } + + base_type __ndf_base; + impl::BeckmannGenerateH __generate_base; +}; + +template +struct PhongExponent +{ + //conversion between alpha and Phong exponent, Walter et.al. + static T ToAlpha2(T _n) + { + return T(2.0) / (_n + T(2.0)); + } + //+INF for a2==0.0 + static T FromAlpha2(T a2) + { + return T(2.0) / a2 - T(2.0); + } }; } diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index eb49bd38f0..2e3b78a48f 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) @@ -32,22 +33,7 @@ NBL_CONCEPT_END( #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,49 +43,63 @@ 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 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; +}; + +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 = IsBSDF ? BxDFClampMode::BCM_ABS : BxDFClampMode::BCM_MAX; + // trowbridge-reitz template) scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) { scalar_type denom = scalar_type(1.0) - one_minus_a2 * cache.getNdotH2(); - return a2 * numbers::inv_pi / (denom * denom); + return hlsl::mix(bit_cast(numeric_limits::infinity), a2 * numbers::inv_pi / (denom * denom), a2 > numeric_limits::min); } - template) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query) + template) + static 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); @@ -111,30 +111,30 @@ struct GGX) > return scalar_type(1.0) / (absNdotX + devsh_part(NdotX2)); } - scalar_type G1_wo_numerator_devsh_part(scalar_type absNdotX, scalar_type devsh_part) + static 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); } // 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) + template && LightSample && surface_interactions::Isotropic) + static scalar_type correlated_wo_numerator(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); - 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 && LightSample && surface_interactions::Isotropic) + static 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); + return scalar_type(4.0) * interaction.getNdotV(_clamp) * _sample.getNdotL(_clamp) * correlated_wo_numerator(query, _sample, interaction); + } + template && LightSample && surface_interactions::Isotropic && ReadableIsotropicMicrofacetCache) + static 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 G2_over_G1; scalar_type NdotV = interaction.getNdotV(_clamp); scalar_type NdotL = _sample.getNdotL(_clamp); @@ -150,54 +150,39 @@ struct GGX) > } else { - G2_over_G1 = NdotL * (devsh_v + NdotV); // alternative `Vterm+NdotL*NdotV /// NdotL*NdotV could come as a parameter + G2_over_G1 = NdotL * (devsh_v + NdotV); G2_over_G1 /= NdotV * devsh_l + NdotL * devsh_v; } return G2_over_G1; } - vector A; scalar_type a2; scalar_type one_minus_a2; }; -template +template NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) -struct GGX) > +struct GGXCommon) > { using scalar_type = T; + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = IsBSDF ? BxDFClampMode::BCM_ABS : BxDFClampMode::BCM_MAX; + template) scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) { scalar_type denom = cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2 + cache.getNdotH2(); - return numbers::inv_pi / (a2 * denom * denom); - } - - // burley - scalar_type D(scalar_type a2, scalar_type TdotH, scalar_type BdotH, scalar_type NdotH, scalar_type anisotropy) - { - 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; + return hlsl::mix(bit_cast(numeric_limits::infinity), numbers::inv_pi / (a2 * denom * denom), a2 > numeric_limits::min); } - template) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query) - { - GGX ggx; - return ggx.template DG1(query); - } + // TODO: potential idea for making GGX spin using covariance matrix of sorts: https://www.desmos.com/3d/weq2ginq9o - template && AnisotropicMicrofacetCache) - scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache) + template) + static scalar_type DG1(NBL_CONST_REF_ARG(Query) query) { - GGX ggx; - return ggx.template DG1(query, cache.iso_cache); + GGXCommon ggx; + return ggx.DG1(query); } scalar_type devsh_part(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) @@ -211,29 +196,29 @@ struct GGX) > return scalar_type(1.0) / (NdotX + devsh_part(TdotX2, BdotX2, NdotX2)); } - scalar_type G1_wo_numerator_devsh_part(scalar_type NdotX, scalar_type devsh_part) + static scalar_type G1_wo_numerator_devsh_part(scalar_type NdotX, scalar_type devsh_part) { return scalar_type(1.0) / (NdotX + devsh_part); } // 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 && LightSample && surface_interactions::Anisotropic) + static scalar_type correlated_wo_numerator(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); - 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 && LightSample && surface_interactions::Anisotropic) + static 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); + return scalar_type(4.0) * interaction.getNdotV(_clamp) * _sample.getNdotL(_clamp) * correlated_wo_numerator(query, _sample, interaction); + } + template && LightSample && surface_interactions::Anisotropic && AnisotropicMicrofacetCache) + static 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 G2_over_G1; scalar_type NdotV = interaction.getNdotV(_clamp); scalar_type NdotL = _sample.getNdotL(_clamp); @@ -256,19 +241,223 @@ struct GGX) > return G2_over_G1; } - vector A; scalar_type ax2; scalar_type ay2; scalar_type a2; }; +template +struct GGXGenerateH +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + + vector3_type __call(const vector3_type localV, const vector2_type u) + { + vector3_type V = nbl::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) * 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 + //tested, seems -t1*t1-t2*t2>-1.0 + vector3_type H = t1*T1 + t2*T2 + sqrt(1.0-t1*t1-t2*t2)*V; + //unstretch + return nbl::hlsl::normalize(vector3_type(ax*H.x, ay*H.y, H.z)); + } + + scalar_type ax; + scalar_type ay; +}; +} + + +template) +struct GGX +{ + NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = _IsAnisotropic; + NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = reflect_refract != MTT_REFLECT; + + using this_t = GGX; + using scalar_type = T; + using base_type = impl::GGXCommon; + using quant_type = SDualMeasureQuant; + using vector2_type = vector; + using vector3_type = vector; + + using dg1_query_type = impl::SGGXDG1Query; + using g2g1_query_type = impl::SGGXG2XQuery; + using quant_query_type = impl::NDFQuantQuery; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = IsBSDF ? BxDFClampMode::BCM_ABS : BxDFClampMode::BCM_NONE; + template + NBL_CONSTEXPR_STATIC_INLINE bool RequiredInteraction = IsAnisotropic ? surface_interactions::Anisotropic : surface_interactions::Isotropic; + template + NBL_CONSTEXPR_STATIC_INLINE bool RequiredMicrofacetCache = IsAnisotropic ? AnisotropicMicrofacetCache : ReadableIsotropicMicrofacetCache; + + template > + static enable_if_t create(scalar_type A) + { + 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 > + static enable_if_t create(scalar_type ax, scalar_type ay) + { + 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 NBL_FUNC_REQUIRES(RequiredMicrofacetCache) + enable_if_t createQuantQuery(NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta) + { + quant_query_type dummy; // brdfs don't make use of this + return dummy; + } + template NBL_FUNC_REQUIRES(RequiredMicrofacetCache) + enable_if_t createQuantQuery(NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta) + { + quant_query_type quant_query; + quant_query.VdotHLdotH = cache.getVdotHLdotH(); + quant_query.VdotH_etaLdotH = cache.getVdotH() + orientedEta * cache.getLdotH(); + 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 = __ndf_base.G1_wo_numerator(clampedNdotV, 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 = __ndf_base.G1_wo_numerator(clampedNdotV, 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; + } + + vector3_type generateH(const vector3_type localV, const vector2_type u) + { + return __generate_base.__call(localV, u); + } + + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction && RequiredMicrofacetCache) + enable_if_t 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); + quant_type dmq; + dmq.microfacetMeasure = d; + dmq.projectedLightMeasure = d * _sample.getNdotL(BxDFClampMode::BCM_MAX); + return dmq; + } + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction && RequiredMicrofacetCache) + enable_if_t 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); + quant_type dmq; + dmq.microfacetMeasure = d; // note: microfacetMeasure/2NdotV + + const scalar_type VdotHLdotH = quant_query.getVdotHLdotH(); + const scalar_type VdotH_etaLdotH = quant_query.getVdotH_etaLdotH(); + 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 / (VdotH_etaLdotH * VdotH_etaLdotH); + dmq.projectedLightMeasure = d * NdotL_over_denominator; + return dmq; + } + + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction) + enable_if_t 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 = base_type::template DG1(query); + quant_type dmq; + dmq.microfacetMeasure = dg1; + dmq.projectedLightMeasure = dg1;// TODO: figure this out * _sample.getNdotL(BxDFClampMode::BCM_MAX); + return dmq; + } + template NBL_FUNC_REQUIRES(LightSample && RequiredInteraction) + enable_if_t 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 = base_type::template DG1(query); + quant_type dmq; + dmq.microfacetMeasure = dg1; // note: microfacetMeasure/2NdotV + + const scalar_type VdotHLdotH = quant_query.getVdotHLdotH(); + const scalar_type VdotH_etaLdotH = quant_query.getVdotH_etaLdotH(); + 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 / (VdotH_etaLdotH * VdotH_etaLdotH); + dmq.projectedLightMeasure = dg1;// TODO: figure this out * NdotL_over_denominator; + return dmq; + } + + 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 base_type::template correlated_wo_numerator(query, _sample, interaction); + } + + 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) + { + return base_type::template G2_over_G1(query, _sample, interaction, cache); + } + + 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..badfbd99f0 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 @@ -23,181 +23,52 @@ enum MicrofacetTransformTypes : uint16_t MTT_REFLECT_REFRACT = 0b11 }; -template -struct SDualMeasureQuant; template -struct SDualMeasureQuant +struct SDualMeasureQuant { - 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; + using value_type = T; + + T microfacetMeasure; + T projectedLightMeasure; }; -template -struct SDualMeasureQuant -{ - 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 pdf * maxNdotL; - } - - scalar_type pdf; - scalar_type maxNdotL; -}; - -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 +namespace impl { - 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; -}; - -template -struct SDualMeasureQuant +template +struct createDualMeasureQuantity_helper { - 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 + 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 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*mix(scalar_type(0.25),VdotHLdotH,transmitted); + scalar_type denominator = clampedNdotV; + if (transmitted) // 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; + retval.projectedLightMeasure /= denominator; + 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 VdotH_etaLdotH) +{ + return impl::createDualMeasureQuantity_helper::__call(specialMeasure,clampedNdotV,clampedNdotL,VdotHLdotH,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..e34b87193b 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 { @@ -92,4 +93,31 @@ struct blake3_hasher::update_impl,Dummy> } #endif } + +#ifndef __HLSL_VERSION +namespace std +{ +template +struct hash > +{ + size_t operator()(const nbl::hlsl::vector& v) const noexcept + { + size_t seed = 0; + NBL_UNROLL for (uint16_t i = 0; i < N; i++) + nbl::core::hash_combine(seed, v[i]); + return seed; + } +}; + +template +struct hash > +{ + size_t operator()(const nbl::hlsl::vector& v) const noexcept + { + std::hash hasher; + return hasher(v.x); + } +}; +} +#endif #endif \ No newline at end of file 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..37c8e92469 100644 --- a/include/nbl/builtin/hlsl/ieee754.hlsl +++ b/include/nbl/builtin/hlsl/ieee754.hlsl @@ -159,13 +159,26 @@ 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 } }; +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeScalar) +struct flipSign_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 && concepts::BooleanScalar) struct flipSign_helper && concepts::BooleanScalar) > @@ -203,6 +216,24 @@ struct flipSign_helper +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial) +struct flipSign_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, flipSign_helper::__call(getter_v(val, i), getter_v(flip, i))); + + return output; + } +}; } template @@ -211,6 +242,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::flipSign_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/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")