diff --git a/examples_tests b/examples_tests index 66e486ca1a..c1493d6ee2 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 66e486ca1a33932c498d849effe9af3358ac1095 +Subproject commit c1493d6ee21ad388eb21c07d41cdbcff2ce4f8d5 diff --git a/include/nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl b/include/nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl new file mode 100644 index 0000000000..5ef93e7abf --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl @@ -0,0 +1,107 @@ +// 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_TRAITS_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRAITS_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ + +enum BxDFType : uint16_t +{ + BT_BSDF = 0, + BT_BRDF, + BT_BTDF +}; + +template +struct traits; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +// no blinn phong + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index c6e8679d3b..c3bf426538 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2023 - 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_COMMON_INCLUDED_ @@ -6,39 +6,183 @@ #include "nbl/builtin/hlsl/limits.hlsl" #include "nbl/builtin/hlsl/numbers.hlsl" -#include "nbl/builtin/hlsl/math/functions.glsl" +#include "nbl/builtin/hlsl/type_traits.hlsl" +#include "nbl/builtin/hlsl/concepts.hlsl" +#include "nbl/builtin/hlsl/ieee754.hlsl" +#include "nbl/builtin/hlsl/tgmath.hlsl" +#include "nbl/builtin/hlsl/math/functions.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" +#include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" namespace nbl { namespace hlsl { + namespace bxdf { +template) +struct ComputeMicrofacetNormal +{ + using scalar_type = T; + using vector_type = vector; + using monochrome_type = vector; + + using unsigned_integer_type = typename unsigned_integer_of_size::type; + + static ComputeMicrofacetNormal create(NBL_CONST_REF_ARG(vector_type) V, NBL_CONST_REF_ARG(vector_type) L, NBL_CONST_REF_ARG(vector_type) H, scalar_type eta) + { + ComputeMicrofacetNormal retval; + retval.V = V; + retval.L = L; + fresnel::OrientedEtas orientedEtas = fresnel::OrientedEtas::create(hlsl::dot(V, H), eta); + retval.orientedEta = orientedEtas.value; + return retval; + } + + static ComputeMicrofacetNormal create(NBL_CONST_REF_ARG(vector_type) V, NBL_CONST_REF_ARG(vector_type) L, scalar_type VdotH, scalar_type eta) + { + ComputeMicrofacetNormal retval; + retval.V = V; + retval.L = L; + fresnel::OrientedEtas orientedEtas = fresnel::OrientedEtas::create(VdotH, eta); + retval.orientedEta = orientedEtas.value; + return retval; + } + + // NDFs are defined in terms of `abs(NdotH)` and microfacets are two sided. Note that `N` itself is the definition of the upper hemisphere direction. + // The possible directions of L form a cone around -V with the cosine of the angle equal higher or equal to min(orientedEta, 1.f/orientedEta), and vice versa. + // This means that for: + // - Eta>1 the L will be longer than V projected on V, and VdotH<0 for all L + // - whereas with Eta<1 the L is shorter, and VdotH>0 for all L + // Because to be a refraction `VdotH` and `LdotH` must differ in sign, so whenever one is positive the other is negative. + // Since we're considering single scattering, the V and L must enter the microfacet described by H same way they enter the macro-medium described by N. + // All this means that by looking at the sign of VdotH we can also tell the sign of VdotN. + // However the whole `V+L*eta` formula is backwards because what it should be is `-V-L*eta` so the sign flip is applied just to restore the H-finding to that value. + + // The math: + // dot(V,H) = V2 + VdotL*eta = 1 + VdotL*eta, note that VdotL<=1 so VdotH>=0 when eta==1 + // then with valid transmission path constraint: + // VdotH <= 1-orientedEta2 for orientedEta<1 -> VdotH<0 + // VdotH <= 0 for orientedEta>1 + // so for transmission VdotH<=0, H needs to be flipped to be consistent with oriented eta + vector_type unnormalized(const bool _refract) + { + assert(hlsl::dot(V, L) <= -hlsl::min(orientedEta, scalar_type(1.0) / orientedEta)); + const scalar_type etaFactor = hlsl::mix(scalar_type(1.0), orientedEta.value, _refract); + vector_type tmpH = V + L * etaFactor; + tmpH = ieee754::flipSign(tmpH, _refract); + return tmpH; + } + + // returns normalized vector, but NaN when result is length 0 + vector_type normalized(const bool _refract) + { + const vector_type H = unnormalized(_refract); + return hlsl::normalize(H); + } + + // if V and L are on different sides of the surface normal, then their dot product sign bits will differ, hence XOR will yield 1 at last bit + static bool isTransmissionPath(scalar_type NdotV, scalar_type NdotL) + { + return bool((hlsl::bit_cast(NdotV) ^ hlsl::bit_cast(NdotL)) & unsigned_integer_type(1u)<<(sizeof(scalar_type)*8u-1u)); + } + + vector_type V; + vector_type L; + scalar_type orientedEta; +}; + namespace ray_dir_info { -// no ray-differentials, nothing -struct Basic +#define NBL_CONCEPT_NAME Basic +#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_2 (dirDotN, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_3 (m, typename T::matrix3x3_type) +#define NBL_CONCEPT_PARAM_4 (rfl, Reflect) +#define NBL_CONCEPT_PARAM_5 (rfr, Refract) +#define NBL_CONCEPT_PARAM_6 (backside, bool) +NBL_CONCEPT_BEGIN(7) +#define rdirinfo NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define N NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define dirDotN 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 +#define rfr NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define backside NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +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_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)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.refract(rfr, backside, dirDotN)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.transform(m)), ::nbl::hlsl::is_same_v, T)) + ((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)) +); +#undef backside +#undef rfr +#undef rfl +#undef m +#undef dirDotN +#undef N +#undef rdirinfo +#include + +template +struct SBasic { - float3 getDirection() {return direction;} - - Basic transmit() - { - Basic retval; - retval.direction = -direction; - return retval; - } - - Basic reflect(const float3 N, const float directionDotN) - { - Basic retval; - retval.direction = nbl::hlsl::reflect(direction,N,directionDotN); - return retval; - } - - float3 direction; + using scalar_type = T; + using vector3_type = vector; + using matrix3x3_type = matrix; + + vector3_type getDirection() NBL_CONST_MEMBER_FUNC { return direction; } + + SBasic transmit() + { + SBasic retval; + retval.direction = -direction; + return retval; + } + + SBasic reflect(NBL_CONST_REF_ARG(Reflect) r) + { + SBasic retval; + retval.direction = r(); + return retval; + } + + SBasic refract(NBL_CONST_REF_ARG(Refract) r, const bool backside, scalar_type rcpOrientedEta) + { + SBasic retval; + retval.direction = r(backside, rcpOrientedEta); + return retval; + } + + // WARNING: matrix must be orthonormal + SBasic transform(NBL_CONST_REF_ARG(matrix3x3_type) m) NBL_CONST_MEMBER_FUNC + { + matrix3x3_type m_T = nbl::hlsl::transpose(m); + assert(nbl::hlsl::abs(nbl::hlsl::dot(m_T[0], m_T[1])) < 1e-5); + assert(nbl::hlsl::abs(nbl::hlsl::dot(m_T[0], m_T[2])) < 1e-5); + assert(nbl::hlsl::abs(nbl::hlsl::dot(m_T[1], m_T[2])) < 1e-5); + + SBasic retval; + retval.direction = nbl::hlsl::mul(m, direction); + return retval; + } + + vector3_type direction; }; // more to come! @@ -48,359 +192,761 @@ struct Basic namespace surface_interactions { -template -struct Isotropic +#define NBL_CONCEPT_NAME Isotropic +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (iso, T) +#define NBL_CONCEPT_PARAM_1 (normV, typename T::ray_dir_info_type) +#define NBL_CONCEPT_PARAM_2 (normN, typename T::vector3_type) +NBL_CONCEPT_BEGIN(3) +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define normV NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define normN NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getV()), ::nbl::hlsl::is_same_v, typename T::ray_dir_info_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getN()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getNdotV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((iso.getNdotV2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(normV,normN)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) +); +#undef normN +#undef normV +#undef iso +#include + +template) +struct SIsotropic { - // WARNING: Changed since GLSL, now arguments need to be normalized! - static Isotropic create(const RayDirInfo normalizedV, const float3 normalizedN) - { - Isotropic retval; - retval.V = normalizedV; - retval.N = normalizedN; - - retval.NdotV = dot(retval.N,retval.V.getDirection()); - retval.NdotV_squared = retval.NdotV*retval.NdotV; - - return retval; - } - - RayDirInfo V; - float3 N; - float NdotV; - float NdotV2; // old NdotV_squared + using ray_dir_info_type = RayDirInfo; + using scalar_type = typename RayDirInfo::scalar_type; + using vector3_type = typename RayDirInfo::vector3_type; + + // WARNING: Changed since GLSL, now arguments need to be normalized! + static SIsotropic create(NBL_CONST_REF_ARG(RayDirInfo) normalizedV, NBL_CONST_REF_ARG(vector3_type) normalizedN) + { + SIsotropic retval; + retval.V = normalizedV; + retval.N = normalizedN; + retval.NdotV = nbl::hlsl::dot(retval.N, retval.V.getDirection()); + retval.NdotV2 = retval.NdotV * retval.NdotV; + + return retval; + } + + RayDirInfo getV() NBL_CONST_MEMBER_FUNC { return V; } + vector3_type getN() NBL_CONST_MEMBER_FUNC { return N; } + scalar_type getNdotV() NBL_CONST_MEMBER_FUNC { return NdotV; } + scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return NdotV2; } + + RayDirInfo V; + vector3_type N; + scalar_type NdotV; + scalar_type NdotV2; }; -template -struct Anisotropic : Isotropic +#define NBL_CONCEPT_NAME Anisotropic +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (aniso, T) +#define NBL_CONCEPT_PARAM_1 (iso, typename T::isotropic_interaction_type) +#define NBL_CONCEPT_PARAM_2 (normT, typename T::vector3_type) +NBL_CONCEPT_BEGIN(3) +#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define normT NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) + ((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_RET_TYPE)((aniso.getT()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getB()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getTdotV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getBdotV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(iso,normT,normT)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getTangentSpaceV()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getToTangentSpace()), ::nbl::hlsl::is_same_v, typename T::matrix3x3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((aniso.getFromTangentSpace()), ::nbl::hlsl::is_same_v, typename T::matrix3x3_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Isotropic, typename T::isotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) +); +#undef normT +#undef iso +#undef aniso +#include + +template) +struct SAnisotropic { - // WARNING: Changed since GLSL, now arguments need to be normalized! - static Anisotropic create( - const Isotropic isotropic, - const float3 normalizedT, - const float normalizedB - ) - { - Anisotropic retval; - retval::Isotropic = isotropic; - retval.T = normalizedT; - retval.B = normalizedB; - - const float3 V = retval.getDirection(); - retval.TdotV = dot(V,retval.T); - retval.BdotV = dot(V,retval.B); - - return retval; - } - static Anisotropic create(const Isotropic isotropic, const float3 normalizedT) - { - return create(isotropic,normalizedT,cross(isotropic.N,normalizedT)); - } - static Anisotropic create(const Isotropic isotropic) - { - float2x3 TB = nbl::hlsl::frisvad(isotropic.N); - return create(isotropic,TB[0],TB[1]); - } - - float3 getTangentSpaceV() {return float3(Tdot,BdotV,Isotropic::NdotV);} - // WARNING: its the transpose of the old GLSL function return value! - float3x3 getTangentFrame() {return float3x3(T,B,Isotropic::N);} - - float3 T; - float3 B; - float3 TdotV; - float3 BdotV; + using this_t = SAnisotropic; + using isotropic_interaction_type = IsotropicInteraction; + using ray_dir_info_type = typename isotropic_interaction_type::ray_dir_info_type; + using scalar_type = typename ray_dir_info_type::scalar_type; + using vector3_type = typename ray_dir_info_type::vector3_type; + using matrix3x3_type = matrix; + + // WARNING: Changed since GLSL, now arguments need to be normalized! + static this_t create( + NBL_CONST_REF_ARG(isotropic_interaction_type) isotropic, + NBL_CONST_REF_ARG(vector3_type) normalizedT, + NBL_CONST_REF_ARG(vector3_type) normalizedB + ) + { + this_t retval; + retval.isotropic = isotropic; + + retval.T = normalizedT; + retval.B = normalizedB; + + retval.TdotV = nbl::hlsl::dot(retval.isotropic.getV().getDirection(), retval.T); + retval.BdotV = nbl::hlsl::dot(retval.isotropic.getV().getDirection(), retval.B); + + return retval; + } + static this_t create(NBL_CONST_REF_ARG(isotropic_interaction_type) isotropic, NBL_CONST_REF_ARG(vector3_type) normalizedT) + { + return create(isotropic, normalizedT, cross(isotropic.getN(), normalizedT)); + } + static this_t create(NBL_CONST_REF_ARG(isotropic_interaction_type) isotropic) + { + vector3_type T, B; + math::frisvad(isotropic.getN(), T, B); + return create(isotropic, nbl::hlsl::normalize(T), nbl::hlsl::normalize(B)); + } + + static this_t create(NBL_CONST_REF_ARG(ray_dir_info_type) normalizedV, NBL_CONST_REF_ARG(vector3_type) normalizedN) + { + isotropic_interaction_type isotropic = isotropic_interaction_type::create(normalizedV, normalizedN); + return create(isotropic); + } + + ray_dir_info_type getV() NBL_CONST_MEMBER_FUNC { return isotropic.getV(); } + vector3_type getN() NBL_CONST_MEMBER_FUNC { return isotropic.getN(); } + scalar_type getNdotV() NBL_CONST_MEMBER_FUNC { return isotropic.getNdotV(); } + scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return isotropic.getNdotV2(); } + + vector3_type getT() NBL_CONST_MEMBER_FUNC { return T; } + vector3_type getB() NBL_CONST_MEMBER_FUNC { return B; } + scalar_type getTdotV() NBL_CONST_MEMBER_FUNC { return TdotV; } + scalar_type getBdotV() NBL_CONST_MEMBER_FUNC { return BdotV; } + + vector3_type getTangentSpaceV() NBL_CONST_MEMBER_FUNC { return vector3_type(TdotV, BdotV, isotropic.getNdotV()); } + matrix3x3_type getToTangentSpace() NBL_CONST_MEMBER_FUNC { return matrix3x3_type(T, B, isotropic.getN()); } + matrix3x3_type getFromTangentSpace() NBL_CONST_MEMBER_FUNC { return nbl::hlsl::transpose(matrix3x3_type(T, B, isotropic.getN())); } + + isotropic_interaction_type isotropic; + vector3_type T; + vector3_type B; + scalar_type TdotV; + scalar_type BdotV; }; } -template -struct LightSample +#define NBL_CONCEPT_NAME LightSample +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (_sample, T) +#define NBL_CONCEPT_PARAM_1 (inter, surface_interactions::SIsotropic) +#define NBL_CONCEPT_PARAM_2 (rdirinfo, typename T::ray_dir_info_type) +#define NBL_CONCEPT_PARAM_3 (pV, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_4 (frame, typename T::matrix3x3_type) +#define NBL_CONCEPT_PARAM_5 (pVdotL, typename T::scalar_type) +NBL_CONCEPT_BEGIN(6) +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define inter NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define rdirinfo NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define pV NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define frame NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define pVdotL NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::matrix3x3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getL()), ::nbl::hlsl::is_same_v, typename T::ray_dir_info_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getVdotL()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getTdotL()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getBdotL()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getNdotL()), ::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)((T::createFromTangentSpace(pV,rdirinfo,frame)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pVdotL,pV)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pVdotL,pV,pV,pV)), ::nbl::hlsl::is_same_v, T)) + // ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create >(pV,inter)), ::nbl::hlsl::is_same_v, T)) // NOTE: temporarily commented out due to dxc bug https://github.com/microsoft/DirectXShaderCompiler/issues/7154 + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getTangentSpaceL()), ::nbl::hlsl::is_same_v, typename T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ray_dir_info::Basic, typename T::ray_dir_info_type)) +); +#undef pVdotL +#undef frame +#undef pV +#undef rdirinfo +#undef inter +#undef _sample +#include + +template) +struct SLightSample { - static LightSample createTangentSpace( - const float3 tangentSpaceV, - const RayDirInfo tangentSpaceL, - const float3x3 tangentFrame // WARNING: its the transpose of the old GLSL function return value! - ) - { - LightSample retval; - - retval.L = RayDirInfo::transform(tangentSpaceL,tangentFrame); - retval.VdotL = dot(tangentSpaceV,tangentSpaceL); + using this_t = SLightSample; + using ray_dir_info_type = RayDirInfo; + using scalar_type = typename ray_dir_info_type::scalar_type; + using vector3_type = typename ray_dir_info_type::vector3_type; + using matrix3x3_type = matrix; + + static this_t createFromTangentSpace( + NBL_CONST_REF_ARG(vector3_type) tangentSpaceV, + NBL_CONST_REF_ARG(ray_dir_info_type) tangentSpaceL, + NBL_CONST_REF_ARG(matrix3x3_type) tangentFrame + ) + { + this_t retval; - retval.TdotL = tangentSpaceL.x; - retval.BdotL = tangentSpaceL.y; - retval.NdotL = tangentSpaceL.z; - retval.NdotL2 = retval.NdotL*retval.NdotL; - - return retval; - } - static LightSample create(const RayDirInfo L, const float VdotL, const float3 N) - { - LightSample retval; - - retval.L = L; - retval.VdotL = VdotL; + const vector3_type tsL = tangentSpaceL.getDirection(); + retval.L = tangentSpaceL.transform(tangentFrame); + retval.VdotL = nbl::hlsl::dot(tangentSpaceV, tsL); - retval.TdotL = nbl::hlsl::numeric_limits::nan(); - retval.BdotL = nbl::hlsl::numeric_limits::nan(); - retval.NdotL = dot(N,L); - retval.NdotL2 = retval.NdotL*retval.NdotL; - - return retval; - } - static LightSample create(const RayDirInfo L, const float VdotL, const float3 T, const float3 B, const float3 N) - { - LightSample retval = create(L,VdotL,N); - - retval.TdotL = dot(T,L); - retval.BdotL = dot(B,L); + retval.TdotL = tsL.x; + retval.BdotL = tsL.y; + retval.NdotL = tsL.z; + retval.NdotL2 = retval.NdotL*retval.NdotL; + + return retval; + } + static this_t create(NBL_CONST_REF_ARG(ray_dir_info_type) L, const scalar_type VdotL, NBL_CONST_REF_ARG(vector3_type) N) + { + this_t retval; + + retval.L = L; + retval.VdotL = VdotL; + + retval.TdotL = nbl::hlsl::numeric_limits::quiet_NaN; + retval.BdotL = nbl::hlsl::numeric_limits::quiet_NaN; + retval.NdotL = nbl::hlsl::dot(N,L.getDirection()); + retval.NdotL2 = retval.NdotL * retval.NdotL; + + return retval; + } + static this_t create(NBL_CONST_REF_ARG(ray_dir_info_type) L, const scalar_type VdotL, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N) + { + this_t retval = create(L,VdotL,N); + + retval.TdotL = nbl::hlsl::dot(T,L.getDirection()); + retval.BdotL = nbl::hlsl::dot(B,L.getDirection()); + + return retval; + } + + template) + static this_t create(NBL_CONST_REF_ARG(vector3_type) L, NBL_CONST_REF_ARG(SurfaceInteraction) interaction) + { + const vector3_type V = interaction.V.getDirection(); + const scalar_type VdotL = nbl::hlsl::dot(V,L); + this_t retval; + if (surface_interactions::Anisotropic) // TODO use NBL_IF_CONSTEXPR when pr #860 is merged + retval = create(L,VdotL,interaction.T,interaction.B,interaction.N); + else + retval = create(L, VdotL, interaction.N); + return retval; + } - return retval; - } - // overloads for surface_interactions - template - static LightSample create(const float3 L, const surface_interactions::Isotropic interaction) - { - const float3 V = interaction.V.getDirection(); - const float VdotL = dot(V,L); - return create(L,VdotL,interaction.N); - } - template - static LightSample create(const float3 L, const surface_interactions::Anisotropic interaction) - { - const float3 V = interaction.V.getDirection(); - const float VdotL = dot(V,L); - return create(L,VdotL,interaction.T,interaction.B,interaction.N); - } - // - float3 getTangentSpaceL() - { - return float3(TdotL,BdotL,NdotL); - } - - RayDirInfo L; - float VdotL; - - float TdotL; - float BdotL; - float NdotL; - float NdotL2; + vector3_type getTangentSpaceL() NBL_CONST_MEMBER_FUNC + { + return vector3_type(TdotL, BdotL, NdotL); + } + + ray_dir_info_type getL() NBL_CONST_MEMBER_FUNC { return L; } + scalar_type getVdotL() NBL_CONST_MEMBER_FUNC { return VdotL; } + scalar_type getTdotL() NBL_CONST_MEMBER_FUNC { return TdotL; } + scalar_type getBdotL() NBL_CONST_MEMBER_FUNC { return BdotL; } + scalar_type getNdotL() NBL_CONST_MEMBER_FUNC { return NdotL; } + scalar_type getNdotL2() NBL_CONST_MEMBER_FUNC { return NdotL2; } + + + RayDirInfo L; + scalar_type VdotL; + + scalar_type TdotL; + scalar_type BdotL; + scalar_type NdotL; + scalar_type NdotL2; }; -// -struct IsotropicMicrofacetCache +#define NBL_CONCEPT_NAME ReadableIsotropicMicrofacetCache +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (cache, T) +#define NBL_CONCEPT_PARAM_1 (pNdotV, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_2 (b0, bool) +NBL_CONCEPT_BEGIN(3) +#define cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define pNdotV NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define b0 NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getVdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getLdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getNdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getNdotH2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::isValidMicrofacetCache(b0,pNdotV,pNdotV,pNdotV,pNdotV)), ::nbl::hlsl::is_same_v, bool)) +); +#undef b0 +#undef pNdotV +#undef cache +#include + +#define NBL_CONCEPT_NAME CreatableIsotropicMicrofacetCache +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (cache, T) +#define NBL_CONCEPT_PARAM_1 (iso, surface_interactions::SIsotropic >) +#define NBL_CONCEPT_PARAM_2 (pNdotV, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_3 (_sample, SLightSample >) +#define NBL_CONCEPT_PARAM_4 (V, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_5 (eta, fresnel::OrientedEtas >) +NBL_CONCEPT_BEGIN(6) +#define cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define pNdotV NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define V NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define eta NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(pNdotV,pNdotV,pNdotV,pNdotV)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(pNdotV,pNdotV,pNdotV)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection >,SLightSample > >(iso,_sample)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,V,eta,V)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create >,SLightSample > >(iso,_sample,eta,V)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(ReadableIsotropicMicrofacetCache, T)) +); +#undef eta +#undef V +#undef _sample +#undef pNdotV +#undef iso +#undef cache +#include + +template ) +struct SIsotropicMicrofacetCache { - // always valid because its specialized for the reflective case - static IsotropicMicrofacetCache createForReflection(const float NdotV, const float NdotL, const float VdotL, out float LplusV_rcpLen) - { - LplusV_rcpLen = inversesqrt(2.0+2.0*VdotL); + using this_t = SIsotropicMicrofacetCache; + using scalar_type = T; + using vector3_type = vector; + using matrix3x3_type = matrix; + using monochrome_type = vector; + + // always valid because its specialized for the reflective case + static this_t createForReflection(const scalar_type NdotV, const scalar_type NdotL, const scalar_type VdotL, NBL_REF_ARG(scalar_type) LplusV_rcpLen) + { + LplusV_rcpLen = rsqrt(2.0 + 2.0 * VdotL); - IsotropicMicrofacetCache retval; - - retval.VdotH = LplusV_rcpLen*VdotL+LplusV_rcpLen; - retval.LdotH = retval.VdotH; - retval.NdotH = (NdotL+NdotV)*LplusV_rcpLen; - retval.NdotH2 = retval.NdotH*retval.NdotH; - - return retval; - } - static IsotropicMicrofacetCache createForReflection(const float NdotV, const float NdotL, const float VdotL) - { - float dummy; - return createForReflection(NdotV,NdotL,VdotL,dummy); - } - template - static IsotropicMicrofacetCache createForReflection( - const surface_interactions::Isotropic interaction, - const LightSample _sample) - { - return createForReflection(interaction.NdotV,_sample.NdotL,_sample.VdotL); - } - // transmissive cases need to be checked if the path is valid before usage - static bool compute( - out IsotropicMicrofacetCache retval, - const bool transmitted, const float3 V, const float3 L, - const float3 N, const float NdotL, const float VdotL, - const float orientedEta, const float rcpOrientedEta, out float3 H - ) - { - // TODO: can we optimize? - H = computeMicrofacetNormal(transmitted,V,L,orientedEta); - retval.NdotH = dot(N,H); - - // not coming from the medium (reflected) OR - // exiting at the macro scale AND ( (not L outside the cone of possible directions given IoR with constraint VdotH*LdotH<0.0) OR (microfacet not facing toward the macrosurface, i.e. non heightfield profile of microsurface) ) - const bool valid = !transmitted || (VdotL<=-min(orientedEta,rcpOrientedEta) && _cache.NdotH>nbl::hlsl::numeric_limits::min()); - if (valid) - { - // TODO: can we optimize? - retval.VdotH = dot(V,H); - retval.LdotH = dot(L,H); - retval.NdotH2 = retval.NdotH*retval.NdotH; - return true; - } - return false; - } - template - static bool compute( - out IsotropicMicrofacetCache retval, - const surface_interactions::Isotropic interaction, - const LightSample _sample, - const float eta, out float3 H - ) - { - const float NdotV = interaction.NdotV; - const float NdotL = _sample.NdotL; - const bool transmitted = nbl_glsl_isTransmissionPath(NdotV,NdotL); - - float orientedEta, rcpOrientedEta; - const bool backside = nbl_glsl_getOrientedEtas(orientedEta,rcpOrientedEta,NdotV,eta); - - const vec3 V = interaction.V.getDirection(); - const vec3 L = _sample.L; - const float VdotL = dot(V,L); - return nbl_glsl_calcIsotropicMicrofacetCache(_cache,transmitted,V,L,interaction.N,NdotL,VdotL,orientedEta,rcpOrientedEta,H); - } - template - static bool compute( - out IsotropicMicrofacetCache retval, - const surface_interactions::Isotropic interaction, - const LightSample _sample, - const float eta - ) - { - float3 dummy; - return nbl_glsl_calcIsotropicMicrofacetCache(_cache,transmitted,V,L,interaction.N,NdotL,VdotL,orientedEta,rcpOrientedEta,dummy); - } - - bool isValidVNDFMicrofacet(const bool is_bsdf, const bool transmission, const float VdotL, const float eta, const float rcp_eta) - { - return NdotH >= 0.0 && !(is_bsdf && transmission && (VdotL > -min(eta,rcp_eta))); - } - - float VdotH; - float LdotH; - float NdotH; - float NdotH2; + this_t retval; + + retval.VdotH = LplusV_rcpLen * VdotL + LplusV_rcpLen; + retval.LdotH = retval.VdotH; + retval.NdotH = (NdotL + NdotV) * LplusV_rcpLen; + retval.NdotH2 = retval.NdotH * retval.NdotH; + + return retval; + } + static this_t createForReflection(const scalar_type NdotV, const scalar_type NdotL, const scalar_type VdotL) + { + float dummy; + return createForReflection(NdotV, NdotL, VdotL, dummy); + } + template && LightSample) + static this_t createForReflection( + NBL_CONST_REF_ARG(IsotropicInteraction) interaction, + NBL_CONST_REF_ARG(LS) _sample) + { + return createForReflection(interaction.getNdotV(), _sample.getNdotL(), _sample.getVdotL()); + } + + // transmissive cases need to be checked if the path is valid before usage + static this_t create(const bool transmitted, NBL_CONST_REF_ARG(ComputeMicrofacetNormal) computeMicrofacetNormal, const scalar_type VdotL, + NBL_CONST_REF_ARG(vector3_type) N, NBL_REF_ARG(vector3_type) H) + { + this_t retval; + H = computeMicrofacetNormal.normalized(transmitted); + retval.NdotH = hlsl::dot(N, H); + + // not coming from the medium (reflected) OR + // exiting at the macro scale AND ( (not L outside the cone of possible directions given IoR with constraint VdotH*LdotH<0.0) OR (microfacet not facing toward the macrosurface, i.e. non heightfield profile of microsurface) ) + const bool valid = isValidMicrofacetCache(transmitted, VdotL, retval.NdotH, computeMicrofacetNormal.orientedEta.value, computeMicrofacetNormal.orientedEta.rcp); + if (valid) + { + retval.VdotH = hlsl::dot(computeMicrofacetNormal.V,H); + retval.LdotH = hlsl::dot(computeMicrofacetNormal.L,H); + retval.NdotH2 = retval.NdotH * retval.NdotH; + } + else + retval.NdotH = nbl::hlsl::numeric_limits::quiet_NaN; + return retval; + } + + static this_t create( + NBL_CONST_REF_ARG(vector3_type) V, NBL_CONST_REF_ARG(vector3_type) L, NBL_CONST_REF_ARG(vector3_type) N, + NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEtas, NBL_REF_ARG(vector3_type) H) + { + this_t retval; + const scalar_type NdotV = hlsl::dot(N, V); + const scalar_type NdotL = hlsl::dot(N, L); + const scalar_type VdotL = hlsl::dot(V, L); + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(NdotV,NdotL); + + ComputeMicrofacetNormal computeMicrofacetNormal = ComputeMicrofacetNormal::create(V,L,N,1.0); + computeMicrofacetNormal.orientedEta = orientedEtas; + + return create(transmitted, computeMicrofacetNormal, VdotL, N, H); + } + + static this_t create( + NBL_CONST_REF_ARG(vector3_type) V, NBL_CONST_REF_ARG(vector3_type) L, NBL_CONST_REF_ARG(vector3_type) N, + NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEtas) + { + vector3_type dummy; + return create(V, L, N, orientedEtas, dummy); + } + + template && LightSample) + static this_t create( + NBL_CONST_REF_ARG(IsotropicInteraction) interaction, + NBL_CONST_REF_ARG(LS) _sample, + NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEtas, NBL_REF_ARG(vector3_type) H) + { + const vector3_type V = interaction.getV().getDirection(); + const vector3_type L = _sample.getL().getDirection(); + const vector3_type N = interaction.getN(); + + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(interaction.getNdotV(),_sample.getNdotL()); + + ComputeMicrofacetNormal computeMicrofacetNormal = ComputeMicrofacetNormal::create(V,L,N,1.0); + computeMicrofacetNormal.orientedEta = orientedEtas; + + return create(transmitted, computeMicrofacetNormal, _sample.getVdotL(), N, H); + } + + template && LightSample) + static this_t create( + NBL_CONST_REF_ARG(IsotropicInteraction) interaction, + NBL_CONST_REF_ARG(LS) _sample, + NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEtas) + { + vector3_type dummy; + return create(interaction,_sample,orientedEtas,dummy); + } + + static bool isValidMicrofacetCache(const bool transmitted, const scalar_type VdotL, const scalar_type NdotH, const scalar_type eta, const scalar_type rcp_eta) + { + return !transmitted || (VdotL <= -hlsl::min(eta, rcp_eta) && NdotH >= nbl::hlsl::numeric_limits::min); + } + + scalar_type getVdotH() NBL_CONST_MEMBER_FUNC + { + assert(VdotH >= scalar_type(0.0)); + return VdotH; + } + + scalar_type getLdotH() NBL_CONST_MEMBER_FUNC { return LdotH; } + scalar_type getNdotH() NBL_CONST_MEMBER_FUNC { return NdotH; } + scalar_type getNdotH2() NBL_CONST_MEMBER_FUNC { return NdotH2; } + + scalar_type VdotH; + scalar_type LdotH; + scalar_type NdotH; + scalar_type NdotH2; }; -struct AnisotropicMicrofacetCache : IsotropicMicrofacetCache + +#define NBL_CONCEPT_NAME AnisotropicMicrofacetCache +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (cache, T) +#define NBL_CONCEPT_PARAM_1 (aniso, surface_interactions::SAnisotropic > >) +#define NBL_CONCEPT_PARAM_2 (pNdotL, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_3 (_sample, SLightSample >) +#define NBL_CONCEPT_PARAM_4 (V, typename T::vector3_type) +#define NBL_CONCEPT_PARAM_5 (b0, bool) +#define NBL_CONCEPT_PARAM_6 (eta, fresnel::OrientedEtas >) +#define NBL_CONCEPT_PARAM_7 (rcp_eta, fresnel::OrientedEtaRcps >) +NBL_CONCEPT_BEGIN(8) +#define cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define pNdotL NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define V NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define b0 NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define eta NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +#define rcp_eta NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getTdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getBdotH()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(V,V)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,b0,rcp_eta)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createForReflection(V,V,pNdotL)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection > >,SLightSample > >(aniso,_sample)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,V,V,V,eta,V)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create > >,SLightSample > >(aniso,_sample,eta)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) +); +#undef rcp_eta +#undef eta +#undef b0 +#undef V +#undef _sample +#undef pNdotL +#undef aniso +#undef cache +#include + +template) +struct SAnisotropicMicrofacetCache { - // always valid by construction - static AnisotropicMicrofacetCache create(const float3 tangentSpaceV, const float3 tangentSpaceH) - { - AnisotropicMicrofacetCache retval; - - retval.VdotH = dot(tangentSpaceV,tangentSpaceH); - retval.LdotH = retval.VdotH; - retval.NdotH = tangentSpaceH.z; - retval.NdotH2 = retval.NdotH*retval.NdotH; - retval.TdotH = tangentSpaceH.x; - retval.BdotH = tangentSpaceH.y; - - return retval; - } - static AnisotropicMicrofacetCache create( - const float3 tangentSpaceV, - const float3 tangentSpaceH, - const bool transmitted, - const float rcpOrientedEta, - const float rcpOrientedEta2 - ) - { - AnisotropicMicrofacetCache retval = create(tangentSpaceV,tangentSpaceH); - if (transmitted) - { - const float VdotH = retval.VdotH; - LdotH = transmitted ? refract_compute_NdotT(VdotH<0.0,VdotH*VdotH,rcpOrientedEta2); + using this_t = SAnisotropicMicrofacetCache; + using isocache_type = IsoCache; + using scalar_type = typename IsoCache::scalar_type; + using vector3_type = vector; + using matrix3x3_type = matrix; + using monochrome_type = vector; + + // always valid by construction + static this_t createForReflection(NBL_CONST_REF_ARG(vector3_type) tangentSpaceV, NBL_CONST_REF_ARG(vector3_type) tangentSpaceH) + { + this_t retval; + + retval.iso_cache.VdotH = nbl::hlsl::dot(tangentSpaceV,tangentSpaceH); + retval.iso_cache.LdotH = retval.iso_cache.getVdotH(); + retval.iso_cache.NdotH = tangentSpaceH.z; + retval.iso_cache.NdotH2 = retval.iso_cache.getNdotH() * retval.iso_cache.getNdotH(); + retval.TdotH = tangentSpaceH.x; + retval.BdotH = tangentSpaceH.y; + + return retval; } - - return retval; - } - // always valid because its specialized for the reflective case - static AnisotropicMicrofacetCache createForReflection(const float3 tangentSpaceV, const float3 tangentSpaceL, const float VdotL) - { - AnisotropicMicrofacetCache retval; - - float LplusV_rcpLen; - retval = createForReflection(tangentSpaceV.z,tangentSpaceL.z,VdotL,LplusV_rcpLen); - retval.TdotH = (tangentSpaceV.x+tangentSpaceL.x)*LplusV_rcpLen; - retval.BdotH = (tangentSpaceV.y+tangentSpaceL.y)*LplusV_rcpLen; - - return retval; - } - template - static AnisotropicMicrofacetCache createForReflection( - const surface_interactions::Anisotropic interaction, - const LightSample _sample) - { - return createForReflection(interaction.getTangentSpaceV(),_sample.getTangentSpaceL(),_sample.VdotL); - } - // transmissive cases need to be checked if the path is valid before usage - static bool compute( - out AnisotropicMicrofacetCache retval, - const bool transmitted, const float3 V, const float3 L, - const float3 T, const float3 B, const float3 N, - const float NdotL, const float VdotL, - const float orientedEta, const float rcpOrientedEta, out float3 H - ) - { - float3 H; - const bool valid = IsotropicMicrofacetCache::compute(retval,transmitted,V,L,N,NdotL,VdotL,orientedEta,rcpOrientedEta,H); - if (valid) - { - retval.TdotH = dot(T,H); - retval.BdotH = dot(B,H); - } - return valid; - } - template - static bool compute( - out AnisotropicMicrofacetCache retval, - const surface_interactions::Anisotropic interaction, - const LightSample _sample, - const float eta - ) - { - float3 H; - const bool valid = IsotropicMicrofacetCache::compute(retval,interaction,_sample,eta,H); - if (valid) - { - retval.TdotH = dot(interaction.T,H); - retval.BdotH = dot(interaction.B,H); - } - return valid; - } - - float TdotH; - float BdotH; + static this_t create( + NBL_CONST_REF_ARG(vector3_type) tangentSpaceV, + NBL_CONST_REF_ARG(vector3_type) tangentSpaceH, + const bool transmitted, + NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpOrientedEta + ) + { + this_t retval = create(tangentSpaceV,tangentSpaceH); + if (transmitted) + { + const scalar_type VdotH = retval.iso_cache.VdotH; + retval.iso_cache.LdotH = refract_compute_NdotT(VdotH<0.0,VdotH*VdotH,rcpOrientedEta.value2); + } + + return retval; + } + // always valid because its specialized for the reflective case + static this_t createForReflection(NBL_CONST_REF_ARG(vector3_type) tangentSpaceV, NBL_CONST_REF_ARG(vector3_type) tangentSpaceL, const scalar_type VdotL) + { + this_t retval; + + scalar_type LplusV_rcpLen; + retval.iso_cache = isocache_type::createForReflection(tangentSpaceV.z, tangentSpaceL.z, VdotL, LplusV_rcpLen); + retval.TdotH = (tangentSpaceV.x + tangentSpaceL.x) * LplusV_rcpLen; + retval.BdotH = (tangentSpaceV.y + tangentSpaceL.y) * LplusV_rcpLen; + + return retval; + } + template && LightSample) + static this_t createForReflection( + NBL_CONST_REF_ARG(AnisotropicInteraction) interaction, + NBL_CONST_REF_ARG(LS) _sample) + { + return createForReflection(interaction.getTangentSpaceV(), _sample.getTangentSpaceL(), _sample.getVdotL()); + } + // transmissive cases need to be checked if the path is valid before usage + static this_t create( + NBL_CONST_REF_ARG(vector3_type) V, NBL_CONST_REF_ARG(vector3_type) L, + NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N, + NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEtas, NBL_REF_ARG(vector3_type) H + ) + { + this_t retval; + retval.iso_cache = isocache_type::create(V,L,N,orientedEtas,H); + const bool valid = retval.iso_cache.NdotH >= 0.0; + if (valid) + { + retval.TdotH = nbl::hlsl::dot(T,H); + retval.BdotH = nbl::hlsl::dot(B,H); + } + return retval; + } + template && LightSample) + static this_t create( + NBL_CONST_REF_ARG(AnisotropicInteraction) interaction, + NBL_CONST_REF_ARG(LS) _sample, + NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEtas + ) + { + this_t retval; + vector3_type H; + retval.iso_cache = isocache_type::template create(interaction.isotropic,_sample,orientedEtas,H); + const bool valid = retval.iso_cache.NdotH >= 0.0; + if (valid) + { + retval.TdotH = nbl::hlsl::dot(interaction.getT(),H); + retval.BdotH = nbl::hlsl::dot(interaction.getB(),H); + } + return retval; + } + + static bool isValidMicrofacetCache(const bool transmitted, const scalar_type VdotL, const scalar_type NdotH, const scalar_type eta, const scalar_type rcp_eta) + { + return isocache_type::isValidMicrofacetCache(transmitted, VdotL, NdotH, eta, rcp_eta); + } + + scalar_type getVdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.getVdotH(); } + scalar_type getLdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.getLdotH(); } + scalar_type getNdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.getNdotH(); } + scalar_type getNdotH2() NBL_CONST_MEMBER_FUNC { return iso_cache.getNdotH2(); } + + scalar_type getTdotH() NBL_CONST_MEMBER_FUNC { return TdotH; } + scalar_type getBdotH() NBL_CONST_MEMBER_FUNC { return BdotH; } + + isocache_type iso_cache; + scalar_type TdotH; + scalar_type BdotH; }; -// finally fixed the semantic F-up, value/pdf = quotient not remainder -template -struct quotient_and_pdf +#define NBL_CONCEPT_NAME BxDF +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (bxdf, T) +#define NBL_CONCEPT_PARAM_1 (spec, typename T::spectral_type) +#define NBL_CONCEPT_PARAM_2 (pdf, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_3 (_sample, typename T::sample_type) +#define NBL_CONCEPT_PARAM_4 (iso, typename T::isotropic_interaction_type) +#define NBL_CONCEPT_PARAM_5 (aniso, typename T::anisotropic_interaction_type) +#define NBL_CONCEPT_PARAM_6 (param_iso, typename T::params_isotropic_t) +#define NBL_CONCEPT_PARAM_7 (param_aniso, typename T::params_anisotropic_t) +#define NBL_CONCEPT_PARAM_8 (u, vector) +NBL_CONCEPT_BEGIN(9) +#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define spec NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define pdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define param_iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +#define param_aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 +#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_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)(T::params_isotropic_t)) + ((NBL_CONCEPT_REQ_TYPE)(T::params_anisotropic_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_iso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_aniso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_aniso)), ::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)(sampling::Spectral, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) +); +#undef u +#undef param_aniso +#undef param_iso +#undef aniso +#undef iso +#undef _sample +#undef pdf +#undef spec +#undef bxdf +#include + +#define NBL_CONCEPT_NAME MicrofacetBxDF +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (bxdf, T) +#define NBL_CONCEPT_PARAM_1 (spec, typename T::spectral_type) +#define NBL_CONCEPT_PARAM_2 (pdf, typename T::scalar_type) +#define NBL_CONCEPT_PARAM_3 (_sample, typename T::sample_type) +#define NBL_CONCEPT_PARAM_4 (iso, typename T::isotropic_interaction_type) +#define NBL_CONCEPT_PARAM_5 (aniso, typename T::anisotropic_interaction_type) +#define NBL_CONCEPT_PARAM_6 (isocache, typename T::isocache_type) +#define NBL_CONCEPT_PARAM_7 (anisocache, typename T::anisocache_type) +#define NBL_CONCEPT_PARAM_8 (param_iso, typename T::params_isotropic_t) +#define NBL_CONCEPT_PARAM_9 (param_aniso, typename T::params_anisotropic_t) +#define NBL_CONCEPT_PARAM_10 (u, vector) +NBL_CONCEPT_BEGIN(11) +#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define spec NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define pdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +#define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 +#define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 +#define param_iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8 +#define param_aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_9 +#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_10 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_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)(T::isocache_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::params_isotropic_t)) + ((NBL_CONCEPT_REQ_TYPE)(T::params_anisotropic_t)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_iso)), ::nbl::hlsl::is_same_v, T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_aniso)), ::nbl::hlsl::is_same_v, T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u,isocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u,anisocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) + //((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.template pdf(_sample,iso)), ::nbl::hlsl::is_scalar_v)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_aniso)), ::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)(sampling::Spectral, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) +); +#undef u +#undef param_aniso +#undef param_iso +#undef anisocache +#undef isocache +#undef aniso +#undef iso +#undef _sample +#undef pdf +#undef spec +#undef bxdf +#include + +enum BxDFClampMode : uint16_t { - quotient_and_pdf create(const SpectralBins _quotient, const float _pdf) - { - quotient_and_pdf retval; - retval.quotient = _quotient; - retval.pdf = _pdf; - return retval; - } - - SpectralBins value() - { - return quotient*pdf; - } - - SpectralBins quotient; - float pdf; + BCM_NONE = 0, + BCM_MAX, + BCM_ABS }; - } } } diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index d3b3543a28..25ad3ca705 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -4,10 +4,11 @@ #ifndef _NBL_BUILTIN_HLSL_BXDF_FRESNEL_INCLUDED_ #define _NBL_BUILTIN_HLSL_BXDF_FRESNEL_INCLUDED_ +#include "nbl/builtin/hlsl/ieee754.hlsl" #include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/concepts.hlsl" #include "nbl/builtin/hlsl/numbers.hlsl" #include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" -#include "nbl/builtin/hlsl/spirv_intrinsics/core.hlsl" namespace nbl { @@ -17,131 +18,338 @@ namespace hlsl namespace bxdf { -namespace impl +namespace fresnel { -template -struct orientedEtas; -template<> -struct orientedEtas +template) +struct OrientedEtas { - static bool __call(NBL_REF_ARG(float) orientedEta, NBL_REF_ARG(float) rcpOrientedEta, float NdotI, float eta) + using scalar_type = typename vector_traits::scalar_type; + + static OrientedEtas create(scalar_type NdotI, T eta) { - const bool backside = NdotI < 0.0; - const float rcpEta = 1.0 / eta; - orientedEta = backside ? rcpEta : eta; - rcpOrientedEta = backside ? eta : rcpEta; - return backside; + OrientedEtas retval; + const bool backside = NdotI < scalar_type(0.0); + const T rcpEta = hlsl::promote(1.0) / eta; + retval.value = backside ? rcpEta : eta; + retval.rcp = backside ? eta : rcpEta; + return retval; } + + T value; + T rcp; }; -template<> -struct orientedEtas +template) +struct OrientedEtaRcps { - static bool __call(NBL_REF_ARG(float32_t3) orientedEta, NBL_REF_ARG(float32_t3) rcpOrientedEta, float NdotI, float32_t3 eta) + using scalar_type = typename vector_traits::scalar_type; + + static OrientedEtaRcps create(scalar_type NdotI, T eta) { - const bool backside = NdotI < 0.0; - const float32_t3 rcpEta = (float32_t3)1.0 / eta; - orientedEta = backside ? rcpEta:eta; - rcpOrientedEta = backside ? eta:rcpEta; - return backside; + OrientedEtaRcps retval; + const bool backside = NdotI < scalar_type(0.0); + const T rcpEta = hlsl::promote(1.0) / eta; + retval.value = backside ? eta : rcpEta; + retval.value2 = retval.value * retval.value; + return retval; } -}; -} -template || is_vector_v) -bool getOrientedEtas(NBL_REF_ARG(T) orientedEta, NBL_REF_ARG(T) rcpOrientedEta, scalar_type_t NdotI, T eta) -{ - return impl::orientedEtas::__call(orientedEta, rcpOrientedEta, NdotI, eta); -} + T diffuseFresnelCorrectionFactor() + { + // assert(n*n==n2); + const T n = hlsl::promote(1.0) / value; + const T n2 = hlsl::promote(1.0) / value2; + vector::Dimension> TIR = n < hlsl::promote(1.0); + T invdenum = nbl::hlsl::mix(hlsl::promote(1.0), hlsl::promote(1.0) / (n2 * n2 * (hlsl::promote(554.33) - hlsl::promote(380.7) * n)), TIR); + T num = n * nbl::hlsl::mix(hlsl::promote(0.1921156102251088), n * hlsl::promote(298.25) - hlsl::promote(261.38) * n2 + hlsl::promote(138.43), TIR); + num += nbl::hlsl::mix(hlsl::promote(0.8078843897748912), hlsl::promote(-1.67), TIR); + return num * invdenum; + } + + T value; + T value2; +}; -template ) -T reflect(T I, T N, typename vector_traits::scalar_type NdotI) -{ - return N * 2.0f * NdotI - I; } -template::Dimension == 3) -struct refract +template) +struct Reflect { - using this_t = refract; - using vector_type = T; - using scalar_type = typename vector_traits::scalar_type; + using this_t = Reflect; + using vector_type = vector; + using scalar_type = T; - static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, bool backside, scalar_type NdotI, scalar_type NdotI2, scalar_type rcpOrientedEta, scalar_type rcpOrientedEta2) + static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, scalar_type NdotI) { this_t retval; retval.I = I; retval.N = N; - retval.backside = backside; retval.NdotI = NdotI; - retval.NdotI2 = NdotI2; - retval.rcpOrientedEta = rcpOrientedEta; - retval.rcpOrientedEta2 = rcpOrientedEta2; return retval; } - static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, scalar_type NdotI, scalar_type eta) + static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N) { this_t retval; retval.I = I; retval.N = N; - scalar_type orientedEta; - retval.backside = getOrientedEtas(orientedEta, retval.rcpOrientedEta, NdotI, eta); - retval.NdotI = NdotI; - retval.NdotI2 = NdotI * NdotI; - retval.rcpOrientedEta2 = retval.rcpOrientedEta * retval.rcpOrientedEta; + retval.recomputeNdotI(); return retval; } - static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, scalar_type eta) + void recomputeNdotI() + { + NdotI = hlsl::dot(N, I); + } + + vector_type operator()() + { + return N * 2.0f * NdotI - I; + } + + vector_type I; + vector_type N; + scalar_type NdotI; +}; + +template) +struct Refract +{ + using this_t = Refract; + using vector_type = vector; + using monochrome_type = vector; + using scalar_type = T; + + static this_t create(NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEtas, NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, const bool backside) { this_t retval; retval.I = I; retval.N = N; - retval.NdotI = dot(N, I); - scalar_type orientedEta; - retval.backside = getOrientedEtas(orientedEta, retval.rcpOrientedEta, retval.NdotI, eta); - retval.NdotI2 = retval.NdotI * retval.NdotI; - retval.rcpOrientedEta2 = retval.rcpOrientedEta * retval.rcpOrientedEta; + retval.recomputeNdotI(); + retval.recomputeNdotT(backside, retval.NdotI2, rcpEtas.value2[0]); return retval; } - static scalar_type computeNdotT(bool backside, scalar_type NdotI2, scalar_type rcpOrientedEta2) + void recomputeNdotI() { - scalar_type NdotT2 = rcpOrientedEta2 * NdotI2 + 1.0 - rcpOrientedEta2; + NdotI = hlsl::dot(N, I); + NdotI2 = NdotI * NdotI; + } + + void recomputeNdotT(bool backside, scalar_type _NdotI2, scalar_type rcpOrientedEta2) + { + scalar_type NdotT2 = rcpOrientedEta2 * _NdotI2 + 1.0 - rcpOrientedEta2; scalar_type absNdotT = sqrt(NdotT2); - return backside ? absNdotT : -(absNdotT); + NdotT = ieee754::flipSign(absNdotT, backside); + } + + vector_type operator()(const bool backside, scalar_type rcpOrientedEta) + { + recomputeNdotT(rcpOrientedEta.backside, NdotI2, rcpOrientedEta*rcpOrientedEta); + return N * (NdotI * rcpOrientedEta + NdotT) - rcpOrientedEta * I; + } + + vector_type I; + vector_type N; + scalar_type NdotT; + scalar_type NdotI; + scalar_type NdotI2; +}; + +template) +struct ReflectRefract +{ + using this_t = ReflectRefract; + using vector_type = vector; + using scalar_type = T; + + static this_t create(bool r, NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, scalar_type NdotI, scalar_type NdotTorR, scalar_type rcpOrientedEta) + { + this_t retval; + retval.I = I; + retval.N = N; + retval.NdotI = NdotI; + retval.NdotTorR = NdotTorR; + retval.rcpOrientedEta = rcpOrientedEta; + return retval; + } + + static this_t create(bool r, NBL_CONST_REF_ARG(Refract) refract, scalar_type rcpOrientedEta) + { + this_t retval; + retval.I = refract.I; + retval.N = refract.N; + retval.NdotI = refract.NdotI; + retval.NdotTorR = hlsl::mix(refract.NdotI, refract.NdotT, r); + retval.rcpOrientedEta = rcpOrientedEta; + return retval; } - vector_type doRefract() + // when you know you'll reflect + void recomputeNdotR() { - return N * (NdotI * rcpOrientedEta + computeNdotT(backside, NdotI2, rcpOrientedEta2)) - rcpOrientedEta * I; + refract.recomputeNdotI(); } - static vector_type doReflectRefract(bool _refract, NBL_CONST_REF_ARG(vector_type) _I, NBL_CONST_REF_ARG(vector_type) _N, scalar_type _NdotI, scalar_type _NdotTorR, scalar_type _rcpOrientedEta) + // when you know you'll refract + void recomputeNdotT(bool backside, scalar_type _NdotI2, scalar_type rcpOrientedEta2) { - return _N * (_NdotI * (_refract ? _rcpOrientedEta : 1.0f) + _NdotTorR) - _I * (_refract ? _rcpOrientedEta : 1.0f); + refract.recomputeNdotT(backside, _NdotI2, rcpOrientedEta2); } - vector_type doReflectRefract(bool r) + vector_type operator()(const bool doRefract) { - const T NdotTorR = r ? computeNdotT(backside, NdotI2, rcpOrientedEta2) : NdotI; - return doReflectRefract(r, I, N, NdotI, NdotTorR, rcpOrientedEta); + return N * (NdotI * (hlsl::mix(1.0f, rcpOrientedEta, doRefract)) + NdotTorR) - I * (hlsl::mix(1.0f, rcpOrientedEta, doRefract)); } + Refract refract; vector_type I; vector_type N; - bool backside; scalar_type NdotI; - scalar_type NdotI2; + scalar_type NdotTorR; scalar_type rcpOrientedEta; - scalar_type rcpOrientedEta2; }; + +namespace fresnel +{ + +template || concepts::FloatingPointLikeVectorial) +struct Schlick +{ + using scalar_type = typename vector_traits::scalar_type; + + static Schlick create(NBL_CONST_REF_ARG(T) F0, scalar_type clampedCosTheta) + { + Schlick retval; + retval.F0 = F0; + retval.clampedCosTheta = clampedCosTheta; + return retval; + } + + T operator()() + { + assert(clampedCosTheta > scalar_type(0.0)); + assert(hlsl::promote(0.02) < F0 && F0 <= hlsl::promote(1.0)); + T x = 1.0 - clampedCosTheta; + return F0 + (1.0 - F0) * x*x*x*x*x; + } + + T F0; + scalar_type clampedCosTheta; +}; + +template || concepts::FloatingPointLikeVectorial) +struct Conductor +{ + using scalar_type = typename vector_traits::scalar_type; + + static Conductor create(NBL_CONST_REF_ARG(T) eta, NBL_CONST_REF_ARG(T) etak, scalar_type clampedCosTheta) + { + Conductor retval; + retval.eta = eta; + retval.etak2 = etak*etak; + retval.clampedCosTheta = clampedCosTheta; + return retval; + } + + T operator()() + { + const scalar_type cosTheta2 = clampedCosTheta * clampedCosTheta; + //const float sinTheta2 = 1.0 - cosTheta2; + + const T etaLen2 = eta * eta + etak2; + assert(hlsl::any(etaLen2 > hlsl::promote(hlsl::exp2(-numeric_limits::digits)))); + const T etaCosTwice = eta * clampedCosTheta * 2.0f; + + const T rs_common = etaLen2 + (T)(cosTheta2); + const T rs2 = (rs_common - etaCosTwice) / (rs_common + etaCosTwice); + + const T rp_common = etaLen2 * cosTheta2 + (T)(1.0); + const T rp2 = (rp_common - etaCosTwice) / (rp_common + etaCosTwice); + + return (rs2 + rp2) * 0.5f; + } + + T eta; + T etak2; + scalar_type clampedCosTheta; +}; + +template || concepts::FloatingPointLikeVectorial) +struct Dielectric +{ + using scalar_type = typename vector_traits::scalar_type; + + static Dielectric create(NBL_CONST_REF_ARG(T) eta, scalar_type cosTheta) + { + Dielectric retval; + scalar_type absCosTheta = hlsl::abs(cosTheta); + OrientedEtas orientedEta = OrientedEtas::create(absCosTheta, eta); + retval.orientedEta2 = orientedEta.value * orientedEta.value; + retval.absCosTheta = absCosTheta; + return retval; + } + + static T __call(NBL_CONST_REF_ARG(T) orientedEta2, scalar_type absCosTheta) + { + const scalar_type sinTheta2 = 1.0 - absCosTheta * absCosTheta; + + // the max() clamping can handle TIR when orientedEta2<1.0 + const T t0 = hlsl::sqrt(hlsl::max(orientedEta2 - sinTheta2, hlsl::promote(0.0))); + const T rs = (hlsl::promote(absCosTheta) - t0) / (hlsl::promote(absCosTheta) + t0); + + const T t2 = orientedEta2 * absCosTheta; + const T rp = (t0 - t2) / (t0 + t2); + + return (rs * rs + rp * rp) * 0.5f; + } + + T operator()() + { + return __call(orientedEta2, absCosTheta); + } + + T orientedEta2; + scalar_type absCosTheta; +}; + +template || concepts::FloatingPointLikeVectorial) +struct DielectricFrontFaceOnly +{ + using scalar_type = typename vector_traits::scalar_type; + + static DielectricFrontFaceOnly create(NBL_CONST_REF_ARG(T) orientedEta2, scalar_type absCosTheta) + { + Dielectric retval; + retval.orientedEta2 = orientedEta2; + retval.absCosTheta = hlsl::abs(absCosTheta); + return retval; + } + + T operator()() + { + return Dielectric::__call(orientedEta2, absCosTheta); + } + + T orientedEta2; + scalar_type absCosTheta; +}; + + +// gets the sum of all R, T R T, T R^3 T, T R^5 T, ... paths +template) +T thinDielectricInfiniteScatter(const T singleInterfaceReflectance) +{ + const T doubleInterfaceReflectance = singleInterfaceReflectance * singleInterfaceReflectance; + return hlsl::mix(hlsl::promote(1.0), (singleInterfaceReflectance - doubleInterfaceReflectance) / (hlsl::promote(1.0) - doubleInterfaceReflectance) * 2.0f, doubleInterfaceReflectance > hlsl::promote(0.9999)); +} + } +} } } diff --git a/include/nbl/builtin/hlsl/bxdf/geom_smith.hlsl b/include/nbl/builtin/hlsl/bxdf/geom_smith.hlsl new file mode 100644 index 0000000000..b578ec40b9 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/geom_smith.hlsl @@ -0,0 +1,398 @@ +// Copyright (C) 2018-2023 - 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_GEOM_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_GEOM_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/ndf.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace smith +{ + +namespace brdf +{ + +template +struct VNDF_pdf; + +template +struct VNDF_pdf > +{ + using scalar_type = T; + using ndf_type = ndf::Beckmann; + using this_t = VNDF_pdf; + + static this_t create(scalar_type ndf, scalar_type maxNdotV) + { + this_t retval; + retval.ndf = ndf; + retval.maxNdotV = maxNdotV; + return retval; + } + + scalar_type operator()(scalar_type lambda_V) + { + onePlusLambda_V = 1.0 + lambda_V; + ndf::microfacet_to_light_measure_transform transform = ndf::microfacet_to_light_measure_transform::create(ndf / onePlusLambda_V, maxNdotV); + return transform(); + } + + scalar_type ndf; + scalar_type maxNdotV; + scalar_type onePlusLambda_V; +}; + +template +struct VNDF_pdf > +{ + using scalar_type = T; + using ndf_type = ndf::GGX; + using this_t = VNDF_pdf; + + static this_t create(scalar_type ndf, scalar_type maxNdotV) + { + this_t retval; + retval.ndf = ndf; + retval.maxNdotV = maxNdotV; + return retval; + } + + scalar_type operator()(scalar_type G1_over_2NdotV) + { + return ndf * 0.5 * G1_over_2NdotV; + } + + scalar_type ndf; + scalar_type maxNdotV; + scalar_type onePlusLambda_V; +}; + +} + +namespace bsdf +{ + +template) +struct FVNDF_pdf +{ + static FVNDF_pdf create(T fresnel_ndf, T absNdotV) + { + FVNDF_pdf retval; + retval.fresnel_ndf = fresnel_ndf; + retval.absNdotV = absNdotV; + return retval; + } + + T operator()(T G1_over_2NdotV, bool transmitted, T VdotH, T LdotH, T VdotHLdotH, T orientedEta) + { + T FNG = fresnel_ndf * G1_over_2NdotV; + T factor = 0.5; + if (transmitted) + { + const T VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so this factor is negative + factor *= -2.0 * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH); + } + return FNG * factor; + } + + T fresnel_ndf; + T absNdotV; +}; + +template +struct VNDF_pdf; + +template +struct VNDF_pdf > +{ + using scalar_type = T; + using ndf_type = ndf::Beckmann; + using this_t = VNDF_pdf; + + static this_t create(scalar_type ndf, scalar_type absNdotV) + { + this_t retval; + retval.ndf = ndf; + retval.absNdotV = absNdotV; + return retval; + } + + scalar_type operator()(scalar_type lambda_V, bool transmitted, scalar_type VdotH, scalar_type LdotH, scalar_type VdotHLdotH, scalar_type orientedEta, scalar_type reflectance) + { + onePlusLambda_V = 1.0 + lambda_V; + ndf::microfacet_to_light_measure_transform transform + = ndf::microfacet_to_light_measure_transform::create(hlsl::mix(reflectance, scalar_type(1.0) - reflectance, transmitted) * ndf / onePlusLambda_V, absNdotV, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta); + return transform(); + } + + scalar_type ndf; + scalar_type absNdotV; + scalar_type onePlusLambda_V; +}; + +template +struct VNDF_pdf > +{ + using scalar_type = T; + using ndf_type = ndf::GGX; + using this_t = VNDF_pdf; + + static this_t create(scalar_type ndf, scalar_type absNdotV) + { + this_t retval; + retval.ndf = ndf; + retval.absNdotV = absNdotV; + return retval; + } + + scalar_type operator()(scalar_type G1_over_2NdotV, bool transmitted, scalar_type VdotH, scalar_type LdotH, scalar_type VdotHLdotH, scalar_type orientedEta, scalar_type reflectance) + { + scalar_type FN = hlsl::mix(reflectance, scalar_type(1.0) - reflectance, transmitted) * ndf; + FVNDF_pdf fvndf = FVNDF_pdf::create(FN, absNdotV); + return fvndf(G1_over_2NdotV, transmitted, VdotH, LdotH, VdotHLdotH, orientedEta); + } + + scalar_type ndf; + scalar_type absNdotV; + scalar_type onePlusLambda_V; +}; + +} + + +template) +struct SIsotropicParams +{ + using this_t = SIsotropicParams; + + static this_t create(T a2, T NdotV2, T NdotL2, T lambdaV_plus_one) // beckmann + { + this_t retval; + retval.a2 = a2; + retval.NdotV2 = NdotV2; + retval.NdotL2 = NdotL2; + retval.lambdaV_plus_one = lambdaV_plus_one; + return retval; + } + + static this_t create(T a2, T NdotV, T NdotV2, T NdotL, T NdotL2) // ggx + { + this_t retval; + retval.a2 = a2; + retval.NdotV = NdotV; + retval.NdotV2 = NdotV2; + retval.NdotL = NdotL; + retval.NdotL2 = NdotL2; + retval.one_minus_a2 = 1.0 - a2; + return retval; + } + + T a2; + T NdotV; + T NdotL; + T NdotV2; + T NdotL2; + T lambdaV_plus_one; + T one_minus_a2; +}; + +template) +struct SAnisotropicParams +{ + using this_t = SAnisotropicParams; + + static this_t create(T ax2, T ay2, T TdotV2, T BdotV2, T NdotV2, T TdotL2, T BdotL2, T NdotL2, T lambdaV_plus_one) // beckmann + { + this_t retval; + retval.ax2 = ax2; + retval.ay2 = ay2; + retval.TdotV2 = TdotV2; + retval.BdotV2 = BdotV2; + retval.NdotV2 = NdotV2; + retval.TdotL2 = TdotL2; + retval.BdotL2 = BdotL2; + retval.NdotL2 = NdotL2; + retval.lambdaV_plus_one = lambdaV_plus_one; + return retval; + } + + static this_t create(T ax2, T ay2, T NdotV, T TdotV2, T BdotV2, T NdotV2, T NdotL, T TdotL2, T BdotL2, T NdotL2) // ggx + { + this_t retval; + retval.ax2 = ax2; + retval.ay2 = ay2; + retval.NdotL = NdotL; + retval.NdotV = NdotV; + retval.TdotV2 = TdotV2; + retval.BdotV2 = BdotV2; + retval.NdotV2 = NdotV2; + retval.TdotL2 = TdotL2; + retval.BdotL2 = BdotL2; + retval.NdotL2 = NdotL2; + return retval; + } + + T ax2; + T ay2; + T NdotV; + T NdotL; + T TdotV2; + T BdotV2; + T NdotV2; + T TdotL2; + T BdotL2; + T NdotL2; + T lambdaV_plus_one; +}; + + +// beckmann +template) +struct Beckmann +{ + using scalar_type = T; + + scalar_type G1(scalar_type lambda) + { + return 1.0 / (1.0 + lambda); + } + + scalar_type C2(scalar_type NdotX2, scalar_type a2) + { + return NdotX2 / (a2 * (1.0 - NdotX2)); + } + + scalar_type C2(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2, scalar_type ax2, scalar_type ay2) + { + return NdotX2 / (TdotX2 * ax2 + BdotX2 * ay2); + } + + scalar_type Lambda(scalar_type c2) + { + scalar_type c = sqrt(c2); + scalar_type nom = 1.0 - 1.259 * c + 0.396 * c2; + scalar_type denom = 2.181 * c2 + 3.535 * c; + return hlsl::mix(0.0, nom / denom, c < 1.6); + } + + scalar_type Lambda(scalar_type NdotX2, scalar_type a2) + { + return Lambda(C2(NdotX2, a2)); + } + + scalar_type Lambda(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2, scalar_type ax2, scalar_type ay2) + { + return Lambda(C2(TdotX2, BdotX2, NdotX2, ax2, ay2)); + } + + scalar_type correlated(SIsotropicParams params) + { + scalar_type c2 = C2(params.NdotV2, params.a2); + scalar_type L_v = Lambda(c2); + c2 = C2(params.NdotL2, params.a2); + scalar_type L_l = Lambda(c2); + return G1(L_v + L_l); + } + + scalar_type correlated(SAnisotropicParams params) + { + scalar_type c2 = C2(params.TdotV2, params.BdotV2, params.NdotV2, params.ax2, params.ay2); + scalar_type L_v = Lambda(c2); + c2 = C2(params.TdotL2, params.BdotL2, params.NdotL2, params.ax2, params.ay2); + scalar_type L_l = Lambda(c2); + return G1(L_v + L_l); + } + + scalar_type G2_over_G1(SIsotropicParams params) + { + scalar_type lambdaL = Lambda(params.NdotL2, params.a2); + return params.lambdaV_plus_one / (params.lambdaV_plus_one + lambdaL); + } + + scalar_type G2_over_G1(SAnisotropicParams params) + { + scalar_type c2 = C2(params.TdotL2, params.BdotL2, params.NdotL2, params.ax2, params.ay2); + scalar_type lambdaL = Lambda(c2); + return params.lambdaV_plus_one / (params.lambdaV_plus_one + lambdaL); + } +}; + + +// ggx +template) +struct GGX +{ + using scalar_type = T; + + scalar_type devsh_part(scalar_type NdotX2, scalar_type a2, scalar_type one_minus_a2) + { + return sqrt(a2 + one_minus_a2 * NdotX2); + } + + scalar_type devsh_part(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2, scalar_type ax2, scalar_type ay2) + { + return sqrt(TdotX2 * ax2 + BdotX2 * ay2 + NdotX2); + } + + scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type NdotX2, scalar_type a2, scalar_type one_minus_a2) + { + return 1.0 / (NdotX + devsh_part(NdotX2,a2,one_minus_a2)); + } + + scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2, scalar_type ax2, scalar_type ay2) + { + return 1.0 / (NdotX + devsh_part(TdotX2, BdotX2, NdotX2, ax2, ay2)); + } + + scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type devsh_part) + { + return 1.0 / (NdotX + devsh_part); + } + + scalar_type correlated_wo_numerator(SIsotropicParams params) + { + scalar_type Vterm = params.NdotL * devsh_part(params.NdotV2, params.a2, params.one_minus_a2); + scalar_type Lterm = params.NdotV * devsh_part(params.NdotL2, params.a2, params.one_minus_a2); + return 0.5 / (Vterm + Lterm); + } + + scalar_type correlated_wo_numerator(SAnisotropicParams params) + { + scalar_type Vterm = params.NdotL * devsh_part(params.TdotV2, params.BdotV2, params.NdotV2, params.ax2, params.ay2); + scalar_type Lterm = params.NdotV * devsh_part(params.TdotL2, params.BdotL2, params.NdotL2, params.ax2, params.ay2); + return 0.5 / (Vterm + Lterm); + } + + scalar_type G2_over_G1(SIsotropicParams params) + { + scalar_type devsh_v = devsh_part(params.NdotV2, params.a2, params.one_minus_a2); + scalar_type G2_over_G1 = params.NdotL * (devsh_v + params.NdotV); // alternative `Vterm+NdotL*NdotV /// NdotL*NdotV could come as a parameter + G2_over_G1 /= params.NdotV * devsh_part(params.NdotL2, params.a2, params.one_minus_a2) + params.NdotL * devsh_v; + + return G2_over_G1; + } + + scalar_type G2_over_G1(SAnisotropicParams params) + { + scalar_type devsh_v = devsh_part(params.TdotV2, params.BdotV2, params.NdotV2, params.ax2, params.ay2); + scalar_type G2_over_G1 = params.NdotL * (devsh_v + params.NdotV); + G2_over_G1 /= params.NdotV * devsh_part(params.TdotL2, params.BdotL2, params.NdotL2, params.ax2, params.ay2) + params.NdotL * devsh_v; + + return G2_over_G1; + } + +}; + +} +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/ndf.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl new file mode 100644 index 0000000000..dfaf4f06a1 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl @@ -0,0 +1,339 @@ +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_NDF_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_NDF_INCLUDED_ + +#include "nbl/builtin/hlsl/limits.hlsl" +#include "nbl/builtin/hlsl/bxdf/common.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace ndf +{ + +template) +struct SIsotropicParams +{ + using this_t = SIsotropicParams; + + static this_t create(T n_or_a2, T NdotH, T NdotH2) // beckmann, ggx : a2; blinn-phong : n + { + this_t retval; + retval.n_or_a2 = n_or_a2; + retval.NdotH = NdotH; + retval.NdotH2 = NdotH2; + return retval; + } + + T n_or_a2; + T NdotH; + T NdotH2; +}; + +template) +struct SAnisotropicParams +{ + using this_t = SAnisotropicParams; + + static this_t create(T NdotH, T one_minus_NdotH2_rcp, T TdotH2, T BdotH2, T nx, T ny) // blinn-phong + { + this_t retval; + retval.NdotH = NdotH; + retval.one_minus_NdotH2_rcp = one_minus_NdotH2_rcp; + retval.TdotH2 = TdotH2; + retval.BdotH2 = BdotH2; + retval.nx = nx; + retval.ny = ny; + return retval; + } + + static this_t create(T ax, T ay, T ax2, T ay2, T TdotH2, T BdotH2, T NdotH2) // beckmann, ggx aniso + { + this_t retval; + retval.ax = ax; + retval.ax2 = ax2; + retval.ay = ay; + retval.ay2 = ay2; + retval.TdotH2 = TdotH2; + retval.BdotH2 = BdotH2; + retval.NdotH2 = NdotH2; + return retval; + } + + static this_t create(T a2, T TdotH, T BdotH, T NdotH) // ggx burley + { + this_t retval; + retval.ax = a2; + retval.TdotH = TdotH; + retval.BdotH = BdotH; + retval.NdotH = NdotH; + return retval; + } + + T ax; + T ay; + T ax2; + T ay2; + T nx; + T ny; + T NdotH; + T TdotH; + T BdotH; + T NdotH2; + T TdotH2; + T BdotH2; + T one_minus_NdotH2_rcp; +}; + + +template) +struct BlinnPhong +{ + using scalar_type = T; + + // blinn-phong + scalar_type operator()(SIsotropicParams params) + { + // n is shininess exponent in original paper + return isinf(params.n_or_a2) ? numeric_limits::infinity : numbers::inv_pi * 0.5 * (params.n_or_a2 + 2.0) * pow(params.NdotH, params.n_or_a2); + } + + //ashikhmin-shirley ndf + scalar_type operator()(SAnisotropicParams params) + { + scalar_type n = (params.TdotH2 * params.ny + params.BdotH2 * params.nx) * params.one_minus_NdotH2_rcp; + return (isinf(params.nx) || isinf(params.ny)) ? numeric_limits::infinity : + sqrt((params.nx + 2.0) * (params.ny + 2.0)) * numbers::inv_pi * 0.5 * pow(params.NdotH, n); + } +}; + +template) +struct Beckmann +{ + using scalar_type = T; + + scalar_type operator()(SIsotropicParams params) + { + scalar_type nom = exp( (params.NdotH2 - 1.0) / (params.n_or_a2 * params.NdotH2) ); // exp(x) == exp2(x/log(2)) ? + scalar_type denom = params.n_or_a2 * params.NdotH2 * params.NdotH2; + return numbers::inv_pi * nom / denom; + } + + scalar_type operator()(SAnisotropicParams params) + { + scalar_type nom = exp(-(params.TdotH2 / params.ax2 + params.BdotH2 / params.ay2) / params.NdotH2); + scalar_type denom = params.ax * params.ay * params.NdotH2 * params.NdotH2; + return numbers::inv_pi * nom / denom; + } +}; + + +template) +struct GGX +{ + using scalar_type = T; + + // trowbridge-reitz + scalar_type operator()(SIsotropicParams params) + { + scalar_type denom = params.NdotH2 * (params.n_or_a2 - 1.0) + 1.0; + return params.n_or_a2 * numbers::inv_pi / (denom * denom); + } + + scalar_type operator()(SAnisotropicParams params) + { + scalar_type a2 = params.ax * params.ay; + scalar_type denom = params.TdotH2 / params.ax2 + params.BdotH2 / params.ay2 + params.NdotH2; + return numbers::inv_pi / (a2 * denom * denom); + } + + // burley + scalar_type operator()(SAnisotropicParams params, scalar_type anisotropy) + { + scalar_type antiAniso = 1.0 - anisotropy; + scalar_type atab = params.ax * antiAniso; + scalar_type anisoTdotH = antiAniso * params.TdotH; + scalar_type anisoNdotH = antiAniso * params.NdotH; + scalar_type w2 = antiAniso/(params.BdotH * params.BdotH + anisoTdotH * anisoTdotH + anisoNdotH * anisoNdotH * params.ax); + return w2 * w2 * atab * numbers::inv_pi; + } +}; + +// common +namespace impl +{ +template +struct is_ggx : bool_constant< + is_same >::value +> {}; +} + +template +struct is_ggx : impl::is_ggx {}; + +template +NBL_CONSTEXPR bool is_ggx_v = is_ggx::value; + + +enum MicrofacetTransformTypes : uint16_t +{ + REFLECT_BIT = 0b01, + REFRACT_BIT = 0b10, + REFLECT_REFRACT_BIT = 0b11 +}; + +template +struct microfacet_to_light_measure_transform; + + +template +struct microfacet_to_light_measure_transform +{ + using this_t = microfacet_to_light_measure_transform; + using scalar_type = typename NDF::scalar_type; + + static this_t create(scalar_type NDFcos, scalar_type maxNdotV) + { + this_t retval; + retval.NDFcos = NDFcos; + if (is_ggx_v) + retval.maxNdotL = maxNdotV; + else + retval.maxNdotV = maxNdotV; + return retval; + } + + scalar_type operator()() + { + if (is_ggx_v) + return NDFcos * maxNdotL; + else + return 0.25 * NDFcos / maxNdotV; + } + + scalar_type NDFcos; + scalar_type maxNdotV; + scalar_type maxNdotL; +}; + +template +struct microfacet_to_light_measure_transform +{ + using this_t = microfacet_to_light_measure_transform; + using scalar_type = typename NDF::scalar_type; + + static this_t create(scalar_type NDFcos, scalar_type absNdotV, scalar_type VdotH, scalar_type LdotH, scalar_type VdotHLdotH, scalar_type orientedEta) + { + this_t retval; + retval.NDFcos = NDFcos; + if (is_ggx_v) + retval.absNdotL = absNdotV; + else + retval.absNdotV = absNdotV; + retval.VdotH = VdotH; + retval.LdotH = LdotH; + retval.VdotHLdotH = VdotHLdotH; + retval.orientedEta = orientedEta; + return retval; + } + + scalar_type operator()() + { + if (is_ggx_v) + { + scalar_type denominator = absNdotL; + const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + denominator *= -4.0 * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH); + return NDFcos * denominator; + } + else + { + scalar_type denominator = absNdotV; + const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + denominator *= -VdotH_etaLdotH * VdotH_etaLdotH; + return NDFcos * VdotHLdotH / denominator; + } + } + + scalar_type NDFcos; + scalar_type absNdotV; + scalar_type absNdotL; + + scalar_type VdotH; + scalar_type LdotH; + scalar_type VdotHLdotH; + scalar_type orientedEta; +}; + +template +struct microfacet_to_light_measure_transform +{ + using this_t = microfacet_to_light_measure_transform; + using scalar_type = typename NDF::scalar_type; + + static this_t create(scalar_type NDFcos, scalar_type absNdotV, bool transmitted, scalar_type VdotH, scalar_type LdotH, scalar_type VdotHLdotH, scalar_type orientedEta) + { + this_t retval; + retval.NDFcos = NDFcos; + if (is_ggx_v) + retval.absNdotL = absNdotV; + else + retval.absNdotV = absNdotV; + retval.transmitted = transmitted; + retval.VdotH = VdotH; + retval.LdotH = LdotH; + retval.VdotHLdotH = VdotHLdotH; + retval.orientedEta = orientedEta; + return retval; + } + + scalar_type operator()() + { + if (is_ggx_v) + { + 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 *= -4.0 * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH); + } + return NDFcos * denominator; + } + else + { + scalar_type denominator = absNdotV; + if (transmitted) + { + const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + denominator *= -VdotH_etaLdotH * VdotH_etaLdotH; + } + return NDFcos * (transmitted ? VdotHLdotH : 0.25) / denominator; + } + } + + bool transmitted; + scalar_type NDFcos; + scalar_type absNdotV; + scalar_type absNdotL; + + scalar_type VdotH; + scalar_type LdotH; + scalar_type VdotHLdotH; + scalar_type orientedEta; +}; + +} +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl index 878cd7b6f3..ed049d3d1a 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection.hlsl @@ -1,40 +1,22 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h #ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_INCLUDED_ #define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_INCLUDED_ -#include +#include "nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl" namespace nbl { namespace hlsl { -namespace bxdf -{ -namespace reflection -{ -template -LightSample cos_generate(const surface_interactions::Isotropic interaction) -{ - return LightSample(interaction.V.reflect(interaction.N,interaction.NdotV),interaction.NdotV,interaction.N); -} -template -LightSample cos_generate(const surface_interactions::Anisotropic interaction) -{ - return LightSample(interaction.V.reflect(interaction.N,interaction.NdotV),interaction.NdotV,interaction.T,interaction.B,interaction.N); -} +// After Clang-HLSL introduces https://en.cppreference.com/w/cpp/language/namespace_alias +// namespace brdf = bxdf::reflection; -// for information why we don't check the relation between `V` and `L` or `N` and `H`, see comments for `nbl::hlsl::transmission::cos_quotient_and_pdf` -template -quotient_and_pdf cos_quotient_and_pdf() -{ - return quotient_and_pdf::create(SpectralBins(1.f),nbl::hlsl::numeric_limits::inf()); -} - -} -} } } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl new file mode 100644 index 0000000000..d8a4c3c3c5 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl @@ -0,0 +1,377 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_BECKMANN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_BECKMANN_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/geom_smith.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template +struct BeckmannParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic && !AnisotropicMicrofacetCache) +struct BeckmannParams && !AnisotropicMicrofacetCache) > +{ + using this_t = BeckmannParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic && AnisotropicMicrofacetCache) +struct BeckmannParams && AnisotropicMicrofacetCache) > +{ + using this_t = BeckmannParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + Scalar getTdotH2() NBL_CONST_MEMBER_FUNC {return cache.getTdotH() * cache.getTdotH(); } + Scalar getBdotH2() NBL_CONST_MEMBER_FUNC {return cache.getBdotH() * cache.getBdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic && CreatableIsotropicMicrofacetCache && AnisotropicMicrofacetCache) +struct SBeckmannBxDF +{ + using this_t = SBeckmannBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector2_type = vector; + using vector3_type = vector; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + + using params_isotropic_t = BeckmannParams; + using params_anisotropic_t = BeckmannParams; + + + // iso + 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.A = vector2_type(A,A); + retval.ior0 = ior0; + retval.ior1 = ior1; + return retval; + } + + // aniso + 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.A = vector2_type(ax,ay); + retval.ior0 = ior0; + retval.ior1 = ior1; + return retval; + } + + scalar_type __eval_DG_wo_clamps(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + scalar_type NG = beckmann_ndf(ndfparams); + if (a2 > numeric_limits::min) + { + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(a2, params.getNdotV2(), params.getNdotL2(), 0); + smith::Beckmann beckmann_smith; + NG *= beckmann_smith.correlated(smithparams); + } + return NG; + } + scalar_type __eval_DG_wo_clamps(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + scalar_type NG = beckmann_ndf(ndfparams); + if (any >(A > (vector2_type)numeric_limits::min)) + { + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(ax2, ay2, params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2(), 0); + smith::Beckmann beckmann_smith; + NG *= beckmann_smith.correlated(smithparams); + } + return NG; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + if (params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type scalar_part = __eval_DG_wo_clamps(params); + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT> microfacet_transform = ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT>::create(scalar_part, params.getNdotVUnclamped()); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + return f() * microfacet_transform(); + } + else + return (spectral_type)0.0; + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + if (params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type scalar_part = __eval_DG_wo_clamps(params); + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT> microfacet_transform = ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT>::create(scalar_part, params.getNdotVUnclamped()); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + return f() * microfacet_transform(); + } + else + return (spectral_type)0.0; + } + + vector3_type __generate(NBL_CONST_REF_ARG(vector3_type) localV, NBL_CONST_REF_ARG(vector2_type) u) + { + //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, NBL_CONST_REF_ARG(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, cache.iso_cache.getVdotH()); + localL.direction = r(); + + return sample_type::createFromTangentSpace(localV, localL, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector2_type) u, NBL_REF_ARG(isocache_type) cache) + { + anisocache_type anisocache; + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params, NBL_REF_ARG(scalar_type) onePlusLambda_V) + { + scalar_type ndf, lambda; + scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + ndf = beckmann_ndf(ndfparams); + + smith::Beckmann beckmann_smith; + lambda = beckmann_smith.Lambda(params.getNdotV2(), a2); + + smith::brdf::VNDF_pdf > vndf = smith::brdf::VNDF_pdf >::create(ndf, params.getNdotVUnclamped()); + scalar_type _pdf = vndf(lambda); + onePlusLambda_V = vndf.onePlusLambda_V; + + return _pdf; + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params, NBL_REF_ARG(scalar_type) onePlusLambda_V) + { + scalar_type ndf, lambda; + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, A.x*A.x, A.y*A.y, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + ndf = beckmann_ndf(ndfparams); + + smith::Beckmann beckmann_smith; + const scalar_type c2 = beckmann_smith.C2(params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), A.x, A.y); + lambda = beckmann_smith.Lambda(c2); + + smith::brdf::VNDF_pdf > vndf = smith::brdf::VNDF_pdf >::create(ndf, params.getNdotVUnclamped()); + scalar_type _pdf = vndf(lambda); + onePlusLambda_V = vndf.onePlusLambda_V; + + return _pdf; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type dummy; + return pdf(params, dummy); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type dummy; + return pdf(params, dummy); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type onePlusLambda_V; + scalar_type _pdf = pdf(params, onePlusLambda_V); + + smith::Beckmann beckmann_smith; + spectral_type quo = (spectral_type)0.0; + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(A.x*A.x, params.getNdotV2(), params.getNdotL2(), onePlusLambda_V); + scalar_type G2_over_G1 = beckmann_smith.G2_over_G1(smithparams); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type onePlusLambda_V; + scalar_type _pdf = pdf(params, onePlusLambda_V); + + smith::Beckmann beckmann_smith; + spectral_type quo = (spectral_type)0.0; + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(A.x*A.x, A.y*A.y, params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2(), onePlusLambda_V); + scalar_type G2_over_G1 = beckmann_smith.G2_over_G1(smithparams); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + + vector2_type A; + spectral_type ior0, ior1; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl new file mode 100644 index 0000000000..afa87765c6 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl @@ -0,0 +1,325 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_GGX_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_GGX_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/geom_smith.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template +struct GGXParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic && !AnisotropicMicrofacetCache) +struct GGXParams && !AnisotropicMicrofacetCache) > +{ + using this_t = GGXParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic && AnisotropicMicrofacetCache) +struct GGXParams && AnisotropicMicrofacetCache) > +{ + using this_t = GGXParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + Scalar getTdotH2() NBL_CONST_MEMBER_FUNC {return cache.getTdotH() * cache.getTdotH(); } + Scalar getBdotH2() NBL_CONST_MEMBER_FUNC {return cache.getBdotH() * cache.getBdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic && CreatableIsotropicMicrofacetCache && AnisotropicMicrofacetCache) +struct SGGXBxDF +{ + using this_t = SGGXBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector2_type = vector; + using vector3_type = vector; + using matrix2x3_type = matrix; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + + using params_isotropic_t = GGXParams; + using params_anisotropic_t = GGXParams; + + + // iso + 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.A = vector2_type(A,A); + retval.ior0 = ior0; + retval.ior1 = ior1; + return retval; + } + + // aniso + 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.A = vector2_type(ax,ay); + retval.ior0 = ior0; + retval.ior1 = ior1; + return retval; + } + + scalar_type __eval_DG_wo_clamps(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::GGX ggx_ndf; + scalar_type NG = ggx_ndf(ndfparams); + if (a2 > numeric_limits::min) + { + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(a2, params.getNdotV(), params.getNdotV2(), params.getNdotL(), params.getNdotL2()); + smith::GGX ggx_smith; + NG *= ggx_smith.correlated_wo_numerator(smithparams); + } + return NG; + } + scalar_type __eval_DG_wo_clamps(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::GGX ggx_ndf; + scalar_type NG = ggx_ndf(ndfparams); + if (any >(A > (vector2_type)numeric_limits::min)) + { + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(ax2, ay2, params.getNdotV(), params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getNdotL(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2()); + smith::GGX ggx_smith; + NG *= ggx_smith.correlated_wo_numerator(smithparams); + } + return NG; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type scalar_part = __eval_DG_wo_clamps(params); + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT> microfacet_transform = ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT>::create(scalar_part, params.getNdotL()); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + return f() * microfacet_transform(); + } + else + return (spectral_type)0.0; + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type scalar_part = __eval_DG_wo_clamps(params); + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT> microfacet_transform = ndf::microfacet_to_light_measure_transform,ndf::REFLECT_BIT>::create(scalar_part, params.getNdotL()); + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + return f() * microfacet_transform(); + } + else + return (spectral_type)0.0; + } + + vector3_type __generate(NBL_CONST_REF_ARG(vector3_type) localV, NBL_CONST_REF_ARG(vector2_type) u) + { + 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, NBL_CONST_REF_ARG(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, cache.iso_cache.getVdotH()); + localL.direction = r(); + + return sample_type::createFromTangentSpace(localV, localL, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector2_type) u, NBL_REF_ARG(isocache_type) cache) + { + anisocache_type anisocache; + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type ndf, G1_over_2NdotV; + const scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::GGX ggx_ndf; + ndf = ggx_ndf(ndfparams); + + smith::GGX ggx_smith; + const scalar_type devsh_v = ggx_smith.devsh_part(params.getNdotV2(), a2, 1.0-a2); + G1_over_2NdotV = ggx_smith.G1_wo_numerator(params.getNdotVUnclamped(), devsh_v); + + smith::brdf::VNDF_pdf > vndf = smith::brdf::VNDF_pdf >::create(ndf, params.getNdotVUnclamped()); + return vndf(G1_over_2NdotV); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type ndf, G1_over_2NdotV; + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::GGX ggx_ndf; + ndf = ggx_ndf(ndfparams); + + smith::GGX ggx_smith; + const scalar_type devsh_v = ggx_smith.devsh_part(params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), ax2, ay2); + G1_over_2NdotV = ggx_smith.G1_wo_numerator(params.getNdotVUnclamped(), devsh_v); + + smith::brdf::VNDF_pdf > vndf = smith::brdf::VNDF_pdf >::create(ndf, params.getNdotVUnclamped()); + return vndf(G1_over_2NdotV); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type _pdf = pdf(params); + + spectral_type quo = (spectral_type)0.0; + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type G2_over_G1; + smith::GGX ggx_smith; + + const scalar_type a2 = A.x*A.x; + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(a2, params.getNdotVUnclamped(), params.getNdotV2(), params.getNdotLUnclamped(), params.getNdotL2()); + G2_over_G1 = ggx_smith.G2_over_G1(smithparams); + + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type _pdf = pdf(params); + + spectral_type quo = (spectral_type)0.0; + if (params.getNdotLUnclamped() > numeric_limits::min && params.getNdotVUnclamped() > numeric_limits::min) + { + scalar_type G2_over_G1; + smith::GGX ggx_smith; + + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(ax2, ay2, params.getNdotVUnclamped(), params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getNdotLUnclamped(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2()); + G2_over_G1 = ggx_smith.G2_over_G1(smithparams); + + fresnel::Conductor f = fresnel::Conductor::create(ior0, ior1, params.getVdotH()); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + + vector2_type A; + spectral_type ior0, ior1; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl new file mode 100644 index 0000000000..8a9dc55ef1 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl @@ -0,0 +1,166 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_LAMBERTIAN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_LAMBERTIAN_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/geom_smith.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template +struct LambertianParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic) +struct LambertianParams) > +{ + using this_t = LambertianParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic) +struct LambertianParams) > +{ + using this_t = LambertianParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic) +struct SLambertianBxDF +{ + using this_t = SLambertianBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + + using params_isotropic_t = LambertianParams; + using params_anisotropic_t = LambertianParams; + + + static this_t create() + { + this_t retval; + // nothing here, just keeping in convention with others + return retval; + } + + scalar_type __eval_pi_factored_out(scalar_type maxNdotL) + { + return maxNdotL; + } + + scalar_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return __eval_pi_factored_out(params.getNdotL()) * numbers::inv_pi; + } + scalar_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return __eval_pi_factored_out(params.getNdotL()) * numbers::inv_pi; + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + ray_dir_info_type L; + L.direction = sampling::ProjectedHemisphere::generate(u); + return sample_type::createFromTangentSpace(interaction.getTangentSpaceV(), L, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + return generate_wo_clamps(interaction, u); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + return generate_wo_clamps(anisotropic_interaction_type::create(interaction), u); + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return sampling::ProjectedHemisphere::pdf(params.getNdotL()); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return sampling::ProjectedHemisphere::pdf(params.getNdotL()); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + sampling::quotient_and_pdf qp = sampling::ProjectedHemisphere::template quotient_and_pdf(params.getNdotL()); + return quotient_pdf_type::create(hlsl::promote(qp.quotient), qp.pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + sampling::quotient_and_pdf qp = sampling::ProjectedHemisphere::template quotient_and_pdf(params.getNdotL()); + return quotient_pdf_type::create(hlsl::promote(qp.quotient), qp.pdf); + } +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl new file mode 100644 index 0000000000..6cca0d0eac --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl @@ -0,0 +1,177 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_REFLECTION_OREN_NAYAR_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_REFLECTION_OREN_NAYAR_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/geom_smith.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template +struct OrenNayarParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic) +struct OrenNayarParams) > +{ + using this_t = OrenNayarParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic) +struct OrenNayarParams) > +{ + using this_t = OrenNayarParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic) +struct SOrenNayarBxDF +{ + using this_t = SOrenNayarBxDF; + using scalar_type = typename LS::scalar_type; + using vector2_type = vector; + using ray_dir_info_type = typename LS::ray_dir_info_type; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + + using params_isotropic_t = OrenNayarParams; + using params_anisotropic_t = OrenNayarParams; + + + static this_t create(scalar_type A) + { + this_t retval; + retval.A = A; + return retval; + } + + scalar_type __rec_pi_factored_out_wo_clamps(scalar_type VdotL, scalar_type maxNdotL, scalar_type maxNdotV) + { + scalar_type A2 = A * 0.5; + vector2_type AB = vector2_type(1.0, 0.0) + vector2_type(-0.5, 0.45) * vector2_type(A2, A2) / vector2_type(A2 + 0.33, A2 + 0.09); + scalar_type C = 1.0 / max(maxNdotL, maxNdotV); + + scalar_type cos_phi_sin_theta = max(VdotL - maxNdotL * maxNdotV, 0.0); + return (AB.x + AB.y * cos_phi_sin_theta * C); + } + + scalar_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return params.getNdotL() * numbers::inv_pi * __rec_pi_factored_out_wo_clamps(params.getVdotL(), params.getNdotL(), params.getNdotV()); + } + scalar_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return params.getNdotL() * numbers::inv_pi * __rec_pi_factored_out_wo_clamps(params.getVdotL(), params.getNdotL(), params.getNdotV()); + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector2_type) u) + { + ray_dir_info_type L; + L.direction = sampling::ProjectedHemisphere::generate(u); + return sample_type::createFromTangentSpace(interaction.getTangentSpaceV(), L, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector2_type) u) + { + return generate_wo_clamps(interaction, u); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + return generate_wo_clamps(anisotropic_interaction_type::create(interaction), u); + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return sampling::ProjectedHemisphere::pdf(params.getNdotL()); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return sampling::ProjectedHemisphere::pdf(params.getNdotL()); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type _pdf = pdf(params); + scalar_type q = __rec_pi_factored_out_wo_clamps(params.getVdotL(), params.getNdotL(), params.getNdotV()); + return quotient_pdf_type::create(hlsl::promote(q), _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type _pdf = pdf(params); + scalar_type q = __rec_pi_factored_out_wo_clamps(params.getVdotL(), params.getNdotL(), params.getNdotV()); + return quotient_pdf_type::create(hlsl::promote(q), _pdf); + } + + scalar_type A; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl index ff708548b8..81f531e1a6 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission.hlsl @@ -1,45 +1,22 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// 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 +// For conditions of distribution and use, see copyright notice nabla.h #ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_INCLUDED_ #define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_INCLUDED_ -#include +#include "nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl" +#include "nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl" namespace nbl { namespace hlsl { -namespace bxdf -{ -namespace transmission -{ -template -LightSample cos_generate(const surface_interactions::Isotropic interaction) -{ - return LightSample(interaction.V.transmit(),-1.f,interaction.N); -} -template -LightSample cos_generate(const surface_interactions::Anisotropic interaction) -{ - return LightSample(interaction.V.transmit(),-1.f,interaction.T,interaction.B,interaction.N); -} +// After Clang-HLSL introduces https://en.cppreference.com/w/cpp/language/namespace_alias +// namespace bsdf = bxdf::transmission; -// Why don't we check that the incoming and outgoing directions equal each other -// (or similar for other delta distributions such as reflect, or smooth [thin] dielectrics): -// - The `quotient_and_pdf` functions are meant to be used with MIS and RIS -// - Our own generator can never pick an improbable path, so no checking necessary -// - For other generators the estimator will be `f_BSDF*f_Light*f_Visibility*clampedCos(theta)/(1+(p_BSDF^alpha+p_otherNonChosenGenerator^alpha+...)/p_ChosenGenerator^alpha)` -// therefore when `p_BSDF` equals `nbl_glsl_FLT_INF` it will drive the overall MIS estimator for the other generators to 0 so no checking necessary -template -quotient_and_pdf cos_quotient_and_pdf() -{ - return quotient_and_pdf::create(SpectralBins(1.f),nbl::hlsl::numeric_limits::inf()); -} - -} -} } } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl new file mode 100644 index 0000000000..249e04f847 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl @@ -0,0 +1,326 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_BECKMANN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_BECKMANN_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template +struct BeckmannParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic && !AnisotropicMicrofacetCache) +struct BeckmannParams && !AnisotropicMicrofacetCache) > +{ + using this_t = BeckmannParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic && AnisotropicMicrofacetCache) +struct BeckmannParams && AnisotropicMicrofacetCache) > +{ + using this_t = BeckmannParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + Scalar getTdotH2() NBL_CONST_MEMBER_FUNC {return cache.getTdotH() * cache.getTdotH(); } + Scalar getBdotH2() NBL_CONST_MEMBER_FUNC {return cache.getBdotH() * cache.getBdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic && CreatableIsotropicMicrofacetCache && AnisotropicMicrofacetCache) +struct SBeckmannDielectricBxDF +{ + using this_t = SBeckmannDielectricBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector2_type = vector; + using vector3_type = vector; + using matrix3x3_type = matrix; + using monochrome_type = vector; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + using brdf_type = reflection::SBeckmannBxDF; + + using params_isotropic_t = BeckmannParams; + using params_anisotropic_t = BeckmannParams; + + + static this_t create(scalar_type eta, scalar_type A) + { + this_t retval; + retval.eta = eta; + retval.A = vector2_type(A, A); + return retval; + } + + static this_t create(scalar_type eta, scalar_type ax, scalar_type ay) + { + this_t retval; + retval.eta = eta; + retval.A = vector2_type(ax, ay); + return retval; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), hlsl::promote(eta)); + const scalar_type orientedEta2 = orientedEta.value[0] * orientedEta.value[0]; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + spectral_type dummyior; + brdf_type beckmann = brdf_type::create(A.x, dummyior, dummyior); + typename brdf_type::params_isotropic_t brdf_params = typename brdf_type::params_isotropic_t::create(params._sample, params.interaction, params.cache, params._clamp); + const scalar_type scalar_part = beckmann.__eval_DG_wo_clamps(brdf_params); + + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT> microfacet_transform = + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT>::create(scalar_part,params.getNdotV(),transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value[0]); + scalar_type f = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + return hlsl::promote(f) * microfacet_transform(); + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), hlsl::promote(eta)); + const scalar_type orientedEta2 = orientedEta.value[0] * orientedEta.value[0]; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + spectral_type dummyior; + brdf_type beckmann = brdf_type::create(A.x, A.y, dummyior, dummyior); + typename brdf_type::params_anisotropic_t brdf_params = typename brdf_type::params_anisotropic_t::create(params._sample, params.interaction, params.cache, params._clamp); + const scalar_type scalar_part = beckmann.__eval_DG_wo_clamps(brdf_params); + + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT> microfacet_transform = + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT>::create(scalar_part,params.getNdotV(),transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value[0]); + scalar_type f = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + return hlsl::promote(f) * microfacet_transform(); + } + + sample_type __generate_wo_clamps(NBL_CONST_REF_ARG(vector3_type) localV, NBL_CONST_REF_ARG(vector3_type) H, NBL_CONST_REF_ARG(matrix3x3_type) m, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, 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 = fresnel::Dielectric::__call(orientedEta.value[0] * orientedEta.value[0],nbl::hlsl::abs(localVdotH)); + + 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; + r.recomputeNdotT(VdotH < 0.0, VdotH * VdotH, rcpEta.value2[0]); + cache.iso_cache.LdotH = hlsl::mix(VdotH, r.NdotT, transmitted); + ray_dir_info_type localL; + bxdf::ReflectRefract rr = bxdf::ReflectRefract::create(transmitted, localV, H, VdotH, cache.iso_cache.getLdotH(), rcpEta.value[0]); + localL.direction = rr(transmitted); + + return sample_type::createFromTangentSpace(localV, 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 = fresnel::OrientedEtas::create(interaction.isotropic.getNdotV(), hlsl::promote(eta)); + fresnel::OrientedEtaRcps rcpEta = fresnel::OrientedEtaRcps::create(interaction.isotropic.getNdotV(), hlsl::promote(eta)); + + const vector3_type upperHemisphereV = hlsl::mix(localV, -localV, interaction.isotropic.getNdotV() < scalar_type(0.0)); + + spectral_type dummyior; + brdf_type beckmann = brdf_type::create(A.x, A.y, dummyior, dummyior); + const vector3_type H = beckmann.__generate(upperHemisphereV, u.xy); + + return __generate_wo_clamps(localV, H, interaction.getFromTangentSpace(), u, orientedEta, 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); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(isocache_type) cache) + { + anisocache_type anisocache; + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params, NBL_REF_ARG(scalar_type) onePlusLambda_V) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), hlsl::promote(eta)); + const scalar_type orientedEta2 = orientedEta.value[0] * orientedEta.value[0]; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + + scalar_type ndf, lambda; + const scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + ndf = beckmann_ndf(ndfparams); + + smith::Beckmann beckmann_smith; + lambda = beckmann_smith.Lambda(params.getNdotV2(), a2); + + smith::bsdf::VNDF_pdf > vndf = smith::bsdf::VNDF_pdf >::create(ndf, params.getNdotV()); + scalar_type _pdf = vndf(lambda,transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value[0],reflectance); + onePlusLambda_V = vndf.onePlusLambda_V; + + return _pdf; + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params, NBL_REF_ARG(scalar_type) onePlusLambda_V) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), hlsl::promote(eta)); + const scalar_type orientedEta2 = orientedEta.value[0] * orientedEta.value[0]; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + + scalar_type ndf, lambda; + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::Beckmann beckmann_ndf; + ndf = beckmann_ndf(ndfparams); + + smith::Beckmann beckmann_smith; + scalar_type c2 = beckmann_smith.C2(params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), ax2, ay2); + lambda = beckmann_smith.Lambda(c2); + + smith::bsdf::VNDF_pdf > vndf = smith::bsdf::VNDF_pdf >::create(ndf, params.getNdotV()); + scalar_type _pdf = vndf(lambda,transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value[0],reflectance); + onePlusLambda_V = vndf.onePlusLambda_V; + + return _pdf; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type dummy; + return pdf(params, dummy); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type dummy; + return pdf(params, dummy); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + scalar_type onePlusLambda_V; + scalar_type _pdf = pdf(params, onePlusLambda_V); + + scalar_type quo; + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(A.x*A.x, params.getNdotV2(), params.getNdotL2(), onePlusLambda_V); + smith::Beckmann beckmann_smith; + quo = beckmann_smith.G2_over_G1(smithparams); + + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + scalar_type onePlusLambda_V; + scalar_type _pdf = pdf(params, onePlusLambda_V); + + scalar_type quo; + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(A.x*A.x, A.y*A.y, params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2(), onePlusLambda_V); + smith::Beckmann beckmann_smith; + quo = beckmann_smith.G2_over_G1(smithparams); + + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + + vector2_type A; + scalar_type eta; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl new file mode 100644 index 0000000000..8074d4b9eb --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl @@ -0,0 +1,318 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_GGX_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_GGX_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template +struct GGXParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic && !AnisotropicMicrofacetCache) +struct GGXParams && !AnisotropicMicrofacetCache) > +{ + using this_t = GGXParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic && AnisotropicMicrofacetCache) +struct GGXParams && AnisotropicMicrofacetCache) > +{ + using this_t = GGXParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, NBL_CONST_REF_ARG(MC) cache, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval.cache = cache; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + Scalar getNdotH() NBL_CONST_MEMBER_FUNC { return cache.getNdotH(); } + Scalar getNdotH2() NBL_CONST_MEMBER_FUNC { return cache.getNdotH2(); } + Scalar getVdotH() NBL_CONST_MEMBER_FUNC { return cache.getVdotH(); } + Scalar getLdotH() NBL_CONST_MEMBER_FUNC { return cache.getLdotH(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + Scalar getTdotH2() NBL_CONST_MEMBER_FUNC {return cache.getTdotH() * cache.getTdotH(); } + Scalar getBdotH2() NBL_CONST_MEMBER_FUNC {return cache.getBdotH() * cache.getBdotH(); } + + LS _sample; + SI interaction; + MC cache; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic && CreatableIsotropicMicrofacetCache && AnisotropicMicrofacetCache) +struct SGGXDielectricBxDF +{ + using this_t = SGGXDielectricBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector2_type = vector; + using vector3_type = vector; + using matrix3x3_type = matrix; + using monochrome_type = vector; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + using brdf_type = reflection::SGGXBxDF; + + using params_isotropic_t = GGXParams; + using params_anisotropic_t = GGXParams; + + static this_t create(scalar_type eta, scalar_type A) + { + this_t retval; + retval.eta = eta; + retval.A = vector2_type(A, A); + return retval; + } + + static this_t create(scalar_type eta, scalar_type ax, scalar_type ay) + { + this_t retval; + retval.eta = eta; + retval.A = vector2_type(ax, ay); + return retval; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), hlsl::promote(eta)); + const scalar_type orientedEta2 = orientedEta.value[0] * orientedEta.value[0]; + + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + scalar_type NG_already_in_reflective_dL_measure; + spectral_type dummyior; + brdf_type ggx = brdf_type::create(A.x, dummyior, dummyior); + typename brdf_type::params_isotropic_t brdf_params = typename brdf_type::params_isotropic_t::create(params._sample, params.interaction, params.cache, params._clamp); + NG_already_in_reflective_dL_measure = ggx.__eval_DG_wo_clamps(brdf_params); + + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT> microfacet_transform = + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT>::create(NG_already_in_reflective_dL_measure,params.getNdotL(),transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value[0]); + scalar_type f = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + return hlsl::promote(f) * microfacet_transform(); + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), hlsl::promote(eta)); + const scalar_type orientedEta2 = orientedEta.value[0] * orientedEta.value[0]; + + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + scalar_type NG_already_in_reflective_dL_measure; + spectral_type dummyior; + brdf_type ggx = brdf_type::create(A.x, A.y, dummyior, dummyior); + typename brdf_type::params_anisotropic_t brdf_params = typename brdf_type::params_anisotropic_t::create(params._sample, params.interaction, params.cache, params._clamp); + NG_already_in_reflective_dL_measure = ggx.__eval_DG_wo_clamps(brdf_params); + + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT> microfacet_transform = + ndf::microfacet_to_light_measure_transform,ndf::REFLECT_REFRACT_BIT>::create(NG_already_in_reflective_dL_measure,params.getNdotL(),transmitted,params.getVdotH(),params.getLdotH(),VdotHLdotH,orientedEta.value[0]); + scalar_type f = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + return hlsl::promote(f) * microfacet_transform(); + } + + sample_type __generate_wo_clamps(NBL_CONST_REF_ARG(vector3_type) localV, NBL_CONST_REF_ARG(vector3_type) H, NBL_CONST_REF_ARG(matrix3x3_type) m, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, 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 = fresnel::Dielectric::__call(orientedEta.value[0] * orientedEta.value[0],nbl::hlsl::abs(localVdotH)); + + 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; + r.recomputeNdotT(VdotH < 0.0, VdotH * VdotH, rcpEta.value2[0]); + cache.iso_cache.LdotH = hlsl::mix(VdotH, r.NdotT, transmitted); + ray_dir_info_type localL; + bxdf::ReflectRefract rr = bxdf::ReflectRefract::create(transmitted, localV, H, VdotH, cache.iso_cache.getLdotH(), rcpEta.value[0]); + localL.direction = rr(transmitted); + + return sample_type::createFromTangentSpace(localV, 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 = fresnel::OrientedEtas::create(interaction.isotropic.getNdotV(), hlsl::promote(eta)); + fresnel::OrientedEtaRcps rcpEta = fresnel::OrientedEtaRcps::create(interaction.isotropic.getNdotV(), hlsl::promote(eta)); + + const vector3_type upperHemisphereV = hlsl::mix(localV, -localV, interaction.isotropic.getNdotV() < scalar_type(0.0)); + + spectral_type dummyior; + brdf_type ggx = brdf_type::create(A.x, A.y, dummyior, dummyior); + const vector3_type H = ggx.__generate(upperHemisphereV, u.xy); + + return __generate_wo_clamps(localV, H, interaction.getFromTangentSpace(), u, orientedEta, 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); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(isocache_type) cache) + { + anisocache_type anisocache; + sample_type s = generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), hlsl::promote(eta)); + const scalar_type orientedEta2 = orientedEta.value[0] * orientedEta.value[0]; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + + scalar_type ndf, devsh_v; + const scalar_type a2 = A.x*A.x; + ndf::SIsotropicParams ndfparams = ndf::SIsotropicParams::create(a2, params.getNdotH(), params.getNdotH2()); + ndf::GGX ggx_ndf; + ndf = ggx_ndf(ndfparams); + + smith::GGX ggx_smith; + devsh_v = ggx_smith.devsh_part(params.getNdotV2(), a2, 1.0-a2); + const scalar_type lambda = ggx_smith.G1_wo_numerator(params.getNdotV(), devsh_v); + + smith::bsdf::VNDF_pdf > vndf = smith::bsdf::VNDF_pdf >::create(ndf, params.getNdotV()); + return vndf(lambda, transmitted, params.getVdotH(), params.getLdotH(), VdotHLdotH, orientedEta.value[0], reflectance); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(params.getVdotH(), hlsl::promote(eta)); + const scalar_type orientedEta2 = orientedEta.value[0] * orientedEta.value[0]; + + const scalar_type VdotHLdotH = params.getVdotH() * params.getLdotH(); + const bool transmitted = VdotHLdotH < 0.0; + + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta2, nbl::hlsl::abs(params.getVdotH())); + + scalar_type ndf, devsh_v; + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + + ndf::SAnisotropicParams ndfparams = ndf::SAnisotropicParams::create(A.x, A.y, ax2, ay2, params.getTdotH2(), params.getBdotH2(), params.getNdotH2()); + ndf::GGX ggx_ndf; + ndf = ggx_ndf(ndfparams); + + smith::GGX ggx_smith; + devsh_v = ggx_smith.devsh_part(params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), ax2, ay2); + const scalar_type lambda = ggx_smith.G1_wo_numerator(params.getNdotV(), devsh_v); + + smith::bsdf::VNDF_pdf > vndf = smith::bsdf::VNDF_pdf >::create(ndf, params.getNdotV()); + return vndf(lambda, transmitted, params.getVdotH(), params.getLdotH(), VdotHLdotH, orientedEta.value[0], reflectance); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + + scalar_type _pdf = pdf(params); + + smith::GGX ggx_smith; + scalar_type quo; + smith::SIsotropicParams smithparams = smith::SIsotropicParams::create(ax2, params.getNdotV(), params.getNdotV2(), params.getNdotL(), params.getNdotL2()); + quo = ggx_smith.G2_over_G1(smithparams); + + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + const scalar_type ax2 = A.x*A.x; + const scalar_type ay2 = A.y*A.y; + + scalar_type _pdf = pdf(params); + + smith::GGX ggx_smith; + scalar_type quo; + smith::SAnisotropicParams smithparams = smith::SAnisotropicParams::create(ax2, ay2, params.getNdotV(), params.getTdotV2(), params.getBdotV2(), params.getNdotV2(), params.getNdotL(), params.getTdotL2(), params.getBdotL2(), params.getNdotL2()); + quo = ggx_smith.G2_over_G1(smithparams); + + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + + vector2_type A; + scalar_type eta; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl new file mode 100644 index 0000000000..1138c2a06c --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl @@ -0,0 +1,166 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_LAMBERTIAN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_LAMBERTIAN_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template +struct LambertianParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic) +struct LambertianParams) > +{ + using this_t = LambertianParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic) +struct LambertianParams) > +{ + using this_t = LambertianParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; + +template && surface_interactions::Isotropic && surface_interactions::Anisotropic) +struct SLambertianBxDF +{ + using this_t = SLambertianBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + + using params_isotropic_t = LambertianParams; + using params_anisotropic_t = LambertianParams; + + + static this_t create() + { + this_t retval; + // nothing here, just keeping convention with others + return retval; + } + + scalar_type __eval_pi_factored_out(scalar_type absNdotL) + { + return absNdotL; + } + + scalar_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return __eval_pi_factored_out(params.getNdotL()) * numbers::inv_pi * 0.5; + } + scalar_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return __eval_pi_factored_out(params.getNdotL()) * numbers::inv_pi * 0.5; + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + ray_dir_info_type L; + L.direction = sampling::ProjectedSphere::generate(u); + return sample_type::createFromTangentSpace(interaction.getTangentSpaceV(), L, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + return generate_wo_clamps(interaction, u); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(vector) u) + { + return generate_wo_clamps(anisotropic_interaction_type::create(interaction), u); + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return sampling::ProjectedSphere::pdf(params.getNdotL()); + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return sampling::ProjectedSphere::pdf(params.getNdotL()); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + sampling::quotient_and_pdf qp = sampling::ProjectedSphere::template quotient_and_pdf(params.getNdotL()); + return quotient_pdf_type::create(hlsl::promote(qp.quotient), qp.pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + sampling::quotient_and_pdf qp = sampling::ProjectedSphere::template quotient_and_pdf(params.getNdotL()); + return quotient_pdf_type::create(hlsl::promote(qp.quotient), qp.pdf); + } +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl new file mode 100644 index 0000000000..482b6220c1 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -0,0 +1,313 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_SMOOTH_DIELECTRIC_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_TRANSMISSION_SMOOTH_DIELECTRIC_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template +struct SmoothDielectricParams; + +template +NBL_PARTIAL_REQ_TOP(!surface_interactions::Anisotropic) +struct SmoothDielectricParams) > +{ + using this_t = SmoothDielectricParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; +template +NBL_PARTIAL_REQ_TOP(surface_interactions::Anisotropic) +struct SmoothDielectricParams) > +{ + using this_t = SmoothDielectricParams; + + static this_t create(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(SI) interaction, BxDFClampMode _clamp) + { + this_t retval; + retval._sample = _sample; + retval.interaction = interaction; + retval._clamp = _clamp; + return retval; + } + + // iso + Scalar getNdotV() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, interaction.getNdotV(), 0.0), interaction.getNdotV(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotVUnclamped() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV(); } + Scalar getNdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getNdotV2(); } + Scalar getNdotL() NBL_CONST_MEMBER_FUNC { return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, _sample.getNdotL(), 0.0), _sample.getNdotL(), _clamp == BxDFClampMode::BCM_NONE); } + Scalar getNdotLUnclamped() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL(); } + Scalar getNdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getNdotL2(); } + Scalar getVdotL() NBL_CONST_MEMBER_FUNC { return _sample.getVdotL(); } + + // aniso + Scalar getTdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getTdotL() * _sample.getTdotL(); } + Scalar getBdotL2() NBL_CONST_MEMBER_FUNC { return _sample.getBdotL() * _sample.getBdotL(); } + Scalar getTdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getTdotV() * interaction.getTdotV(); } + Scalar getBdotV2() NBL_CONST_MEMBER_FUNC { return interaction.getBdotV() * interaction.getBdotV(); } + + LS _sample; + SI interaction; + BxDFClampMode _clamp; +}; + +template // NBL_FUNC_REQUIRES(Sample && IsotropicMicrofacetCache && AnisotropicMicrofacetCache) // dxc won't let me put this in +struct SSmoothDielectricBxDF; + +template +struct SSmoothDielectricBxDF +{ + using this_t = SSmoothDielectricBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector3_type = vector; + using monochrome_type = vector; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + + using params_isotropic_t = SmoothDielectricParams; + using params_anisotropic_t = SmoothDielectricParams; + + + static this_t create(scalar_type eta) + { + this_t retval; + retval.eta = eta; + return retval; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return (spectral_type)0; + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return (spectral_type)0; + } + + sample_type __generate_wo_clamps(NBL_CONST_REF_ARG(vector3_type) V, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N, scalar_type NdotV, scalar_type absNdotV, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, NBL_REF_ARG(bool) transmitted) + { + const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta.value[0]*orientedEta.value[0], absNdotV); + + scalar_type rcpChoiceProb; + transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); + + ray_dir_info_type L; + Refract r = Refract::create(rcpEta, V, N, NdotV); + bxdf::ReflectRefract rr = bxdf::ReflectRefract::create(transmitted, r, orientedEta.rcp[0]); + L.direction = rr(transmitted); + return sample_type::create(L, nbl::hlsl::dot(V, L.direction), T, B, N); + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector) u) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(interaction.isotropic.getNdotV(), hlsl::promote(eta)); + fresnel::OrientedEtaRcps rcpEta = fresnel::OrientedEtaRcps::create(interaction.isotropic.getNdotV(), hlsl::promote(eta)); + scalar_type NdotV = interaction.isotropic.getNdotV(); + bool dummy; + return __generate_wo_clamps(interaction.isotropic.getV().getDirection(), interaction.getT(), interaction.getB(), interaction.isotropic.getN(), NdotV, + NdotV, u, orientedEta, rcpEta, dummy); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector) u) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(interaction.isotropic.getNdotV(), hlsl::promote(eta)); + fresnel::OrientedEtaRcps rcpEta = fresnel::OrientedEtaRcps::create(interaction.isotropic.getNdotV(), hlsl::promote(eta)); + scalar_type NdotV = interaction.isotropic.getNdotV(); + bool dummy; + return __generate_wo_clamps(interaction.isotropic.getV().getDirection(), interaction.getT(), interaction.getB(), interaction.isotropic.getN(), NdotV, + nbl::hlsl::abs(NdotV), u, orientedEta, rcpEta, dummy); + } + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector) u) + { + return generate(anisotropic_interaction_type::create(interaction), u); + } + + // eval and pdf return 0 because smooth dielectric/conductor BxDFs are dirac delta distributions, model perfectly specular objects that scatter light to only one outgoing direction + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return 0; + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return 0; + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(params.getNdotVUnclamped(), params.getNdotLUnclamped()); + + fresnel::OrientedEtaRcps rcpOrientedEtas = fresnel::OrientedEtaRcps::create(params.getNdotV(), hlsl::promote(eta)); + + const scalar_type _pdf = bit_cast(numeric_limits::infinity); + scalar_type quo = hlsl::mix(1.0, rcpOrientedEtas.value[0], transmitted); + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(params.getNdotVUnclamped(), params.getNdotLUnclamped()); + + fresnel::OrientedEtaRcps rcpOrientedEtas = fresnel::OrientedEtaRcps::create(params.getNdotV(), hlsl::promote(eta)); + + const scalar_type _pdf = bit_cast(numeric_limits::infinity); + scalar_type quo = hlsl::mix(1.0, rcpOrientedEtas.value[0], transmitted); + return quotient_pdf_type::create((spectral_type)(quo), _pdf); + } + + scalar_type eta; +}; + +template +struct SSmoothDielectricBxDF +{ + using this_t = SSmoothDielectricBxDF; + using scalar_type = typename LS::scalar_type; + using ray_dir_info_type = typename LS::ray_dir_info_type; + using vector3_type = vector; + using monochrome_type = vector; + + using isotropic_interaction_type = Iso; + using anisotropic_interaction_type = Aniso; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; + using isocache_type = IsoCache; + using anisocache_type = AnisoCache; + + using params_isotropic_t = SmoothDielectricParams; + using params_anisotropic_t = SmoothDielectricParams; + + + static this_t create(NBL_CONST_REF_ARG(spectral_type) eta2, NBL_CONST_REF_ARG(spectral_type) luminosityContributionHint) + { + this_t retval; + retval.eta2 = eta2; + retval.luminosityContributionHint = luminosityContributionHint; + return retval; + } + + spectral_type eval(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return (spectral_type)0; + } + spectral_type eval(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return (spectral_type)0; + } + + // usually `luminosityContributionHint` would be the Rec.709 luma coefficients (the Y row of the RGB to CIE XYZ matrix) + // its basically a set of weights that determine + // assert(1.0==luminosityContributionHint.r+luminosityContributionHint.g+luminosityContributionHint.b); + // `remainderMetadata` is a variable which the generator function returns byproducts of sample generation that would otherwise have to be redundantly calculated `quotient_and_pdf` + sample_type __generate_wo_clamps(NBL_CONST_REF_ARG(vector3_type) V, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N, scalar_type NdotV, scalar_type absNdotV, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(spectral_type) eta2, NBL_CONST_REF_ARG(spectral_type) luminosityContributionHint, NBL_REF_ARG(spectral_type) remainderMetadata) + { + // we will only ever intersect from the outside + const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel::Dielectric::__call(eta2,absNdotV)); + + // we are only allowed one choice for the entire ray, so make the probability a weighted sum + const scalar_type reflectionProb = nbl::hlsl::dot(reflectance, luminosityContributionHint); + + scalar_type rcpChoiceProb; + const bool transmitted = math::partitionRandVariable(reflectionProb, u.z, rcpChoiceProb); + remainderMetadata = (transmitted ? ((spectral_type)(1.0) - reflectance) : reflectance) * rcpChoiceProb; + + ray_dir_info_type L; + L.direction = (transmitted ? (vector3_type)(0.0) : N * 2.0f * NdotV) - V; + return sample_type::create(L, nbl::hlsl::dot(V, L.direction), T, B, N); + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector) u) + { + scalar_type NdotV = interaction.isotropic.getNdotV(); + vector3_type dummy; + return __generate_wo_clamps(interaction.isotropic.getV().getDirection(), interaction.getT(), interaction.getB(), interaction.isotropic.getN(), NdotV, NdotV, u, eta2, luminosityContributionHint, dummy); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector) u) + { + scalar_type NdotV = interaction.isotropic.getNdotV(); + vector3_type dummy; + return __generate_wo_clamps(interaction.isotropic.getV().getDirection(), interaction.getT(), interaction.getB(), interaction.isotropic.getN(), NdotV, nbl::hlsl::abs(NdotV), u, eta2, luminosityContributionHint, dummy); + } + + scalar_type pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + return 0; + } + scalar_type pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + return 0; + } + + // isotropic only? + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_isotropic_t) params) + { + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(params.getNdotVUnclamped(), params.getNdotLUnclamped()); + const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel::Dielectric::__call(eta2, params.getNdotV())); + const spectral_type sampleValue = hlsl::mix(reflectance, (spectral_type)(1.0) - reflectance, transmitted); + + const scalar_type sampleProb = nbl::hlsl::dot(sampleValue,luminosityContributionHint); + + const scalar_type _pdf = bit_cast(numeric_limits::infinity); + return quotient_pdf_type::create((spectral_type)(sampleValue / sampleProb), _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(params_anisotropic_t) params) + { + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(params.getNdotVUnclamped(), params.getNdotLUnclamped()); + const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel::Dielectric::__call(eta2, params.getNdotV())); + const spectral_type sampleValue = hlsl::mix(reflectance, (spectral_type)(1.0) - reflectance, transmitted); + + const scalar_type sampleProb = nbl::hlsl::dot(sampleValue,luminosityContributionHint); + + const scalar_type _pdf = bit_cast(numeric_limits::infinity); + return quotient_pdf_type::create((spectral_type)(sampleValue / sampleProb), _pdf); + } + + spectral_type eta2; + spectral_type luminosityContributionHint; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/concepts.hlsl b/include/nbl/builtin/hlsl/concepts.hlsl index 7fd725dc2b..4b82955bb7 100644 --- a/include/nbl/builtin/hlsl/concepts.hlsl +++ b/include/nbl/builtin/hlsl/concepts.hlsl @@ -33,6 +33,7 @@ namespace concepts #define NBL_CONCEPT_REQ_EXPR 1 // #define NBL_CONCEPT_REQ_EXPR_RET_TYPE 2 +#define NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT 3 //! Now diverge @@ -64,8 +65,9 @@ concept NBL_CONCEPT_NAME = requires BOOST_PP_EXPR_IF(LOCAL_PARAM_COUNT,(BOOST_PP #define NBL_IMPL_CONCEPT_REQ_TYPE(...) typename __VA_ARGS__; #define NBL_IMPL_CONCEPT_REQ_EXPR(...) __VA_ARGS__; #define NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE(E,C,...) {E}; C; +#define NBL_IMPL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT(C,...) C< __VA_ARGS__ >; // -#define NBL_IMPL_CONCEPT (NBL_IMPL_CONCEPT_REQ_TYPE,NBL_IMPL_CONCEPT_REQ_EXPR,NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE) +#define NBL_IMPL_CONCEPT (NBL_IMPL_CONCEPT_REQ_TYPE,NBL_IMPL_CONCEPT_REQ_EXPR,NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE,NBL_IMPL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT) // #define NBL_IMPL_CONCEPT_END_DEF(r,unused,i,e) NBL_EVAL(BOOST_PP_TUPLE_ELEM(BOOST_PP_SEQ_HEAD(e),NBL_IMPL_CONCEPT) BOOST_PP_SEQ_TAIL(e)) // @@ -95,8 +97,9 @@ concept NBL_CONCEPT_NAME = requires BOOST_PP_EXPR_IF(LOCAL_PARAM_COUNT,(BOOST_PP #define NBL_IMPL_CONCEPT_REQ_TYPE(...) ::nbl::hlsl::make_void_t #define NBL_IMPL_CONCEPT_REQ_EXPR(...) ::nbl::hlsl::make_void_t #define NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE(E,C,...) ::nbl::hlsl::enable_if_t > +#define NBL_IMPL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT(C,...) ::nbl::hlsl::enable_if_t > // -#define NBL_IMPL_CONCEPT_SFINAE (NBL_IMPL_CONCEPT_REQ_TYPE,NBL_IMPL_CONCEPT_REQ_EXPR,NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE) +#define NBL_IMPL_CONCEPT_SFINAE (NBL_IMPL_CONCEPT_REQ_TYPE,NBL_IMPL_CONCEPT_REQ_EXPR,NBL_IMPL_CONCEPT_REQ_EXPR_RET_TYPE,NBL_IMPL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT) // #define NBL_IMPL_CONCEPT_END_DEF(r,unused,i,e) template \ struct BOOST_PP_CAT(__requirement,i) : ::nbl::hlsl::false_type {}; \ diff --git a/include/nbl/builtin/hlsl/concepts/core.hlsl b/include/nbl/builtin/hlsl/concepts/core.hlsl index c1bc0277df..dcbafae8a5 100644 --- a/include/nbl/builtin/hlsl/concepts/core.hlsl +++ b/include/nbl/builtin/hlsl/concepts/core.hlsl @@ -47,7 +47,7 @@ template NBL_BOOL_CONCEPT UnsignedIntegralScalar = !nbl::hlsl::is_signed_v && ::nbl::hlsl::is_integral_v && nbl::hlsl::is_scalar_v; template -NBL_BOOL_CONCEPT FloatingPointScalar = (nbl::hlsl::is_floating_point_v && nbl::hlsl::is_scalar_v); +NBL_BOOL_CONCEPT FloatingPointScalar = nbl::hlsl::is_floating_point_v && nbl::hlsl::is_scalar_v; template NBL_BOOL_CONCEPT BooleanScalar = concepts::Boolean && nbl::hlsl::is_scalar_v; diff --git a/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl b/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl index f92dddfb26..0c4c4a4b69 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl @@ -141,8 +141,8 @@ template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(length_helper, length, template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(normalize_helper, normalize, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(rsqrt_helper, inverseSqrt, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(fract_helper, fract, (T), (T), T) -template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(all_helper, any, (T), (T), T) -template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(any_helper, any, (T), (T), T) +template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(all_helper, any, (T), (T), bool) +template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(any_helper, any, (T), (T), bool) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(sign_helper, fSign, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(sign_helper, sSign, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(radians_helper, radians, (T), (T), T) @@ -241,8 +241,8 @@ struct mix_helper(e }; template -NBL_PARTIAL_REQ_TOP((concepts::Scalar || concepts::Vectorial) && !concepts::Boolean && concepts::Boolean) -struct mix_helper || concepts::Vectorial) && !concepts::Boolean && concepts::Boolean) > +NBL_PARTIAL_REQ_TOP(concepts::Boolean && ((concepts::Vector && concepts::Vector && vector_traits::Dimension==vector_traits::Dimension) || concepts::Scalar)) +struct mix_helper && ((concepts::Vector && concepts::Vector && vector_traits::Dimension==vector_traits::Dimension) || concepts::Scalar)) > { using return_t = conditional_t, vector::scalar_type, vector_traits::Dimension>, T>; // for a component of a that is false, the corresponding component of x is returned @@ -866,6 +866,25 @@ struct mix_helper } }; +template +NBL_PARTIAL_REQ_TOP(VECTOR_SPECIALIZATION_CONCEPT&& concepts::Boolean&& !(vector_traits::Dimension == vector_traits::Dimension) && concepts::BooleanScalar) +struct mix_helper&& !(vector_traits::Dimension == vector_traits::Dimension) && concepts::BooleanScalar) > +{ + using return_t = T; + static return_t __call(NBL_CONST_REF_ARG(T) x, NBL_CONST_REF_ARG(T) y, NBL_CONST_REF_ARG(U) a) + { + using traitsT = hlsl::vector_traits; + array_get getterT; + array_set setter; + + return_t output; + for (uint32_t i = 0; i < traitsT::Dimension; ++i) + setter(output, i, mix_helper::__call(getterT(x, i), getterT(y, i), a)); + + return output; + } +}; + template NBL_PARTIAL_REQ_TOP(VECTOR_SPECIALIZATION_CONCEPT && concepts::Boolean && vector_traits::Dimension == vector_traits::Dimension) struct mix_helper && vector_traits::Dimension == vector_traits::Dimension) > diff --git a/include/nbl/builtin/hlsl/glsl_compat/core.hlsl b/include/nbl/builtin/hlsl/glsl_compat/core.hlsl index 337db5e22f..88d2f4b7a6 100644 --- a/include/nbl/builtin/hlsl/glsl_compat/core.hlsl +++ b/include/nbl/builtin/hlsl/glsl_compat/core.hlsl @@ -8,6 +8,9 @@ #include "nbl/builtin/hlsl/spirv_intrinsics/core.hlsl" #include "nbl/builtin/hlsl/type_traits.hlsl" #include "nbl/builtin/hlsl/spirv_intrinsics/glsl.std.450.hlsl" +#include "nbl/builtin/hlsl/concepts/core.hlsl" +#include "nbl/builtin/hlsl/concepts/vector.hlsl" +#include "nbl/builtin/hlsl/concepts/matrix.hlsl" namespace nbl { @@ -234,6 +237,68 @@ T bitfieldReverse(T value) #endif +namespace impl +{ +template +struct equal_helper; + +#ifdef __HLSL_VERSION + +template +NBL_PARTIAL_REQ_TOP(concepts::Vectorial && concepts::Integral) +struct equal_helper && concepts::Integral) > +{ + using return_t = vector::Dimension>; + + static return_t __call(const Vectorial lhs, const Vectorial rhs) + { + return spirv::IEqual(lhs, rhs); + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::Vectorial && concepts::FloatingPoint) +struct equal_helper && concepts::FloatingPoint) > +{ + using return_t = vector::Dimension>; + + static return_t __call(const Vectorial lhs, const Vectorial rhs) + { + return spirv::FOrdEqual(lhs, rhs); + } +}; + +#else + +template +NBL_PARTIAL_REQ_TOP(concepts::Vectorial) +struct equal_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 +} + +template +inline vector::Dimension> equal(NBL_CONST_REF_ARG(T) x, NBL_CONST_REF_ARG(T) y) +{ + return impl::equal_helper::__call(x, y); +} + } } } diff --git a/include/nbl/builtin/hlsl/ieee754.hlsl b/include/nbl/builtin/hlsl/ieee754.hlsl index 4b281c2111..a32ba589f2 100644 --- a/include/nbl/builtin/hlsl/ieee754.hlsl +++ b/include/nbl/builtin/hlsl/ieee754.hlsl @@ -142,13 +142,67 @@ NBL_CONSTEXPR_INLINE_FUNC FloatingPoint copySign(FloatingPoint to, FloatingPoint return bit_cast(toAsUint | extractSignPreserveBitPattern(from)); } -template ) -NBL_CONSTEXPR_INLINE_FUNC FloatingPoint flipSign(FloatingPoint val, bool flip = true) +namespace impl +{ +template +struct flipSign_helper; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeScalar && concepts::BooleanScalar) +struct flipSign_helper && concepts::BooleanScalar) > +{ + static FloatingPoint __call(FloatingPoint val, Bool 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 ^ (flip ? ieee754::traits::signMask : AsUint(0ull))); + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial && concepts::BooleanScalar) +struct flipSign_helper && concepts::BooleanScalar) > { - 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 ^ (flip ? ieee754::traits::signMask : AsUint(0ull))); + static Vectorial __call(Vectorial val, Bool flip) + { + using traits = hlsl::vector_traits; + array_get getter; + array_set setter; + + Vectorial output; + for (uint32_t i = 0; i < traits::Dimension; ++i) + setter(output, i, flipSign_helper::__call(getter(val, i), flip)); + + return output; + } +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial && concepts::Boolean && !concepts::Scalar && vector_traits::Dimension==vector_traits::Dimension) +struct flipSign_helper && concepts::Boolean && !concepts::Scalar && vector_traits::Dimension==vector_traits::Dimension) > +{ + static Vectorial __call(Vectorial val, BoolVector flip) + { + using traits_v = hlsl::vector_traits; + using traits_f = hlsl::vector_traits; + array_get getter_v; + array_get getter_f; + 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_f(flip, i))); + + return output; + } +}; +} + +template +NBL_CONSTEXPR_INLINE_FUNC T flipSign(T val, U flip) +{ + return impl::flipSign_helper::__call(val, flip); } } diff --git a/include/nbl/builtin/hlsl/limits.hlsl b/include/nbl/builtin/hlsl/limits.hlsl index 146957dc3e..2cb175a5fd 100644 --- a/include/nbl/builtin/hlsl/limits.hlsl +++ b/include/nbl/builtin/hlsl/limits.hlsl @@ -129,7 +129,7 @@ struct num_base : type_identity NBL_CONSTEXPR_STATIC_INLINE int32_t float_max_decimal_exponent = 4*S16 + 30*S32 + 232*S64; NBL_CONSTEXPR_STATIC_INLINE int32_t float_exponent_bits = 8 * size - 1 - (float_digits-1); - NBL_CONSTEXPR_STATIC_INLINE int32_t float_max_exponent = 1 << (float_exponent_bits-1); + NBL_CONSTEXPR_STATIC_INLINE int32_t float_max_exponent = int32_t(1) << (float_exponent_bits-1); NBL_CONSTEXPR_STATIC_INLINE int32_t float_min_exponent = 3 - float_max_exponent; NBL_CONSTEXPR_STATIC_INLINE bool is_bool = is_same::value; diff --git a/include/nbl/builtin/hlsl/math/functions.hlsl b/include/nbl/builtin/hlsl/math/functions.hlsl index be341b6a12..f7a84005e8 100644 --- a/include/nbl/builtin/hlsl/math/functions.hlsl +++ b/include/nbl/builtin/hlsl/math/functions.hlsl @@ -163,7 +163,7 @@ struct conditionalAbsOrMax_helper::scalar_type)>; + using UintOfTSize = unsigned_integer_of_size_t::scalar_type)>; const int dimensionOfT = vector_traits::Dimension; using Uint32VectorWithDimensionOfT = vector; using scalar_type = typename vector_traits::scalar_type; diff --git a/include/nbl/builtin/hlsl/random/dim_adaptor_recursive.hlsl b/include/nbl/builtin/hlsl/random/dim_adaptor_recursive.hlsl new file mode 100644 index 0000000000..8ec882de78 --- /dev/null +++ b/include/nbl/builtin/hlsl/random/dim_adaptor_recursive.hlsl @@ -0,0 +1,47 @@ +#ifndef _NBL_HLSL_RANDOM_DIM_ADAPTOR_RECURSIVE_INCLUDED_ +#define _NBL_HLSL_RANDOM_DIM_ADAPTOR_RECURSIVE_INCLUDED_ + +#include "nbl/builtin/hlsl/type_traits.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace random +{ + +// adapts PRNG for multiple dimensions using recursive calls, rather than hash based +template +struct DimAdaptorRecursive +{ + using rng_type = RNG; + using return_type = vector; + + static DimAdaptorRecursive construct(rng_type rng) + { + DimAdaptorRecursive retval; + retval.rng = rng; + return retval; + } + + return_type operator()() + { + array_set setter; + + return_type retval; +#ifdef __HLSL_VERSION + [unroll] +#endif + for (uint32_t i = 0; i < DIM; i++) + setter(retval, i, rng()); + return retval; + } + + rng_type rng; +}; + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/random/lcg.hlsl b/include/nbl/builtin/hlsl/random/lcg.hlsl index 046161bc6b..0ac0d809aa 100644 --- a/include/nbl/builtin/hlsl/random/lcg.hlsl +++ b/include/nbl/builtin/hlsl/random/lcg.hlsl @@ -13,13 +13,15 @@ namespace random struct Lcg { - static Lcg create(const uint32_t _state) + using seed_type = uint32_t; + + static Lcg create(NBL_CONST_REF_ARG(seed_type) _state) { Lcg retval; retval.state = _state; return retval; } - + uint32_t operator()() { uint32_t LCG_A = 1664525u; @@ -30,7 +32,7 @@ struct Lcg return state; } - uint32_t state; + seed_type state; }; } diff --git a/include/nbl/builtin/hlsl/random/pcg.hlsl b/include/nbl/builtin/hlsl/random/pcg.hlsl index 2102b8702c..2b881e29a2 100644 --- a/include/nbl/builtin/hlsl/random/pcg.hlsl +++ b/include/nbl/builtin/hlsl/random/pcg.hlsl @@ -11,24 +11,28 @@ namespace hlsl namespace random { -struct Pcg +struct PCG32 { - static Pcg create(const uint32_t initialState) - { - Pcg retval; - retval.state = initialState; - return retval; - } - - uint32_t operator()() - { - const uint32_t tmp = state * 747796405u + 2891336453u; - const uint32_t word = ((tmp >> ((tmp >> 28u) + 4u)) ^ tmp) * 277803737u; - state = (word >> 22u) ^ word; - return state; + using seed_type = uint32_t; + + static PCG32 construct(NBL_CONST_REF_ARG(seed_type) initialState) + { + PCG32 retval; + retval.state = initialState; + return retval; } - - uint32_t state; + + uint32_t operator()() + { + const seed_type oldState = state; + state = state * 747796405u + 2891336453u; + const uint32_t word = ((oldState >> ((oldState >> 28u) + 4u)) ^ oldState) * 277803737u; + const uint32_t result = (word >> 22u) ^ word; + + return result; + } + + seed_type state; }; } diff --git a/include/nbl/builtin/hlsl/random/xoroshiro.hlsl b/include/nbl/builtin/hlsl/random/xoroshiro.hlsl index 577b89bb95..1a150f1909 100644 --- a/include/nbl/builtin/hlsl/random/xoroshiro.hlsl +++ b/include/nbl/builtin/hlsl/random/xoroshiro.hlsl @@ -1,16 +1,16 @@ -// Copyright (C) 2018-2020 - 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_RANDOM_XOROSHIRO_HLSL_INCLUDED_ -#define _NBL_BUILTIN_HLSL_RANDOM_XOROSHIRO_HLSL_INCLUDED_ - -#include - -#include - -namespace nbl -{ -namespace hlsl +// Copyright (C) 2018-2020 - 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_RANDOM_XOROSHIRO_HLSL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_RANDOM_XOROSHIRO_HLSL_INCLUDED_ + +#include + +#include + +namespace nbl +{ +namespace hlsl { // TODO //namespace random @@ -18,58 +18,62 @@ namespace hlsl struct Xoroshiro64StateHolder { - void xoroshiro64_state_advance() - { - state[1] ^= state[0]; - state[0] = rotl(state[0], 26u) ^ state[1] ^ (state[1]<<9u); // a, b - state[1] = rotl(state[1], 13u); // c + void xoroshiro64_state_advance() + { + state[1] ^= state[0]; + state[0] = rotl(state[0], 26u) ^ state[1] ^ (state[1]<<9u); // a, b + state[1] = rotl(state[1], 13u); // c } - + uint32_t2 state; -}; - +}; + struct Xoroshiro64Star { + using seed_type = uint32_t2; + // TODO: create - static Xoroshiro64Star construct(NBL_CONST_REF_ARG(uint32_t2) initialState) + static Xoroshiro64Star construct(NBL_CONST_REF_ARG(seed_type) initialState) { Xoroshiro64StateHolder stateHolder = {initialState}; return Xoroshiro64Star(stateHolder); } - + uint32_t operator()() { - const uint32_t result = stateHolder.state[0]*0x9E3779BBu; - stateHolder.xoroshiro64_state_advance(); - + const uint32_t result = stateHolder.state[0]*0x9E3779BBu; + stateHolder.xoroshiro64_state_advance(); + return result; } - + Xoroshiro64StateHolder stateHolder; -}; - +}; + struct Xoroshiro64StarStar { + using seed_type = uint32_t2; + // TODO: create - static Xoroshiro64StarStar construct(NBL_CONST_REF_ARG(uint32_t2) initialState) + static Xoroshiro64StarStar construct(NBL_CONST_REF_ARG(seed_type) initialState) { Xoroshiro64StateHolder stateHolder = {initialState}; return Xoroshiro64StarStar(stateHolder); } - + uint32_t operator()() { - const uint32_t result = rotl(stateHolder.state[0]*0x9E3779BBu,5u)*5u; - stateHolder.xoroshiro64_state_advance(); - + const uint32_t result = rotl(stateHolder.state[0]*0x9E3779BBu,5u)*5u; + stateHolder.xoroshiro64_state_advance(); + return result; } Xoroshiro64StateHolder stateHolder; -}; - -//} -} -} - +}; + +//} +} +} + #endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl new file mode 100644 index 0000000000..1a5c96b6df --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/concentric_mapping.hlsl @@ -0,0 +1,50 @@ +// 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_SAMPLING_CONCENTRIC_MAPPING_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_CONCENTRIC_MAPPING_INCLUDED_ + +#include "nbl/builtin/hlsl/glsl_compat/core.hlsl" +#include "nbl/builtin/hlsl/tgmath.hlsl" +#include "nbl/builtin/hlsl/math/functions.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +vector concentricMapping(vector _u) +{ + //map [0;1]^2 to [-1;1]^2 + vector u = 2.0f * _u - hlsl::promote >(1.0); + + vector p; + if (hlsl::all >(glsl::equal(u, hlsl::promote >(0.0)))) + p = hlsl::promote >(0.0); + else + { + T r; + T theta; + if (abs(u.x) > abs(u.y)) { + r = u.x; + theta = 0.25 * numbers::pi * (u.y / u.x); + } else { + r = u.y; + theta = 0.5 * numbers::pi - 0.25 * numbers::pi * (u.x / u.y); + } + + p = r * vector(cos(theta), sin(theta)); + } + + return p; +} + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/cos_weighted.hlsl b/include/nbl/builtin/hlsl/sampling/cos_weighted.hlsl new file mode 100644 index 0000000000..d80778ed9a --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/cos_weighted.hlsl @@ -0,0 +1,90 @@ +// 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_SAMPLING_COS_WEIGHTED_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_COS_WEIGHTED_INCLUDED_ + +#include "nbl/builtin/hlsl/concepts.hlsl" +#include "nbl/builtin/hlsl/sampling/concentric_mapping.hlsl" +#include "nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template) +struct ProjectedHemisphere +{ + using vector_t2 = vector; + using vector_t3 = vector; + + static vector_t3 generate(vector_t2 _sample) + { + vector_t2 p = concentricMapping(_sample * T(0.99999) + T(0.000005)); + T z = hlsl::sqrt(hlsl::max(T(0.0), T(1.0) - p.x * p.x - p.y * p.y)); + return vector_t3(p.x, p.y, z); + } + + static T pdf(T L_z) + { + return L_z * numbers::inv_pi; + } + + template) + static sampling::quotient_and_pdf quotient_and_pdf(T L) + { + return sampling::quotient_and_pdf::create(U(1.0), pdf(L)); + } + + template) + static sampling::quotient_and_pdf quotient_and_pdf(vector_t3 L) + { + return sampling::quotient_and_pdf::create(U(1.0), pdf(L.z)); + } +}; + +template) +struct ProjectedSphere +{ + using vector_t2 = vector; + using vector_t3 = vector; + using hemisphere_t = ProjectedHemisphere; + + static vector_t3 generate(vector_t3 _sample) + { + vector_t3 retval = hemisphere_t::generate(_sample.xy); + const bool chooseLower = _sample.z > T(0.5); + retval.z = chooseLower ? (-retval.z) : retval.z; + if (chooseLower) + _sample.z -= T(0.5); + _sample.z *= T(2.0); + return retval; + } + + static T pdf(T L_z) + { + return T(0.5) * hemisphere_t::pdf(L_z); + } + + template) + static sampling::quotient_and_pdf quotient_and_pdf(T L) + { + return sampling::quotient_and_pdf::create(U(1.0), pdf(L)); + } + + template) + static sampling::quotient_and_pdf quotient_and_pdf(vector_t3 L) + { + return sampling::quotient_and_pdf::create(U(1.0), pdf(L.z)); + } +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl b/include/nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl new file mode 100644 index 0000000000..0620b2bad6 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl @@ -0,0 +1,50 @@ +// 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_SAMPLING_QUOTIENT_AND_PDF_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_QUOTIENT_AND_PDF_INCLUDED_ + +#include "nbl/builtin/hlsl/concepts/vector.hlsl" +#include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +NBL_BOOL_CONCEPT Spectral = (concepts::Vectorial || concepts::Scalar) && is_floating_point_v::scalar_type>; + +// finally fixed the semantic F-up, value/pdf = quotient not remainder +template && is_floating_point_v

) +struct quotient_and_pdf +{ + using this_t = quotient_and_pdf; + static this_t create(NBL_CONST_REF_ARG(Q) _quotient, NBL_CONST_REF_ARG(P) _pdf) + { + this_t retval; + retval.quotient = _quotient; + retval.pdf = _pdf; + return retval; + } + + Q value() + { + return quotient*pdf; + } + + Q quotient; + P pdf; +}; + +typedef quotient_and_pdf quotient_and_pdf_scalar; +typedef quotient_and_pdf, float32_t> quotient_and_pdf_rgb; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/uniform.hlsl b/include/nbl/builtin/hlsl/sampling/uniform.hlsl new file mode 100644 index 0000000000..69a83d5a93 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/uniform.hlsl @@ -0,0 +1,76 @@ +// 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_SAMPLING_UNIFORM_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_UNIFORM_INCLUDED_ + +#include "nbl/builtin/hlsl/concepts.hlsl" +#include "nbl/builtin/hlsl/numbers.hlsl" +#include "nbl/builtin/hlsl/tgmath.hlsl" +#include "nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template) +struct UniformHemisphere +{ + using vector_t2 = vector; + using vector_t3 = vector; + + static vector_t3 generate(vector_t2 _sample) + { + T z = _sample.x; + T r = hlsl::sqrt(hlsl::max(T(0.0), T(1.0) - z * z)); + T phi = T(2.0) * numbers::pi * _sample.y; + return vector_t3(r * hlsl::cos(phi), r * hlsl::sin(phi), z); + } + + static T pdf() + { + return T(1.0) / (T(2.0) * numbers::pi); + } + + template) + static quotient_and_pdf quotient_and_pdf() + { + return quotient_and_pdf::create(U(1.0), pdf()); + } +}; + +template) +struct UniformSphere +{ + using vector_t2 = vector; + using vector_t3 = vector; + + static vector_t3 generate(vector_t2 _sample) + { + T z = T(1.0) - T(2.0) * _sample.x; + T r = hlsl::sqrt(hlsl::max(T(0.0), T(1.0) - z * z)); + T phi = T(2.0) * numbers::pi * _sample.y; + return vector_t3(r * hlsl::cos(phi), r * hlsl::sin(phi), z); + } + + static T pdf() + { + return T(1.0) / (T(4.0) * numbers::pi); + } + + template) + static quotient_and_pdf quotient_and_pdf() + { + return quotient_and_pdf::create(U(1.0), pdf()); + } +}; +} + +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl index 4885fc11f8..88941b8d6d 100644 --- a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl +++ b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl @@ -332,11 +332,11 @@ enable_if_t, Integral> bitCount(Integral mat); template [[vk::ext_instruction(spv::OpAll)]] -enable_if_t && is_same_v::scalar_type, bool>, BooleanVector> all(BooleanVector vec); +enable_if_t && is_same_v::scalar_type, bool>, bool> all(BooleanVector vec); template [[vk::ext_instruction(spv::OpAny)]] -enable_if_t&& is_same_v::scalar_type, bool>, BooleanVector> any(BooleanVector vec); +enable_if_t&& is_same_v::scalar_type, bool>, bool> any(BooleanVector vec); template) [[vk::ext_instruction(spv::OpIAddCarry)]] @@ -356,7 +356,7 @@ template && !is_matrix_v) conditional_t, vector::Dimension>, bool> FOrdEqual(T lhs, T rhs); -template && !is_matrix_v && is_same_v::scalar_type, bool>) +template && (is_scalar_v || (is_vector_v && is_vector_v && vector_traits::Dimension==vector_traits::Dimension)) && is_same_v::scalar_type, bool>) [[vk::ext_instruction(spv::OpSelect)]] T select(U a, T x, T y); diff --git a/include/nbl/builtin/hlsl/type_traits.hlsl b/include/nbl/builtin/hlsl/type_traits.hlsl index a6f9ad0655..2bad563829 100644 --- a/include/nbl/builtin/hlsl/type_traits.hlsl +++ b/include/nbl/builtin/hlsl/type_traits.hlsl @@ -403,27 +403,6 @@ struct is_compound : bool_constant::value> {}; template struct is_aggregate : is_compound {}; -template -struct rank : integral_constant { }; - -template -struct rank : integral_constant::value> { }; - -template -struct rank : integral_constant::value> { }; - -template -struct extent : integral_constant {}; - -template -struct extent : integral_constant {}; - -template -struct extent : integral_constant::value> {}; - -template -struct extent : integral_constant::value> {}; - template struct enable_if {}; @@ -663,7 +642,7 @@ template using conditional_t = typename conditional::type; -// Template Variables +// Template variables template NBL_CONSTEXPR bool is_same_v = is_same::value; template @@ -680,9 +659,6 @@ template NBL_CONSTEXPR uint64_t size_of_v = size_of::value; template NBL_CONSTEXPR uint32_t alignment_of_v = alignment_of::value; -template -NBL_CONSTEXPR uint64_t extent_v = extent::value; - // Overlapping definitions template @@ -718,6 +694,53 @@ template NBL_CONSTEXPR bool is_matrix_v = is_matrix::value; +#ifdef __HLSL_VERSION +template +struct rank : integral_constant, + uint64_t, + 2ull, + conditional_value< + is_vector_v, + uint64_t, + 1ull, + 0ull + >::value + >::value +> { }; + +template +struct rank : integral_constant::value> { }; + +template +struct rank : integral_constant::value> { }; + +template +struct extent : integral_constant {}; + +template +struct extent : integral_constant {}; + +template +struct extent : integral_constant::value> {}; + +template +struct extent : integral_constant::value> {}; + +template +struct extent, 0> : integral_constant {}; + +template +struct extent, I> : integral_constant::value> {}; +#endif + + +// Template Variables +template +NBL_CONSTEXPR uint64_t extent_v = extent::value; + + template::value> struct scalar_type { diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index fcbe58eb41..523491dfdb 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -316,6 +316,10 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/circle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/ellipse.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/line.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/beziers.hlsl") +#sampling +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/concentric_mapping.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/cos_weighted.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/uniform.hlsl") # LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/ndarray_addressing.hlsl") # @@ -325,6 +329,14 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/fft/common.hlsl") #sort LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sort/common.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sort/counting.hlsl") +#bxdf +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/geom_smith.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/ndf.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") #subgroup LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/subgroup/ballot.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/subgroup/basic.hlsl")