diff --git a/examples_tests b/examples_tests index 3de2363c2a..ec19f5df12 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 3de2363c2a91dbe6305cfe43117685a1d2842ffb +Subproject commit ec19f5df1268ae9e00333a6d930dee21c5579377 diff --git a/include/nbl/asset/utils/CHLSLCompiler.h b/include/nbl/asset/utils/CHLSLCompiler.h index e5fca33815..92a1dca394 100644 --- a/include/nbl/asset/utils/CHLSLCompiler.h +++ b/include/nbl/asset/utils/CHLSLCompiler.h @@ -146,6 +146,7 @@ class NBL_API2 CHLSLCompiler final : public IShaderCompiler L"-Wno-c++1z-extensions", L"-Wno-c++14-extensions", L"-Wno-gnu-static-float-init", + L"-Wno-local-type-template-args", L"-HV", L"202x" }); }; 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..e57bb13837 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl @@ -0,0 +1,32 @@ +// 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" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ + +enum BxDFType : uint16_t +{ + BT_BSDF = 0, + BT_BRDF, + BT_BTDF +}; + +template +struct traits; + +// defined individually after each bxdf definition + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index c6e8679d3b..b91319d6e0 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,400 +6,936 @@ #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 { - 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 (rcpEta, 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) +NBL_CONCEPT_BEGIN(6) +#define rdirinfo NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define N NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define rcpEta NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define m NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define rfl NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +#define rfr NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 +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, rcpEta)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.transform(m)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(is_scalar_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(is_vector_v, typename T::vector3_type)) +); +#undef rfr +#undef rfl +#undef m +#undef rcpEta +#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, scalar_type rcpOrientedEta) + { + SBasic retval; + retval.direction = r(rcpOrientedEta); + return retval; + } + + // WARNING: matrix must be orthonormal + SBasic transform(const 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! } +enum BxDFClampMode : uint16_t +{ + BCM_NONE = 0, + BCM_MAX, + BCM_ABS +}; + +template) +T conditionalAbsOrMax(T x, BxDFClampMode _clamp) +{ + return hlsl::mix(math::conditionalAbsOrMax(_clamp == BxDFClampMode::BCM_ABS, x, 0.0), x, _clamp == BxDFClampMode::BCM_NONE); +} 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) +#define NBL_CONCEPT_PARAM_3 (clampMode, BxDFClampMode) +NBL_CONCEPT_BEGIN(4) +#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 +#define clampMode NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +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(clampMode)), ::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 clampMode +#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, const 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(BxDFClampMode _clamp = BxDFClampMode::BCM_NONE) NBL_CONST_MEMBER_FUNC + { + return bxdf::conditionalAbsOrMax(NdotV, _clamp); + } + 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.getTdotV2()), ::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)((aniso.getBdotV2()), ::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, + const vector3_type normalizedT, + const 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, const 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, const 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(BxDFClampMode _clamp = BxDFClampMode::BCM_NONE) NBL_CONST_MEMBER_FUNC { return isotropic.getNdotV(_clamp); } + 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 getTdotV2() NBL_CONST_MEMBER_FUNC { const scalar_type t = getTdotV(); return t*t; } + scalar_type getBdotV() NBL_CONST_MEMBER_FUNC { return BdotV; } + scalar_type getBdotV2() NBL_CONST_MEMBER_FUNC { const scalar_type t = getBdotV(); return t*t; } + + 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 (clampMode, BxDFClampMode) +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 clampMode 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.getTdotL()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getTdotL2()), ::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.getBdotL2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getNdotL(clampMode)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((_sample.getNdotL2()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createFromTangentSpace(rdirinfo,frame)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pV)), ::nbl::hlsl::is_same_v, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(rdirinfo,pV,pV,pV)), ::nbl::hlsl::is_same_v, T)) + // ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::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 clampMode +#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(ray_dir_info_type) tangentSpaceL, + const 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.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 vector3_type N) + { + this_t retval; + + retval.L = L; + retval.TdotL = bit_cast(numeric_limits::quiet_NaN); + retval.BdotL = bit_cast(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 vector3_type T, const vector3_type B, const vector3_type N) + { + this_t retval = create(L,N); + + retval.TdotL = nbl::hlsl::dot(T,L.getDirection()); + retval.BdotL = nbl::hlsl::dot(B,L.getDirection()); + + return retval; + } + + template) + static this_t create(const 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,interaction.T,interaction.B,interaction.N); + else + retval = create(L,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 getTdotL() NBL_CONST_MEMBER_FUNC { return TdotL; } + scalar_type getTdotL2() NBL_CONST_MEMBER_FUNC { const scalar_type t = getTdotL(); return t*t; } + scalar_type getBdotL() NBL_CONST_MEMBER_FUNC { return BdotL; } + scalar_type getBdotL2() NBL_CONST_MEMBER_FUNC { const scalar_type t = getBdotL(); return t*t; } + scalar_type getNdotL(BxDFClampMode _clamp = BxDFClampMode::BCM_NONE) NBL_CONST_MEMBER_FUNC + { + return bxdf::conditionalAbsOrMax(NdotL, _clamp); + } + scalar_type getNdotL2() NBL_CONST_MEMBER_FUNC { return NdotL2; } + + + RayDirInfo L; + + 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) +NBL_CONCEPT_BEGIN(1) +#define cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((cache.getVdotL()), ::nbl::hlsl::is_same_v, typename T::scalar_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.getVdotHLdotH()), ::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)((cache.isTransmission()), ::nbl::hlsl::is_same_v, bool)) +); +#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.VdotL = VdotL; + 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(), hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection())); + } + + // 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, + const vector3_type N, NBL_REF_ARG(vector3_type) H) + { + this_t retval; + retval.VdotL = VdotL; + 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 = ComputeMicrofacetNormal::isValidMicrofacet(transmitted, VdotL, retval.NdotH, computeMicrofacetNormal.orientedEta); + 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 = bit_cast(numeric_limits::quiet_NaN); + return retval; + } + + static this_t create( + const vector3_type V, const vector3_type L, const 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( + const vector3_type V, const vector3_type L, const 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, hlsl::dot(V, L), 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); + } + + bool isTransmission() NBL_CONST_MEMBER_FUNC { return getVdotHLdotH() < scalar_type(0.0); } + + scalar_type getVdotL() NBL_CONST_MEMBER_FUNC { return VdotL; } + scalar_type getVdotH() NBL_CONST_MEMBER_FUNC { return VdotH; } + scalar_type getLdotH() NBL_CONST_MEMBER_FUNC { return LdotH; } + scalar_type getVdotHLdotH() NBL_CONST_MEMBER_FUNC { return getVdotH() * getLdotH(); } + scalar_type getNdotH() NBL_CONST_MEMBER_FUNC + { + assert(NdotH >= scalar_type(0.0)); + return NdotH; + } + scalar_type getNdotH2() NBL_CONST_MEMBER_FUNC { return NdotH2; } + + scalar_type VdotL; + 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.getTdotH2()), ::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)((cache.getBdotH2()), ::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)) // TODO: refuses to compile when arg4 is rcp_eta for some reason, eta is fine + ((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(const vector3_type tangentSpaceV, const 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( + const vector3_type tangentSpaceV, + const vector3_type tangentSpaceH, + const bool transmitted, + NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpOrientedEta + ) + { + this_t retval = createForReflection(tangentSpaceV,tangentSpaceH); + if (transmitted) + { + Refract r = Refract::create(tangentSpaceV, tangentSpaceH); + retval.iso_cache.LdotH = r.getNdotT(rcpOrientedEta.value2[0]); + } + + return retval; + } + // always valid because its specialized for the reflective case + static this_t createForReflection(const vector3_type tangentSpaceV, const 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(), hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection())); + } + // transmissive cases need to be checked if the path is valid before usage + static this_t create( + const vector3_type V, const vector3_type L, + const vector3_type T, const vector3_type B, const 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; + } + + scalar_type getVdotL() NBL_CONST_MEMBER_FUNC { return iso_cache.getVdotL(); } + scalar_type getVdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.getVdotH(); } + scalar_type getLdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.getLdotH(); } + scalar_type getVdotHLdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.getVdotHLdotH(); } + scalar_type getNdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.getNdotH(); } + scalar_type getNdotH2() NBL_CONST_MEMBER_FUNC { return iso_cache.getNdotH2(); } + bool isTransmission() NBL_CONST_MEMBER_FUNC { return iso_cache.isTransmission(); } + + scalar_type getTdotH() NBL_CONST_MEMBER_FUNC { return TdotH; } + scalar_type getTdotH2() NBL_CONST_MEMBER_FUNC { const scalar_type t = getTdotH(); return t*t; } + scalar_type getBdotH() NBL_CONST_MEMBER_FUNC { return BdotH; } + scalar_type getBdotH2() NBL_CONST_MEMBER_FUNC { const scalar_type t = getBdotH(); return t*t; } + + 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)(concepts::FloatingPointLikeVectorial, 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)(concepts::FloatingPointLikeVectorial, 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 + +template +using quotient_and_pdf_scalar = sampling::quotient_and_pdf, P> ; +template +using quotient_and_pdf_rgb = sampling::quotient_and_pdf, P> ; + +namespace impl { - 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; +template && sizeof(T)<8) +struct beta +{ + // beta function specialized for Cook Torrance BTDFs + // uses modified Stirling's approximation for log2 up to 6th polynomial + static T __series_part(T x) + { + const T r = T(1.0) / x; + const T r2 = r * r; + T ser = T(0.0); + if (sizeof(T) > 2) + { + ser = -T(691.0/360360.0) * r2 + T(5.0/5940.0); + ser = ser * r2 - T(1.0/1680.0); + ser = ser * r2 + T(1.0/1260.0); + } + ser = ser * r2 - T(1.0/360.0); + ser = ser * r2 + T(1.0/12.0); + return ser * r - x; + } + + // removed values that cancel out in beta + static T __call(T x, T y) + { + assert(x >= T(0.999) && y >= T(0.999)); + + const T thresholds[4] = { 0, 5e5, 1e6, 1e15 }; // threshold values gotten from testing when the function returns nan/inf/1 + if (x+y > thresholds[mpl::find_lsb_v]) + return T(0.0); + + const T l2x = hlsl::log2(x); + const T l2y = hlsl::log2(y); + const T l2xy = hlsl::log2(x+y); + + return hlsl::exp2((x - T(0.5)) * l2x + (y - T(0.5)) * l2y - (x + y - T(0.5)) * l2xy + + numbers::inv_ln2 * (__series_part(x) + __series_part(y) - __series_part(x+y)) + T(1.32574806473616)); + } }; +} +template +T beta(NBL_CONST_REF_ARG(T) x, NBL_CONST_REF_ARG(T) y) +{ + return impl::beta::__call(x, y)/impl::beta::__call(1.0, 1.0); +} } } diff --git a/include/nbl/builtin/hlsl/bxdf/config.hlsl b/include/nbl/builtin/hlsl/bxdf/config.hlsl new file mode 100644 index 0000000000..471433ede6 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/config.hlsl @@ -0,0 +1,148 @@ +// 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_CONFIG_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_CONFIG_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ + +// TODO should just check `is_base_of` (but not possible right now cause of DXC limitations) +namespace config_concepts +{ +#define NBL_CONCEPT_NAME BasicConfiguration +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (conf, T) +NBL_CONCEPT_BEGIN(1) +#define conf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector2_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_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)) +); +#undef conf +#include + +#define NBL_CONCEPT_NAME MicrofacetConfiguration +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (conf, T) +NBL_CONCEPT_BEGIN(1) +#define conf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector2_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::monochrome_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::matrix3x3_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_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)) +); +#undef conf +#include +} + +template +struct SConfiguration; + +template +NBL_PARTIAL_REQ_TOP(LightSample && surface_interactions::Isotropic && !surface_interactions::Anisotropic && concepts::FloatingPointLikeVectorial) +struct SConfiguration && surface_interactions::Isotropic && !surface_interactions::Anisotropic && concepts::FloatingPointLikeVectorial) > +{ + NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = false; + + 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 monochrome_type = vector; + + using isotropic_interaction_type = Interaction; + using anisotropic_interaction_type = surface_interactions::SAnisotropic; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; +}; + +template +NBL_PARTIAL_REQ_TOP(LightSample && surface_interactions::Anisotropic && concepts::FloatingPointLikeVectorial) +struct SConfiguration && surface_interactions::Anisotropic && concepts::FloatingPointLikeVectorial) > +{ + NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = true; + + 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 monochrome_type = vector; + + using isotropic_interaction_type = typename Interaction::isotropic_interaction_type; + using anisotropic_interaction_type = Interaction; + using sample_type = LS; + using spectral_type = Spectrum; + using quotient_pdf_type = sampling::quotient_and_pdf; +}; + +template +struct SMicrofacetConfiguration; + +#define MICROFACET_CONF_ISO LightSample && surface_interactions::Isotropic && !surface_interactions::Anisotropic && CreatableIsotropicMicrofacetCache && !AnisotropicMicrofacetCache && concepts::FloatingPointLikeVectorial + +template +NBL_PARTIAL_REQ_TOP(MICROFACET_CONF_ISO) +struct SMicrofacetConfiguration : SConfiguration +#undef MICROFACET_CONF_ISO +{ + NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = false; + + using base_type = SConfiguration; + + using matrix3x3_type = matrix; + + using isocache_type = MicrofacetCache; + using anisocache_type = SAnisotropicMicrofacetCache; +}; + +#define MICROFACET_CONF_ANISO LightSample && surface_interactions::Anisotropic && AnisotropicMicrofacetCache && concepts::FloatingPointLikeVectorial + +template +NBL_PARTIAL_REQ_TOP(MICROFACET_CONF_ANISO) +struct SMicrofacetConfiguration : SConfiguration +#undef MICROFACET_CONF_ANISO +{ + NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = true; + + using base_type = SConfiguration; + + using matrix3x3_type = matrix; + + using isocache_type = typename MicrofacetCache::isocache_type; + using anisocache_type = MicrofacetCache; +}; + +#define NBL_BXDF_CONFIG_ALIAS(TYPE,CONFIG) using TYPE = typename CONFIG::TYPE + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl new file mode 100644 index 0000000000..d7c03c85e2 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl @@ -0,0 +1,122 @@ +// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_BUILTIN_HLSL_BXDF_COOK_TORRANCE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_COOK_TORRANCE_INCLUDED_ + +#include "nbl/builtin/hlsl/bxdf/common.hlsl" +#include "nbl/builtin/hlsl/bxdf/config.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf.hlsl" +#include "nbl/builtin/hlsl/bxdf/fresnel.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ + +// N (NDF), F (fresnel), MT (measure transform, using DualMeasureQuant) +template && ndf::NDF && fresnel::Fresnel) +struct SCookTorrance +{ + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + + scalar_type __D(NBL_CONST_REF_ARG(isocache_type) cache) + { + return ndf.template D(cache); + } + scalar_type __D(NBL_CONST_REF_ARG(anisocache_type) cache) + { + return ndf.template D(cache); + } + + template + MT __DG1(NBL_CONST_REF_ARG(Query) query) + { + MT measure_transform; + measure_transform.pdf = ndf.template DG1(query); + return measure_transform; + } + template + MT __DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(isocache_type) cache) + { + MT measure_transform; + measure_transform.pdf = ndf.template DG1(query, cache); + measure_transform.transmitted = cache.isTransmission(); + measure_transform.VdotH = cache.getVdotH(); + measure_transform.LdotH = cache.getLdotH(); + measure_transform.VdotHLdotH = cache.getVdotHLdotH(); + return measure_transform; + } + template + MT __DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(anisocache_type) cache) + { + MT measure_transform; + measure_transform.pdf = ndf.template DG1(query, cache); + measure_transform.transmitted = cache.isTransmission(); + measure_transform.VdotH = cache.getVdotH(); + measure_transform.LdotH = cache.getLdotH(); + measure_transform.VdotHLdotH = cache.getVdotHLdotH(); + return measure_transform; + } + + template + MT __DG(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + MT measure_transform; + measure_transform.pdf = ndf.template D(cache); + NBL_IF_CONSTEXPR(MT::Type == ndf::MicrofacetTransformTypes::MTT_REFLECT_REFRACT) + measure_transform.transmitted = cache.isTransmission(); + NBL_IF_CONSTEXPR(MT::Type == ndf::MicrofacetTransformTypes::MTT_REFRACT) + { + measure_transform.VdotH = cache.getVdotH(); + measure_transform.LdotH = cache.getLdotH(); + measure_transform.VdotHLdotH = cache.getVdotHLdotH(); + } + if (any >(ndf.A > hlsl::promote(numeric_limits::min))) + { + measure_transform.pdf *= ndf.template correlated(query, _sample, interaction); + } + return measure_transform; + } + template + MT __DG(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + MT measure_transform; + measure_transform.pdf = ndf.template D(cache); + NBL_IF_CONSTEXPR(MT::Type == ndf::MicrofacetTransformTypes::MTT_REFLECT_REFRACT) + measure_transform.transmitted = cache.isTransmission(); + NBL_IF_CONSTEXPR(MT::Type == ndf::MicrofacetTransformTypes::MTT_REFRACT) + { + measure_transform.VdotH = cache.getVdotH(); + measure_transform.LdotH = cache.getLdotH(); + measure_transform.VdotHLdotH = cache.getVdotHLdotH(); + } + if (any >(ndf.A > hlsl::promote(numeric_limits::min))) + { + measure_transform.pdf *= ndf.template correlated(query, _sample, interaction); + } + return measure_transform; + } + + N getNDF() { return ndf; } + F getFresnel() { return fresnel; } + + N ndf; + F fresnel; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index d3b3543a28..5711ab428e 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -4,10 +4,13 @@ #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/complex.hlsl" +#include "nbl/builtin/hlsl/tgmath.hlsl" #include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" -#include "nbl/builtin/hlsl/spirv_intrinsics/core.hlsl" namespace nbl { @@ -17,131 +20,439 @@ namespace hlsl namespace bxdf { -namespace impl +namespace fresnel { -template -struct orientedEtas; -template<> -struct orientedEtas +// derived from the Earl Hammon GDC talk but improved for all IOR not just 1.333 with actual numerical integration and curve fitting +template) +struct DiffuseCorrectionFactor { - static bool __call(NBL_REF_ARG(float) orientedEta, NBL_REF_ARG(float) rcpOrientedEta, float NdotI, float eta) + T operator()() { - const bool backside = NdotI < 0.0; - const float rcpEta = 1.0 / eta; - orientedEta = backside ? rcpEta : eta; - rcpOrientedEta = backside ? eta : rcpEta; - return backside; + vector::Dimension> TIR = orientedEta < hlsl::promote(1.0); + T invdenum = nbl::hlsl::mix(hlsl::promote(1.0), hlsl::promote(1.0) / (orientedEta2 * orientedEta2 * (hlsl::promote(554.33) - hlsl::promote(380.7) * orientedEta)), TIR); + T num = orientedEta * nbl::hlsl::mix(hlsl::promote(0.1921156102251088), orientedEta * hlsl::promote(298.25) - hlsl::promote(261.38) * orientedEta2 + hlsl::promote(138.43), TIR); + num = num + nbl::hlsl::mix(hlsl::promote(0.8078843897748912), hlsl::promote(-1.67), TIR); + return num * invdenum; } + + T orientedEta; + T orientedEta2; }; -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) + { + OrientedEtaRcps retval; + const bool backside = NdotI < scalar_type(0.0); + const T rcpEta = hlsl::promote(1.0) / eta; + retval.value = hlsl::mix(rcpEta, eta, backside); + retval.value2 = retval.value * retval.value; + return retval; + } + + DiffuseCorrectionFactor createDiffuseCorrectionFactor() NBL_CONST_MEMBER_FUNC { - 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; + DiffuseCorrectionFactor diffuseCorrectionFactor; + diffuseCorrectionFactor.orientedEta = hlsl::promote(1.0) / value; + diffuseCorrectionFactor.orientedEta2 = hlsl::promote(1.0) / value2; + return diffuseCorrectionFactor; } + + T value; + T value2; }; -} -template || is_vector_v) -bool getOrientedEtas(NBL_REF_ARG(T) orientedEta, NBL_REF_ARG(T) rcpOrientedEta, scalar_type_t NdotI, T eta) +template) +struct OrientedEtas { - return impl::orientedEtas::__call(orientedEta, rcpOrientedEta, NdotI, eta); -} + using scalar_type = typename vector_traits::scalar_type; + static OrientedEtas create(scalar_type NdotI, T eta) + { + OrientedEtas retval; + const bool backside = NdotI < scalar_type(0.0); + const T rcpEta = hlsl::promote(1.0) / eta; + retval.value = hlsl::mix(eta, rcpEta, backside); + retval.rcp = hlsl::mix(rcpEta, eta, backside); + return retval; + } + + OrientedEtaRcps getReciprocals() NBL_CONST_MEMBER_FUNC + { + OrientedEtaRcps retval; + retval.value = rcp; + retval.value2 = rcp * rcp; + return retval; + } + + T value; + T rcp; +}; -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 ComputeMicrofacetNormal { - using this_t = refract; - using vector_type = T; - using scalar_type = typename vector_traits::scalar_type; + using scalar_type = T; + using vector_type = vector; + using monochrome_type = vector; + + using unsigned_integer_type = typename unsigned_integer_of_size::type; - 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 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) { - 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 create(V, L, hlsl::dot(V, H), eta); + } + + 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; } - static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, scalar_type NdotI, scalar_type eta) + // 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)); + } + + static bool isValidMicrofacet(const bool transmitted, const scalar_type VdotL, const scalar_type NdotH, NBL_CONST_REF_ARG(fresnel::OrientedEtas >) orientedEta) + { + return !transmitted || (VdotL <= -hlsl::min(orientedEta.value, orientedEta.rcp) && NdotH >= nbl::hlsl::numeric_limits::min); + } + + vector_type V; + vector_type L; + scalar_type orientedEta; +}; + + +template) +struct Reflect +{ + 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) { 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; return retval; } - static this_t create(NBL_CONST_REF_ARG(vector_type) I, NBL_CONST_REF_ARG(vector_type) N, scalar_type eta) + scalar_type getNdotI() + { + return hlsl::dot(N, I); + } + + vector_type operator()() + { + return operator()(getNdotI()); + } + + vector_type operator()(const scalar_type NdotI) + { + return N * 2.0f * NdotI - I; + } + + vector_type I; + vector_type N; +}; + +template) +struct Refract +{ + using this_t = Refract; + 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) { 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; return retval; } - static scalar_type computeNdotT(bool backside, scalar_type NdotI2, scalar_type rcpOrientedEta2) + scalar_type getNdotI() { - scalar_type NdotT2 = rcpOrientedEta2 * NdotI2 + 1.0 - rcpOrientedEta2; - scalar_type absNdotT = sqrt(NdotT2); - return backside ? absNdotT : -(absNdotT); + return hlsl::dot(N, I); } - vector_type doRefract() + scalar_type getNdotT(const scalar_type rcpOrientedEta2) { - return N * (NdotI * rcpOrientedEta + computeNdotT(backside, NdotI2, rcpOrientedEta2)) - rcpOrientedEta * I; + scalar_type NdotI = getNdotI(); + scalar_type NdotT2 = rcpOrientedEta2 * NdotI*NdotI + 1.0 - rcpOrientedEta2; + scalar_type absNdotT = sqrt(NdotT2); + return ieee754::copySign(absNdotT, -NdotI); // TODO: make a ieee754::copySignIntoPositive, see https://github.com/Devsh-Graphics-Programming/Nabla/pull/899#discussion_r2197473145 } - 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) + vector_type operator()(const scalar_type rcpOrientedEta) { - return _N * (_NdotI * (_refract ? _rcpOrientedEta : 1.0f) + _NdotTorR) - _I * (_refract ? _rcpOrientedEta : 1.0f); + return N * (getNdotI() * rcpOrientedEta + getNdotT(rcpOrientedEta*rcpOrientedEta)) - rcpOrientedEta * I; } - vector_type doReflectRefract(bool r) + vector_type operator()(const scalar_type rcpOrientedEta, const scalar_type NdotI, const scalar_type NdotT) { - const T NdotTorR = r ? computeNdotT(backside, NdotI2, rcpOrientedEta2) : NdotI; - return doReflectRefract(r, I, N, NdotI, NdotTorR, rcpOrientedEta); + return N * (NdotI * rcpOrientedEta + NdotT) - rcpOrientedEta * I; } vector_type I; vector_type N; - bool backside; - scalar_type NdotI; - scalar_type NdotI2; - scalar_type rcpOrientedEta; - scalar_type rcpOrientedEta2; }; +template) +struct ReflectRefract +{ + using this_t = ReflectRefract; + using vector_type = vector; + using scalar_type = T; + + // when you know you'll reflect + scalar_type getNdotR() + { + return refract.getNdotI(); + } + + // when you know you'll refract + scalar_type getNdotT(const scalar_type rcpOrientedEta) + { + return refract.getNdotT(rcpOrientedEta*rcpOrientedEta); + } + + scalar_type getNdotTorR(const bool doRefract, const scalar_type rcpOrientedEta) + { + return hlsl::mix(getNdotR(), getNdotT(rcpOrientedEta), doRefract); + } + + vector_type operator()(const bool doRefract, const scalar_type rcpOrientedEta) + { + scalar_type NdotI = getNdotR(); + return refract.N * (NdotI * (hlsl::mix(1.0f, rcpOrientedEta, doRefract)) + hlsl::mix(NdotI, getNdotT(rcpOrientedEta), doRefract)) - refract.I * (hlsl::mix(1.0f, rcpOrientedEta, doRefract)); + } + + vector_type operator()(const scalar_type NdotTorR, const scalar_type rcpOrientedEta) + { + scalar_type NdotI = getNdotR(); + bool doRefract = ComputeMicrofacetNormal::isTransmissionPath(NdotI, NdotTorR); + return refract.N * (NdotI * (hlsl::mix(1.0f, rcpOrientedEta, doRefract)) + hlsl::mix(NdotI, NdotTorR, doRefract)) - refract.I * (hlsl::mix(1.0f, rcpOrientedEta, doRefract)); + } + + Refract refract; +}; + + +namespace fresnel +{ + +#define NBL_CONCEPT_NAME Fresnel +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (fresnel, T) +NBL_CONCEPT_BEGIN(1) +#define fresnel NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::vector_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((fresnel()), ::nbl::hlsl::is_same_v, typename T::vector_type)) +); +#undef fresnel +#include + +template) +struct Schlick +{ + using scalar_type = typename vector_traits::scalar_type; + using vector_type = T; + + 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::all(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) +struct Conductor +{ + using scalar_type = typename vector_traits::scalar_type; + using vector_type = T; + + static Conductor create(NBL_CONST_REF_ARG(T) eta, NBL_CONST_REF_ARG(T) etak, scalar_type clampedCosTheta) + { + Conductor retval; + retval.eta = eta; + retval.etak = etak; + retval.etak2 = etak*etak; + retval.clampedCosTheta = clampedCosTheta; + return retval; + } + + static Conductor create(NBL_CONST_REF_ARG(complex_t) eta, scalar_type clampedCosTheta) + { + Conductor retval; + retval.eta = eta.real(); + retval.etak = eta.imag(); + retval.etak2 = eta.imag()*eta.imag(); + 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::all(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 etak; + T etak2; + scalar_type clampedCosTheta; +}; + +template) +struct Dielectric +{ + using scalar_type = typename vector_traits::scalar_type; + using vector_type = T; + + static Dielectric create(NBL_CONST_REF_ARG(T) eta, scalar_type cosTheta) + { + Dielectric retval; + scalar_type absCosTheta = hlsl::abs(cosTheta); + retval.orientedEta = OrientedEtas::create(absCosTheta, eta); + 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(orientedEta.value * orientedEta.value, absCosTheta); + } + + OrientedEtas orientedEta; + scalar_type absCosTheta; +}; + +template) +struct DielectricFrontFaceOnly +{ + using scalar_type = typename vector_traits::scalar_type; + using vector_type = T; + + static DielectricFrontFaceOnly create(NBL_CONST_REF_ARG(T) eta, scalar_type absCosTheta) + { + Dielectric retval; + retval.orientedEta = OrientedEtas::create(absCosTheta, eta); + retval.absCosTheta = hlsl::abs(absCosTheta); + return retval; + } + + T operator()() + { + return Dielectric::__call(orientedEta.value * orientedEta.value, absCosTheta); + } + + OrientedEtas orientedEta; + scalar_type absCosTheta; +}; + + +// gets the sum of all R, T R T, T R^3 T, T R^5 T, ... paths +template || concepts::FloatingPointLikeVectorial) +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/ndf.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl new file mode 100644 index 0000000000..4de597f59a --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/ndf.hlsl @@ -0,0 +1,74 @@ +// 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/concepts.hlsl" +#include "nbl/builtin/hlsl/bxdf/common.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace ndf +{ + +namespace dummy_impl +{ +using sample_t = SLightSample >; +using interaction_t = surface_interactions::SAnisotropic > >; +using cache_t = SAnisotropicMicrofacetCache >; +struct DQuery // nonsense struct, just put in all the functions to pass the ndf query concepts +{ + using scalar_type = float; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return 0; } + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return 0; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return 0; } + + scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return 0; } + scalar_type getOrientedEta() NBL_CONST_MEMBER_FUNC { return 0; } + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return 0; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return 0; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return BxDFClampMode::BCM_NONE; } +}; +} + +#define NBL_CONCEPT_NAME NDF +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (ndf, T) +#define NBL_CONCEPT_PARAM_1 (query, dummy_impl::DQuery) +#define NBL_CONCEPT_PARAM_2 (_sample, dummy_impl::sample_t) +#define NBL_CONCEPT_PARAM_3 (interaction, dummy_impl::interaction_t) +#define NBL_CONCEPT_PARAM_4 (cache, dummy_impl::cache_t) +NBL_CONCEPT_BEGIN(5) +#define ndf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define cache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template D(cache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template DG1(query)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template DG1(query, cache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template correlated(query, _sample, interaction)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((ndf.template G2_over_G1(query, _sample, interaction, cache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) +); +#undef cache +#undef interaction +#undef _sample +#undef query +#undef ndf +#include + +} +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl new file mode 100644 index 0000000000..8076102d94 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl @@ -0,0 +1,198 @@ +// 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_BECKMANN_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_NDF_BECKMANN_INCLUDED_ + +#include "nbl/builtin/hlsl/limits.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace ndf +{ + +namespace beckmann_concepts +{ +#define NBL_CONCEPT_NAME DG1Query +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (query, T) +NBL_CONCEPT_BEGIN(1) +#define query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getNdf()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getLambdaV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) +); +#undef query +#include + +#define NBL_CONCEPT_NAME G2overG1Query +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (query, T) +NBL_CONCEPT_BEGIN(1) +#define query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getLambdaL()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getLambdaV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) +); +#undef query +#include +} + +template +struct Beckmann; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) +struct Beckmann) > +{ + using scalar_type = T; + + template) + scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + scalar_type nom = exp2((cache.getNdotH2() - scalar_type(1.0)) / (log(2.0) * a2 * cache.getNdotH2())); + scalar_type denom = a2 * cache.getNdotH2() * cache.getNdotH2(); + return numbers::inv_pi * nom / denom; + } + + // brdf + template) + scalar_type DG1(NBL_CONST_REF_ARG(Query) query) + { + return query.getNdf() / (scalar_type(1.0) + query.getLambdaV()); + } + + // bsdf + template && ReadableIsotropicMicrofacetCache) + scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + return DG1(query); + } + + scalar_type G1(scalar_type lambda) + { + return scalar_type(1.0) / (scalar_type(1.0) + lambda); + } + + scalar_type C2(scalar_type NdotX2) + { + return NdotX2 / (a2 * (scalar_type(1.0) - NdotX2)); + } + + scalar_type Lambda(scalar_type c2) + { + scalar_type c = sqrt(c2); + scalar_type nom = scalar_type(1.0) - scalar_type(1.259) * c + scalar_type(0.396) * c2; + scalar_type denom = scalar_type(2.181) * c2 + scalar_type(3.535) * c; + return hlsl::mix(scalar_type(0.0), nom / denom, c < scalar_type(1.6)); + } + + scalar_type LambdaC2(scalar_type NdotX2) + { + return Lambda(C2(NdotX2)); + } + + template && LightSample && surface_interactions::Isotropic) + scalar_type correlated(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + { + return scalar_type(1.0) / (scalar_type(1.0) + query.getLambdaV() + query.getLambdaL()); + } + + template && LightSample && surface_interactions::Isotropic && ReadableIsotropicMicrofacetCache) + scalar_type G2_over_G1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + scalar_type onePlusLambda_V = scalar_type(1.0) + query.getLambdaV(); + return onePlusLambda_V * hlsl::mix(scalar_type(1.0)/(onePlusLambda_V + query.getLambdaL()), bxdf::beta(onePlusLambda_V, scalar_type(1.0) + query.getLambdaL()), cache.isTransmission()); + } + + vector A; + scalar_type a2; +}; + + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) +struct Beckmann) > +{ + using scalar_type = T; + + template) + scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + scalar_type nom = exp(-(cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2) / cache.getNdotH2()); + scalar_type denom = A.x * A.y * cache.getNdotH2() * cache.getNdotH2(); + return numbers::inv_pi * nom / denom; + } + + template) + scalar_type DG1(NBL_CONST_REF_ARG(Query) query) + { + Beckmann beckmann; + scalar_type dg = beckmann.template DG1(query); + return dg; + } + + template && AnisotropicMicrofacetCache) + scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + Beckmann beckmann; + scalar_type dg = beckmann.template DG1(query, cache.iso_cache); + return dg; + } + + scalar_type G1(scalar_type lambda) + { + return scalar_type(1.0) / (scalar_type(1.0) + lambda); + } + + scalar_type C2(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) + { + return NdotX2 / (TdotX2 * ax2 + BdotX2 * ay2); + } + + scalar_type Lambda(scalar_type c2) + { + scalar_type c = sqrt(c2); + scalar_type nom = scalar_type(1.0) - scalar_type(1.259) * c + scalar_type(0.396) * c2; + scalar_type denom = scalar_type(2.181) * c2 + scalar_type(3.535) * c; + return hlsl::mix(scalar_type(0.0), nom / denom, c < scalar_type(1.6)); + } + + scalar_type LambdaC2(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) + { + return Lambda(C2(TdotX2, BdotX2, NdotX2)); + } + + template && LightSample && surface_interactions::Anisotropic) + scalar_type correlated(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + { + return scalar_type(1.0) / (scalar_type(1.0) + query.getLambdaV() + query.getLambdaL()); + } + + template && LightSample && surface_interactions::Anisotropic && AnisotropicMicrofacetCache) + scalar_type G2_over_G1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + scalar_type onePlusLambda_V = scalar_type(1.0) + query.getLambdaV(); + return onePlusLambda_V * hlsl::mix(scalar_type(1.0)/(onePlusLambda_V + query.getLambdaL()), bxdf::beta(onePlusLambda_V, scalar_type(1.0) + query.getLambdaL()), cache.isTransmission()); + } + + vector A; + scalar_type ax2; + scalar_type ay2; +}; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl new file mode 100644 index 0000000000..c21ded7af1 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -0,0 +1,286 @@ +// 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_GGX_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_NDF_GGX_INCLUDED_ + +#include "nbl/builtin/hlsl/limits.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace ndf +{ + +namespace ggx_concepts +{ +#define NBL_CONCEPT_NAME DG1BrdfQuery +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (query, T) +NBL_CONCEPT_BEGIN(1) +#define query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getNdf()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getG1over2NdotV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) +); +#undef query +#include + +#define NBL_CONCEPT_NAME DG1BsdfQuery +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (query, T) +NBL_CONCEPT_BEGIN(1) +#define query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getNdf()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getG1over2NdotV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getOrientedEta()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) +); +#undef query +#include + +#define NBL_CONCEPT_NAME G2XQuery +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (query, T) +NBL_CONCEPT_BEGIN(1) +#define query NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getDevshV()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getDevshL()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((query.getClampMode()), ::nbl::hlsl::is_same_v, BxDFClampMode)) +); +#undef query +#include +} + +template +struct GGX; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) +struct GGX) > +{ + using scalar_type = T; + + // trowbridge-reitz + template) + scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + scalar_type denom = scalar_type(1.0) - one_minus_a2 * cache.getNdotH2(); + return a2 * numbers::inv_pi / (denom * denom); + } + + template) + scalar_type DG1(NBL_CONST_REF_ARG(Query) query) + { + return scalar_type(0.5) * query.getNdf() * query.getG1over2NdotV(); + } + + template && ReadableIsotropicMicrofacetCache) + scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + scalar_type NG = query.getNdf() * query.getG1over2NdotV(); + scalar_type factor = scalar_type(0.5); + if (cache.isTransmission()) + { + const scalar_type VdotH_etaLdotH = (cache.getVdotH() + query.getOrientedEta() * cache.getLdotH()); + // VdotHLdotH is negative under transmission, so this factor is negative + factor *= -scalar_type(2.0) * cache.getVdotHLdotH() / (VdotH_etaLdotH * VdotH_etaLdotH); + } + return NG * factor; + } + + scalar_type devsh_part(scalar_type NdotX2) + { + assert(a2 >= numeric_limits::min); + return sqrt(a2 + one_minus_a2 * NdotX2); + } + + scalar_type G1_wo_numerator(scalar_type absNdotX, scalar_type NdotX2) + { + return scalar_type(1.0) / (absNdotX + devsh_part(NdotX2)); + } + + scalar_type G1_wo_numerator_devsh_part(scalar_type absNdotX, scalar_type devsh_part) + { + // numerator is 2 * NdotX + return scalar_type(1.0) / (absNdotX + devsh_part); + } + + // without numerator, numerator is 2 * NdotV * NdotL, we factor out 4 * NdotV * NdotL, hence 0.5 + template && LightSample && surface_interactions::Isotropic) + scalar_type correlated(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + { + BxDFClampMode _clamp = query.getClampMode(); + assert(_clamp != BxDFClampMode::BCM_NONE); + + scalar_type Vterm = _sample.getNdotL(_clamp) * query.getDevshV(); + scalar_type Lterm = interaction.getNdotV(_clamp) * query.getDevshL(); + return scalar_type(0.5) / (Vterm + Lterm); + } + + template && LightSample && surface_interactions::Isotropic && ReadableIsotropicMicrofacetCache) + scalar_type G2_over_G1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + BxDFClampMode _clamp = query.getClampMode(); + assert(_clamp != BxDFClampMode::BCM_NONE); + + scalar_type G2_over_G1; + scalar_type NdotV = interaction.getNdotV(_clamp); + scalar_type NdotL = _sample.getNdotL(_clamp); + scalar_type devsh_v = query.getDevshV(); + scalar_type devsh_l = query.getDevshL(); + if (cache.isTransmission()) + { + if (NdotV < 1e-7 || NdotL < 1e-7) + return 0.0; + scalar_type onePlusLambda_V = scalar_type(0.5) * (devsh_v / NdotV + scalar_type(1.0)); + scalar_type onePlusLambda_L = scalar_type(0.5) * (devsh_l / NdotL + scalar_type(1.0)); + G2_over_G1 = bxdf::beta(onePlusLambda_L, onePlusLambda_V) * onePlusLambda_V; + } + else + { + G2_over_G1 = NdotL * (devsh_v + NdotV); // alternative `Vterm+NdotL*NdotV /// NdotL*NdotV could come as a parameter + G2_over_G1 /= NdotV * devsh_l + NdotL * devsh_v; + } + + return G2_over_G1; + } + + vector A; + scalar_type a2; + scalar_type one_minus_a2; +}; + +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) +struct GGX) > +{ + using scalar_type = T; + + template) + scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + scalar_type denom = cache.getTdotH2() / ax2 + cache.getBdotH2() / ay2 + cache.getNdotH2(); + return numbers::inv_pi / (a2 * denom * denom); + } + + // burley + scalar_type D(scalar_type a2, scalar_type TdotH, scalar_type BdotH, scalar_type NdotH, scalar_type anisotropy) + { + scalar_type antiAniso = scalar_type(1.0) - anisotropy; + scalar_type atab = a2 * antiAniso; + scalar_type anisoTdotH = antiAniso * TdotH; + scalar_type anisoNdotH = antiAniso * NdotH; + scalar_type w2 = antiAniso/(BdotH * BdotH + anisoTdotH * anisoTdotH + anisoNdotH * anisoNdotH * a2); + return w2 * w2 * atab * numbers::inv_pi; + } + + template) + scalar_type DG1(NBL_CONST_REF_ARG(Query) query) + { + GGX ggx; + return ggx.template DG1(query); + } + + template && AnisotropicMicrofacetCache) + scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + GGX ggx; + return ggx.template DG1(query, cache.iso_cache); + } + + scalar_type devsh_part(scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) + { + assert(ax2 >= numeric_limits::min && ay2 >= numeric_limits::min); + return sqrt(TdotX2 * ax2 + BdotX2 * ay2 + NdotX2); + } + + scalar_type G1_wo_numerator(scalar_type NdotX, scalar_type TdotX2, scalar_type BdotX2, scalar_type NdotX2) + { + return scalar_type(1.0) / (NdotX + devsh_part(TdotX2, BdotX2, NdotX2)); + } + + scalar_type G1_wo_numerator_devsh_part(scalar_type NdotX, scalar_type devsh_part) + { + return scalar_type(1.0) / (NdotX + devsh_part); + } + + // without numerator + template && LightSample && surface_interactions::Anisotropic) + scalar_type correlated(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction) + { + BxDFClampMode _clamp = query.getClampMode(); + assert(_clamp != BxDFClampMode::BCM_NONE); + + scalar_type Vterm = _sample.getNdotL(_clamp) * query.getDevshV(); + scalar_type Lterm = interaction.getNdotV(_clamp) * query.getDevshL(); + return scalar_type(0.5) / (Vterm + Lterm); + } + + template && LightSample && surface_interactions::Anisotropic && AnisotropicMicrofacetCache) + scalar_type G2_over_G1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) + { + BxDFClampMode _clamp = query.getClampMode(); + assert(_clamp != BxDFClampMode::BCM_NONE); + + scalar_type G2_over_G1; + scalar_type NdotV = interaction.getNdotV(_clamp); + scalar_type NdotL = _sample.getNdotL(_clamp); + scalar_type devsh_v = query.getDevshV(); + scalar_type devsh_l = query.getDevshL(); + if (cache.isTransmission()) + { + if (NdotV < 1e-7 || NdotL < 1e-7) + return 0.0; + scalar_type onePlusLambda_V = scalar_type(0.5) * (devsh_v / NdotV + scalar_type(1.0)); + scalar_type onePlusLambda_L = scalar_type(0.5) * (devsh_l / NdotL + scalar_type(1.0)); + G2_over_G1 = bxdf::beta(onePlusLambda_L, onePlusLambda_V) * onePlusLambda_V; + } + else + { + G2_over_G1 = NdotL * (devsh_v + NdotV); + G2_over_G1 /= NdotV * devsh_l + NdotL * devsh_v; + } + + return G2_over_G1; + } + + vector A; + scalar_type ax2; + scalar_type ay2; + scalar_type a2; +}; + + +namespace impl +{ +template +struct is_ggx : bool_constant< + is_same >::value || + is_same >::value +> {}; +} + +template +struct is_ggx : impl::is_ggx {}; + +template +NBL_CONSTEXPR bool is_ggx_v = is_ggx::value; + +} +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl new file mode 100644 index 0000000000..d4118aaec4 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl @@ -0,0 +1,207 @@ +// 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_NDF_MICROFACET_LIGHT_TRANSFORM_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BXDF_NDF_MICROFACET_LIGHT_TRANSFORM_INCLUDED_ + +#include "nbl/builtin/hlsl/limits.hlsl" +#include "nbl/builtin/hlsl/bxdf/common.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace ndf +{ + +enum MicrofacetTransformTypes : uint16_t +{ + MTT_REFLECT = 0b01, + MTT_REFRACT = 0b10, + MTT_REFLECT_REFRACT = 0b11 +}; + +template +struct SDualMeasureQuant; + +template +struct SDualMeasureQuant +{ + using this_t = SDualMeasureQuant; + using scalar_type = T; + + NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFLECT; + + scalar_type getMicrofacetMeasure() + { + return pdf; + } + + // this computes the max(NdotL,0)/(4*max(NdotV,0)*max(NdotL,0)) factor which transforms PDFs in the f in projected microfacet f * NdotH measure to projected light measure f * NdotL + scalar_type getProjectedLightMeasure() + { + return scalar_type(0.25) * pdf / maxNdotV; + } + + scalar_type pdf; + scalar_type maxNdotV; +}; + +template +struct SDualMeasureQuant +{ + using this_t = SDualMeasureQuant; + using scalar_type = T; + + NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFLECT; + + scalar_type getMicrofacetMeasure() + { + return pdf; + } + + // this computes the max(NdotL,0)/(4*max(NdotV,0)*max(NdotL,0)) factor which transforms PDFs in the f in projected microfacet f * NdotH measure to projected light measure f * NdotL + scalar_type getProjectedLightMeasure() + { + return pdf * maxNdotL; + } + + scalar_type pdf; + scalar_type maxNdotL; +}; + +template +struct SDualMeasureQuant +{ + using this_t = SDualMeasureQuant; + using scalar_type = T; + + NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFRACT; + + scalar_type getMicrofacetMeasure() + { + return pdf; + } + + scalar_type getProjectedLightMeasure() + { + const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + scalar_type denominator = absNdotV * (-VdotH_etaLdotH * VdotH_etaLdotH); + return pdf * VdotHLdotH / denominator; + } + + scalar_type pdf; + scalar_type absNdotV; + scalar_type VdotH; + scalar_type LdotH; + scalar_type VdotHLdotH; + scalar_type orientedEta; +}; + +template +struct SDualMeasureQuant +{ + using this_t = SDualMeasureQuant; + using scalar_type = T; + + NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFRACT; + + scalar_type getMicrofacetMeasure() + { + return pdf; + } + + scalar_type getProjectedLightMeasure() + { + const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + scalar_type denominator = absNdotL * (-scalar_type(4.0) * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH)); + return pdf * denominator; + } + + scalar_type pdf; + scalar_type absNdotL; + scalar_type VdotH; + scalar_type LdotH; + scalar_type VdotHLdotH; + scalar_type orientedEta; +}; + +template +struct SDualMeasureQuant +{ + using this_t = SDualMeasureQuant; + using scalar_type = T; + + NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFLECT_REFRACT; + + scalar_type getMicrofacetMeasure() + { + return pdf; + } + + scalar_type getProjectedLightMeasure() + { + scalar_type denominator = absNdotV; + if (transmitted) + { + const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + denominator *= -VdotH_etaLdotH * VdotH_etaLdotH; + } + return pdf * (transmitted ? VdotHLdotH : scalar_type(0.25)) / denominator; + } + + scalar_type pdf; + scalar_type absNdotV; + bool transmitted; + scalar_type VdotH; + scalar_type LdotH; + scalar_type VdotHLdotH; + scalar_type orientedEta; +}; + +template +struct SDualMeasureQuant +{ + using this_t = SDualMeasureQuant; + using scalar_type = T; + + NBL_CONSTEXPR_STATIC_INLINE MicrofacetTransformTypes Type = MTT_REFLECT_REFRACT; + + scalar_type getMicrofacetMeasure() + { + return pdf; + } + + scalar_type getProjectedLightMeasure() + { + scalar_type denominator = absNdotL; + if (transmitted) + { + const scalar_type VdotH_etaLdotH = (VdotH + orientedEta * LdotH); + // VdotHLdotH is negative under transmission, so thats denominator is negative + denominator *= -scalar_type(4.0) * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH); + } + return pdf * denominator; + } + + scalar_type pdf; + scalar_type absNdotL; + bool transmitted; + scalar_type VdotH; + scalar_type LdotH; + scalar_type VdotHLdotH; + scalar_type orientedEta; +}; + + +} +} +} +} + +#endif 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..18abadc599 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl @@ -0,0 +1,430 @@ +// 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/bxdf/bxdf_traits.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl" +#include "nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template +struct SBeckmannAnisotropicBxDF; + +template) +struct SBeckmannIsotropicBxDF +{ + using this_t = SBeckmannIsotropicBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + + using ndf_type = ndf::Beckmann; + using fresnel_type = fresnel::Conductor; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; + + struct SBeckmannQuery + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + using query_type = SBeckmannQuery; + + // 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.__base.ndf.A = vector2_type(A, A); + retval.__base.ndf.a2 = A*A; + retval.__base.fresnel.eta = ior0; + retval.__base.fresnel.etak = ior1; + retval.__base.fresnel.etak2 = ior1*ior1; + return retval; + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + query_type query; + ndf_type beckmann_ndf = __base.getNDF(); + query.lambda_L = beckmann_ndf.LambdaC2(_sample.getNdotL2()); + query.lambda_V = beckmann_ndf.LambdaC2(interaction.getNdotV2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + if (interaction.getNdotV() > numeric_limits::min) + { + struct SBeckmannG2overG1Query + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + + SBeckmannG2overG1Query g2_query; + g2_query.lambda_L = query.getLambdaL(); + g2_query.lambda_V = query.getLambdaV(); + + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.maxNdotV = interaction.getNdotV(_clamp); + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + fresnel_type f = __base.getFresnel(); + f.clampedCosTheta = cache.getVdotH(); + return f() * DG; + } + else + return hlsl::promote(0.0); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(isocache_type) cache) + { + SBeckmannAnisotropicBxDF beckmann_aniso = SBeckmannAnisotropicBxDF::create(__base.ndf.A.x, __base.ndf.A.y, __base.fresnel.eta, __base.fresnel.etak); + anisocache_type anisocache; + sample_type s = beckmann_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SBeckmannDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type ndf; + scalar_type lambda_V; + }; + + ndf_type beckmann_ndf = __base.getNDF(); + + SBeckmannDG1Query dg1_query; + dg1_query.ndf = __base.__D(cache); + dg1_query.lambda_V = query.getLambdaV(); + + measure_transform_type dualMeasure = __base.template __DG1(dg1_query); + dualMeasure.maxNdotV = interaction.getNdotV(_clamp); + return dualMeasure.getProjectedLightMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + + spectral_type quo = hlsl::promote(0.0); + if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) + { + struct SBeckmannG2overG1Query + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + + ndf_type beckmann_ndf = __base.getNDF(); + SBeckmannG2overG1Query g2_query; + g2_query.lambda_L = query.getLambdaL(); + g2_query.lambda_V = query.getLambdaV(); + scalar_type G2_over_G1 = beckmann_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + fresnel_type f = __base.getFresnel(); + f.clampedCosTheta = cache.getVdotH(); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +template +NBL_PARTIAL_REQ_TOP(config_concepts::MicrofacetConfiguration) +struct SBeckmannAnisotropicBxDF) > +{ + using this_t = SBeckmannAnisotropicBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + + using ndf_type = ndf::Beckmann; + using fresnel_type = fresnel::Conductor; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; + + struct SBeckmannQuery + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + using query_type = SBeckmannQuery; + + // 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.__base.ndf.A = vector2_type(ax, ay); + retval.__base.ndf.ax2 = ax*ax; + retval.__base.ndf.ay2 = ay*ay; + retval.__base.fresnel.eta = ior0; + retval.__base.fresnel.etak = ior1; + retval.__base.fresnel.etak2 = ior1*ior1; + return retval; + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + query_type query; + ndf_type beckmann_ndf = __base.getNDF(); + query.lambda_L = beckmann_ndf.LambdaC2(_sample.getTdotL2(), _sample.getBdotL2(), _sample.getNdotL2()); + query.lambda_V = beckmann_ndf.LambdaC2(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + if (interaction.getNdotV() > numeric_limits::min) + { + struct SBeckmannG2overG1Query + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + + SBeckmannG2overG1Query g2_query; + g2_query.lambda_L = query.getLambdaL(); + g2_query.lambda_V = query.getLambdaV(); + + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.maxNdotV = interaction.getNdotV(_clamp); + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + fresnel_type f = __base.getFresnel(); + f.clampedCosTheta = cache.getVdotH(); + return f() * DG; + } + else + return hlsl::promote(0.0); + } + + vector3_type __generate(NBL_CONST_REF_ARG(vector3_type) localV, const vector2_type u) + { + vector2_type A = __base.ndf.A; + //stretch + vector3_type V = nbl::hlsl::normalize(vector3_type(A.x * localV.x, A.y * localV.y, localV.z)); + + vector2_type slope; + if (V.z > 0.9999)//V.z=NdotV=cosTheta in tangent space + { + scalar_type r = sqrt(-log(1.0 - u.x)); + scalar_type sinPhi = sin(2.0 * numbers::pi * u.y); + scalar_type cosPhi = cos(2.0 * numbers::pi * u.y); + slope = (vector2_type)r * vector2_type(cosPhi,sinPhi); + } + else + { + scalar_type cosTheta = V.z; + scalar_type sinTheta = sqrt(1.0 - cosTheta * cosTheta); + scalar_type tanTheta = sinTheta / cosTheta; + scalar_type cotTheta = 1.0 / tanTheta; + + scalar_type a = -1.0; + scalar_type c = erf(cosTheta); + scalar_type sample_x = max(u.x, 1.0e-6); + scalar_type theta = acos(cosTheta); + scalar_type fit = 1.0 + theta * (-0.876 + theta * (0.4265 - 0.0594*theta)); + scalar_type b = c - (1.0 + c) * pow(1.0-sample_x, fit); + + scalar_type normalization = 1.0 / (1.0 + c + numbers::inv_sqrtpi * tanTheta * exp(-cosTheta*cosTheta)); + + const int ITER_THRESHOLD = 10; + const float MAX_ACCEPTABLE_ERR = 1.0e-5; + int it = 0; + float value=1000.0; + while (++it < ITER_THRESHOLD && nbl::hlsl::abs(value) > MAX_ACCEPTABLE_ERR) + { + if (!(b >= a && b <= c)) + b = 0.5 * (a + c); + + float invErf = erfInv(b); + value = normalization * (1.0 + b + numbers::inv_sqrtpi * tanTheta * exp(-invErf * invErf)) - sample_x; + float derivative = normalization * (1.0 - invErf * cosTheta); + + if (value > 0.0) + c = b; + else + a = b; + + b -= value/derivative; + } + // TODO: investigate if we can replace these two erf^-1 calls with a box muller transform + slope.x = erfInv(b); + slope.y = erfInv(2.0 * max(u.y, 1.0e-6) - 1.0); + } + + scalar_type sinTheta = sqrt(1.0 - V.z*V.z); + scalar_type cosPhi = sinTheta==0.0 ? 1.0 : clamp(V.x/sinTheta, -1.0, 1.0); + scalar_type sinPhi = sinTheta==0.0 ? 0.0 : clamp(V.y/sinTheta, -1.0, 1.0); + //rotate + scalar_type tmp = cosPhi*slope.x - sinPhi*slope.y; + slope.y = sinPhi*slope.x + cosPhi*slope.y; + slope.x = tmp; + + //unstretch + slope = vector2_type(A.x,A.y)*slope; + + return nbl::hlsl::normalize(vector3_type(-slope, 1.0)); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) + { + const vector3_type localV = interaction.getTangentSpaceV(); + const vector3_type H = __generate(localV, u); + + cache = anisocache_type::createForReflection(localV, H); + ray_dir_info_type localL; + bxdf::Reflect r = bxdf::Reflect::create(localV, H); + localL.direction = r(cache.iso_cache.getVdotH()); + + return sample_type::createFromTangentSpace(localL, interaction.getFromTangentSpace()); + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + struct SBeckmannDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type ndf; + scalar_type lambda_V; + }; + + ndf_type beckmann_ndf = __base.getNDF(); + + SBeckmannDG1Query dg1_query; + dg1_query.ndf = __base.__D(cache); + dg1_query.lambda_V = query.getLambdaV(); + + measure_transform_type dualMeasure = __base.template __DG1(dg1_query); + dualMeasure.maxNdotV = interaction.getNdotV(_clamp); + return dualMeasure.getProjectedLightMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + + spectral_type quo = hlsl::promote(0.0); + if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) + { + struct SBeckmannG2overG1Query + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + + ndf_type beckmann_ndf = __base.getNDF(); + SBeckmannG2overG1Query g2_query; + g2_query.lambda_L = query.getLambdaL(); + g2_query.lambda_V = query.getLambdaV(); + scalar_type G2_over_G1 = beckmann_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + fresnel_type f = __base.getFresnel(); + f.clampedCosTheta = cache.getVdotH(); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +} + +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; +}; + +} +} +} + +#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..6d7599be82 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl @@ -0,0 +1,402 @@ +// 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/bxdf/bxdf_traits.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" +#include "nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl" +#include "nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template +struct SGGXAnisotropicBxDF; + +template) +struct SGGXIsotropicBxDF +{ + using this_t = SGGXIsotropicBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + + using ndf_type = ndf::GGX; + using fresnel_type = fresnel::Conductor; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; + + struct SGGXQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + + scalar_type devsh_v; + scalar_type devsh_l; + }; + using query_type = SGGXQuery; + + // 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.__base.ndf.A = vector2_type(A, A); + retval.__base.ndf.a2 = A*A; + retval.__base.ndf.one_minus_a2 = scalar_type(1.0) - A*A; + retval.__base.fresnel.eta = ior0; + retval.__base.fresnel.etak = ior1; + retval.__base.fresnel.etak2 = ior1*ior1; + return retval; + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + query_type query; + ndf_type ggx_ndf = __base.getNDF(); + query.devsh_v = ggx_ndf.devsh_part(interaction.getNdotV2()); + query.devsh_l = ggx_ndf.devsh_part(_sample.getNdotL2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.maxNdotL = _sample.getNdotL(_clamp); + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + fresnel_type f = __base.getFresnel(); + f.clampedCosTheta = cache.getVdotH(); + return f() * DG; + } + else + return hlsl::promote(0.0); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(isocache_type) cache) + { + SGGXAnisotropicBxDF ggx_aniso = SGGXAnisotropicBxDF::create(__base.ndf.A.x, __base.ndf.A.y, __base.fresnel.eta, __base.fresnel.etak); + anisocache_type anisocache; + sample_type s = ggx_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SGGXDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } + + scalar_type ndf; + scalar_type G1_over_2NdotV; + }; + + SGGXDG1Query dg1_query; + ndf_type ggx_ndf = __base.getNDF(); + dg1_query.ndf = __base.__D(cache); + + const scalar_type devsh_v = query.getDevshV(); + dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), devsh_v); + + measure_transform_type dualMeasure = __base.template __DG1(dg1_query); + return dualMeasure.getMicrofacetMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + + spectral_type quo = hlsl::promote(0.0); + if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + ndf_type ggx_ndf = __base.getNDF(); + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + const scalar_type G2_over_G1 = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + + fresnel_type f = __base.getFresnel(); + f.clampedCosTheta = cache.getVdotH(); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +template +NBL_PARTIAL_REQ_TOP(config_concepts::MicrofacetConfiguration) +struct SGGXAnisotropicBxDF) > +{ + using this_t = SGGXAnisotropicBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + + using ndf_type = ndf::GGX; + using fresnel_type = fresnel::Conductor; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; + + struct SGGXQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + + scalar_type devsh_v; + scalar_type devsh_l; + }; + using query_type = SGGXQuery; + + // 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.__base.ndf.A = vector2_type(ax, ay); + retval.__base.ndf.ax2 = ax*ax; + retval.__base.ndf.ay2 = ay*ay; + retval.__base.ndf.a2 = ax*ay; + retval.__base.fresnel.eta = ior0; + retval.__base.fresnel.etak = ior1; + retval.__base.fresnel.etak2 = ior1*ior1; + return retval; + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + query_type query; + ndf_type ggx_ndf = __base.getNDF(); + query.devsh_v = ggx_ndf.devsh_part(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2()); + query.devsh_l = ggx_ndf.devsh_part(_sample.getTdotL2(), _sample.getBdotL2(), _sample.getNdotL2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.maxNdotL = _sample.getNdotL(_clamp); + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + fresnel_type f = __base.getFresnel(); + f.clampedCosTheta = cache.getVdotH(); + return f() * DG; + } + else + return hlsl::promote(0.0); + } + + vector3_type __generate(NBL_CONST_REF_ARG(vector3_type) localV, const vector2_type u) + { + vector2_type A = __base.ndf.A; + vector3_type V = nbl::hlsl::normalize(vector3_type(A.x*localV.x, A.y*localV.y, localV.z));//stretch view vector so that we're sampling as if roughness=1.0 + + scalar_type lensq = V.x*V.x + V.y*V.y; + vector3_type T1 = lensq > 0.0 ? vector3_type(-V.y, V.x, 0.0) * rsqrt(lensq) : vector3_type(1.0,0.0,0.0); + vector3_type T2 = cross(V,T1); + + scalar_type r = sqrt(u.x); + scalar_type phi = 2.0 * numbers::pi * u.y; + scalar_type t1 = r * cos(phi); + scalar_type t2 = r * sin(phi); + scalar_type s = 0.5 * (1.0 + V.z); + t2 = (1.0 - s)*sqrt(1.0 - t1*t1) + s*t2; + + //reprojection onto hemisphere + //TODO try it wothout the max(), not sure if -t1*t1-t2*t2>-1.0 + vector3_type H = t1*T1 + t2*T2 + sqrt(max(0.0, 1.0-t1*t1-t2*t2))*V; + //unstretch + return nbl::hlsl::normalize(vector3_type(A.x*H.x, A.y*H.y, H.z)); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) + { + const vector3_type localV = interaction.getTangentSpaceV(); + const vector3_type H = __generate(localV, u); + + cache = anisocache_type::createForReflection(localV, H); + ray_dir_info_type localL; + bxdf::Reflect r = bxdf::Reflect::create(localV, H); + localL.direction = r(cache.iso_cache.getVdotH()); + + return sample_type::createFromTangentSpace(localL, interaction.getFromTangentSpace()); + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + struct SGGXDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } + + scalar_type ndf; + scalar_type G1_over_2NdotV; + }; + + SGGXDG1Query dg1_query; + ndf_type ggx_ndf = __base.getNDF(); + dg1_query.ndf = __base.__D(cache); + + const scalar_type devsh_v = query.getDevshV(); + dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), devsh_v); + + measure_transform_type dualMeasure = __base.template __DG1(dg1_query); + return dualMeasure.getMicrofacetMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + + spectral_type quo = hlsl::promote(0.0); + if (_sample.getNdotL() > numeric_limits::min && interaction.getNdotV() > numeric_limits::min) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + ndf_type ggx_ndf = __base.getNDF(); + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = BxDFClampMode::BCM_MAX; + const scalar_type G2_over_G1 = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + + fresnel_type f = __base.getFresnel(); + f.clampedCosTheta = cache.getVdotH(); + const spectral_type reflectance = f(); + quo = reflectance * G2_over_G1; + } + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +} + +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; +}; + +} +} +} + +#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..98fce1af6c --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl @@ -0,0 +1,95 @@ +// 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/bxdf/config.hlsl" +#include "nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template) +struct SLambertianBxDF +{ + using this_t = SLambertianBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; + + static this_t create() + { + this_t retval; + // nothing here, just keeping in convention with others + return retval; + } + + scalar_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + return _sample.getNdotL(_clamp) * numbers::inv_pi; + } + scalar_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return eval(_sample, interaction.isotropic); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) + { + ray_dir_info_type L; + L.direction = sampling::ProjectedHemisphere::generate(u); + return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u) + { + return generate(anisotropic_interaction_type::create(interaction), u); + } + + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample) + { + return sampling::ProjectedHemisphere::pdf(_sample.getNdotL(_clamp)); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + sampling::quotient_and_pdf qp = sampling::ProjectedHemisphere::template quotient_and_pdf(_sample.getNdotL(_clamp)); + return quotient_pdf_type::create(qp.quotient[0], qp.pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return quotient_and_pdf(_sample, interaction.isotropic); + } +}; + +} + +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; +}; + +} +} +} + +#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..7b8885eee0 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl @@ -0,0 +1,113 @@ +// 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/bxdf/config.hlsl" +#include "nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace reflection +{ + +template) +struct SOrenNayarBxDF +{ + using this_t = SOrenNayarBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; + + 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(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + scalar_type NdotL = _sample.getNdotL(_clamp); + return NdotL * numbers::inv_pi * __rec_pi_factored_out_wo_clamps(hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()), NdotL, interaction.getNdotV(_clamp)); + } + scalar_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return eval(_sample, interaction.isotropic); + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) + { + ray_dir_info_type L; + L.direction = sampling::ProjectedHemisphere::generate(u); + return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) + { + return generate_wo_clamps(interaction, u); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u) + { + return generate_wo_clamps(anisotropic_interaction_type::create(interaction), u); + } + + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample) + { + return sampling::ProjectedHemisphere::pdf(_sample.getNdotL(_clamp)); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + scalar_type _pdf = pdf(_sample); + scalar_type q = __rec_pi_factored_out_wo_clamps(hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()), _sample.getNdotL(_clamp), interaction.getNdotV(_clamp)); + return quotient_pdf_type::create(q, _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return quotient_and_pdf(_sample, interaction.isotropic); + } + + scalar_type A; +}; + +} + +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; +}; + +} +} +} + +#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..58c1576911 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl @@ -0,0 +1,381 @@ +// 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/bxdf/bxdf_traits.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" +#include "nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template +struct SBeckmannDielectricAnisotropicBxDF; + +template) +struct SBeckmannDielectricIsotropicBxDF +{ + using this_t = SBeckmannDielectricIsotropicBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(matrix3x3_type, Config); + NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + using brdf_type = reflection::SBeckmannIsotropicBxDF; + + using ndf_type = ndf::Beckmann; + using fresnel_type = fresnel::Dielectric; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; + + struct SBeckmannQuery + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + using query_type = SBeckmannQuery; + + static this_t create(NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, scalar_type A) + { + this_t retval; + retval.__base.ndf.A = vector2_type(A, A); + retval.__base.ndf.a2 = A*A; + retval.__base.fresnel.orientedEta = orientedEta; + return retval; + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + query_type query; + ndf_type beckmann_ndf = __base.getNDF(); + query.lambda_L = beckmann_ndf.LambdaC2(_sample.getNdotL2()); + query.lambda_V = beckmann_ndf.LambdaC2(interaction.getNdotV2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SBeckmannG2overG1Query + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + + SBeckmannG2overG1Query g2_query; + g2_query.lambda_L = query.getLambdaL(); + g2_query.lambda_V = query.getLambdaV(); + + fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.absNdotV = interaction.getNdotV(_clamp); + dualMeasure.orientedEta = orientedEta.value[0]; + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getVdotH()); + return hlsl::promote(f()[0]) * DG; + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(isocache_type) cache) + { + SBeckmannDielectricAnisotropicBxDF beckmann_aniso = SBeckmannDielectricAnisotropicBxDF::create(__base.fresnel.orientedEta, __base.ndf.A.x, __base.ndf.A.y); + anisocache_type anisocache; + sample_type s = beckmann_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SBeckmannDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type ndf; + scalar_type lambda_V; + }; + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getVdotH()); + const scalar_type reflectance = f()[0]; + + SBeckmannDG1Query dg1_query; + dg1_query.ndf = __base.__D(cache); + dg1_query.lambda_V = query.getLambdaV(); + + fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; + measure_transform_type dualMeasure = __base.template __DG1(dg1_query, cache); + dualMeasure.absNdotV = interaction.getNdotV(_clamp); + dualMeasure.orientedEta = orientedEta.value[0]; + return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * dualMeasure.getProjectedLightMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + + struct SBeckmannG2overG1Query + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + + ndf_type beckmann_ndf = __base.getNDF(); + SBeckmannG2overG1Query g2_query; + g2_query.lambda_L = query.getLambdaL(); + g2_query.lambda_V = query.getLambdaV(); + scalar_type quo = beckmann_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +template +NBL_PARTIAL_REQ_TOP(config_concepts::MicrofacetConfiguration) +struct SBeckmannDielectricAnisotropicBxDF) > +{ + using this_t = SBeckmannDielectricAnisotropicBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(matrix3x3_type, Config); + NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + using brdf_type = reflection::SBeckmannAnisotropicBxDF; + + using ndf_type = ndf::Beckmann; + using fresnel_type = fresnel::Dielectric; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; + + struct SBeckmannQuery + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + using query_type = SBeckmannQuery; + + static this_t create(NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, scalar_type ax, scalar_type ay) + { + this_t retval; + retval.__base.ndf.A = vector2_type(ax, ay); + retval.__base.ndf.ax2 = ax*ax; + retval.__base.ndf.ay2 = ay*ay; + retval.__base.fresnel.orientedEta = orientedEta; + return retval; + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + query_type query; + ndf_type beckmann_ndf = __base.getNDF(); + query.lambda_L = beckmann_ndf.LambdaC2(_sample.getTdotL2(), _sample.getBdotL2(), _sample.getNdotL2()); + query.lambda_V = beckmann_ndf.LambdaC2(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + struct SBeckmannG2overG1Query + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + + SBeckmannG2overG1Query g2_query; + g2_query.lambda_L = query.getLambdaL(); + g2_query.lambda_V = query.getLambdaV(); + + fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.absNdotV = interaction.getNdotV(_clamp); + dualMeasure.orientedEta = orientedEta.value[0]; + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getVdotH()); + return hlsl::promote(f()[0]) * DG; + } + + sample_type __generate_wo_clamps(const vector3_type localV, const vector3_type H, NBL_CONST_REF_ARG(matrix3x3_type) m, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, NBL_REF_ARG(anisocache_type) cache) + { + const scalar_type localVdotH = nbl::hlsl::dot(localV,H); + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(localVdotH); + const scalar_type reflectance = f()[0]; + + scalar_type rcpChoiceProb; + bool transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); + + cache = anisocache_type::createForReflection(localV, H); + + const scalar_type VdotH = cache.iso_cache.getVdotH(); + Refract r = Refract::create(localV, H); + cache.iso_cache.LdotH = hlsl::mix(VdotH, r.getNdotT(rcpEta.value2[0]), transmitted); + ray_dir_info_type localL; + bxdf::ReflectRefract rr; + rr.refract = r; + localL.direction = rr(transmitted, rcpEta.value[0]); + + return sample_type::createFromTangentSpace(localL, m); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) + { + const vector3_type localV = interaction.getTangentSpaceV(); + + fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; + fresnel::OrientedEtaRcps rcpEta = orientedEta.getReciprocals(); + + const vector3_type upperHemisphereV = hlsl::mix(localV, -localV, interaction.getNdotV() < scalar_type(0.0)); + + spectral_type dummyior; + brdf_type beckmann = brdf_type::create(__base.ndf.A.x, __base.ndf.A.y, dummyior, dummyior); + const vector3_type H = beckmann.__generate(upperHemisphereV, u.xy); + + return __generate_wo_clamps(localV, H, interaction.getFromTangentSpace(), u, rcpEta, cache); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) + { + anisocache_type dummycache; + return generate(interaction, u, dummycache); + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + struct SBeckmannDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type ndf; + scalar_type lambda_V; + }; + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getVdotH()); + const scalar_type reflectance = f()[0]; + + SBeckmannDG1Query dg1_query; + dg1_query.ndf = __base.__D(cache); + dg1_query.lambda_V = query.getLambdaV(); + + fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; + measure_transform_type dualMeasure = __base.template __DG1(dg1_query, cache); + dualMeasure.absNdotV = interaction.getNdotV(_clamp); + dualMeasure.orientedEta = orientedEta.value[0]; + return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * dualMeasure.getProjectedLightMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + + struct SBeckmannG2overG1Query + { + using scalar_type = scalar_type; + + scalar_type getLambdaL() NBL_CONST_MEMBER_FUNC { return lambda_L; } + scalar_type getLambdaV() NBL_CONST_MEMBER_FUNC { return lambda_V; } + + scalar_type lambda_L; + scalar_type lambda_V; + }; + + ndf_type beckmann_ndf = __base.getNDF(); + SBeckmannG2overG1Query g2_query; + g2_query.lambda_L = query.getLambdaL(); + g2_query.lambda_V = query.getLambdaV(); + scalar_type quo = beckmann_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +} + +template +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/transmission/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl new file mode 100644 index 0000000000..b979984086 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl @@ -0,0 +1,407 @@ +// 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/bxdf/bxdf_traits.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" +#include "nbl/builtin/hlsl/bxdf/cook_torrance_base.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template +struct SGGXDielectricAnisotropicBxDF; + +template) +struct SGGXDielectricIsotropicBxDF +{ + using this_t = SGGXDielectricIsotropicBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(matrix3x3_type, Config); + NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + using brdf_type = reflection::SGGXIsotropicBxDF; + + using ndf_type = ndf::GGX; + using fresnel_type = fresnel::Dielectric; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; + + struct SGGXQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + + scalar_type devsh_v; + scalar_type devsh_l; + }; + using query_type = SGGXQuery; + + static this_t create(NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, scalar_type A) + { + this_t retval; + retval.__base.ndf.A = vector2_type(A, A); + retval.__base.ndf.a2 = A*A; + retval.__base.ndf.one_minus_a2 = scalar_type(1.0) - A*A; + retval.__base.fresnel.orientedEta = orientedEta; + return retval; + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + query_type query; + ndf_type ggx_ndf = __base.getNDF(); + query.devsh_v = ggx_ndf.devsh_part(interaction.getNdotV2()); + query.devsh_l = ggx_ndf.devsh_part(_sample.getNdotL2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.absNdotL = _sample.getNdotL(_clamp); + dualMeasure.orientedEta = orientedEta.value[0]; + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getVdotH()); + return hlsl::promote(f()[0]) * DG; + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(isocache_type) cache) + { + SGGXDielectricAnisotropicBxDF ggx_aniso = SGGXDielectricAnisotropicBxDF::create(__base.fresnel.orientedEta, __base.ndf.A.x, __base.ndf.A.y); + anisocache_type anisocache; + sample_type s = ggx_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache); + cache = anisocache.iso_cache; + return s; + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + struct SGGXDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } + scalar_type getOrientedEta() NBL_CONST_MEMBER_FUNC { return orientedEta; } + + scalar_type ndf; + scalar_type G1_over_2NdotV; + scalar_type orientedEta; + }; + + SGGXDG1Query dg1_query; + fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; + dg1_query.orientedEta = orientedEta.value[0]; + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getVdotH()); + const scalar_type reflectance = f()[0]; + + ndf_type ggx_ndf = __base.getNDF(); + dg1_query.ndf = __base.__D(cache); + dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), query.getDevshV()); + + measure_transform_type dualMeasure = __base.template __DG1(dg1_query, cache); + return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * dualMeasure.getMicrofacetMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + const bool transmitted = cache.isTransmission(); + + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + ndf_type ggx_ndf = __base.getNDF(); + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + scalar_type quo; + quo = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +template +NBL_PARTIAL_REQ_TOP(config_concepts::MicrofacetConfiguration) +struct SGGXDielectricAnisotropicBxDF) > +{ + using this_t = SGGXDielectricAnisotropicBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(matrix3x3_type, Config); + NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + NBL_BXDF_CONFIG_ALIAS(isocache_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisocache_type, Config); + using brdf_type = reflection::SGGXAnisotropicBxDF; + + using ndf_type = ndf::GGX; + using fresnel_type = fresnel::Dielectric; + using measure_transform_type = ndf::SDualMeasureQuant; + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; + + struct SGGXQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + + scalar_type devsh_v; + scalar_type devsh_l; + }; + using query_type = SGGXQuery; + + static this_t create(NBL_CONST_REF_ARG(fresnel::OrientedEtas) orientedEta, scalar_type ax, scalar_type ay) + { + this_t retval; + retval.__base.ndf.A = vector2_type(ax, ay); + retval.__base.ndf.ax2 = ax*ax; + retval.__base.ndf.ay2 = ay*ay; + retval.__base.ndf.a2 = ax*ay; + retval.__base.fresnel.orientedEta = orientedEta; + return retval; + } + + query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + query_type query; + ndf_type ggx_ndf = __base.getNDF(); + query.devsh_v = ggx_ndf.devsh_part(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2()); + query.devsh_l = ggx_ndf.devsh_part(_sample.getTdotL2(), _sample.getBdotL2(), _sample.getNdotL2()); + return query; + } + + spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; + measure_transform_type dualMeasure = __base.template __DG(g2_query, _sample, interaction, cache); + dualMeasure.absNdotL = _sample.getNdotL(_clamp); + dualMeasure.orientedEta = orientedEta.value[0]; + scalar_type DG = dualMeasure.getProjectedLightMeasure(); + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getVdotH()); + return hlsl::promote(f()[0]) * DG; + } + + sample_type __generate_wo_clamps(const vector3_type localV, const vector3_type H, NBL_CONST_REF_ARG(matrix3x3_type) m, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, NBL_REF_ARG(anisocache_type) cache) + { + const scalar_type localVdotH = nbl::hlsl::dot(localV,H); + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(localVdotH); + const scalar_type reflectance = f()[0]; + + scalar_type rcpChoiceProb; + bool transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); + + cache = anisocache_type::createForReflection(localV, H); + + const scalar_type VdotH = cache.iso_cache.getVdotH(); + Refract r = Refract::create(localV, H); + cache.iso_cache.LdotH = hlsl::mix(VdotH, r.getNdotT(rcpEta.value2[0]), transmitted); + ray_dir_info_type localL; + bxdf::ReflectRefract rr; + rr.refract = r; + localL.direction = rr(transmitted, rcpEta.value[0]); + + return sample_type::createFromTangentSpace(localL, m); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u, NBL_REF_ARG(anisocache_type) cache) + { + const vector3_type localV = interaction.getTangentSpaceV(); + + fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; + fresnel::OrientedEtaRcps rcpEta = orientedEta.getReciprocals(); + + const vector3_type upperHemisphereV = hlsl::mix(localV, -localV, interaction.getNdotV() < scalar_type(0.0)); + + spectral_type dummyior; + brdf_type ggx = brdf_type::create(__base.ndf.A.x, __base.ndf.A.y, dummyior, dummyior); + const vector3_type H = ggx.__generate(upperHemisphereV, u.xy); + + return __generate_wo_clamps(localV, H, interaction.getFromTangentSpace(), u, rcpEta, cache); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) + { + anisocache_type dummycache; + return generate(interaction, u, dummycache); + } + + scalar_type pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + struct SGGXDG1Query + { + using scalar_type = scalar_type; + + scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } + scalar_type getG1over2NdotV() NBL_CONST_MEMBER_FUNC { return G1_over_2NdotV; } + scalar_type getOrientedEta() NBL_CONST_MEMBER_FUNC { return orientedEta; } + + scalar_type ndf; + scalar_type G1_over_2NdotV; + scalar_type orientedEta; + }; + + SGGXDG1Query dg1_query; + fresnel::OrientedEtas orientedEta = __base.fresnel.orientedEta; + dg1_query.orientedEta = orientedEta.value[0]; + + fresnel_type f = __base.getFresnel(); + f.absCosTheta = hlsl::abs(cache.getVdotH()); + const scalar_type reflectance = f()[0]; + + ndf_type ggx_ndf = __base.getNDF(); + dg1_query.ndf = __base.__D(cache); + dg1_query.G1_over_2NdotV = ggx_ndf.G1_wo_numerator_devsh_part(interaction.getNdotV(_clamp), query.getDevshV()); + + measure_transform_type dualMeasure = __base.template __DG1(dg1_query, cache); + return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * dualMeasure.getMicrofacetMeasure(); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache) + { + scalar_type _pdf = pdf(query, interaction, cache); + const bool transmitted = cache.isTransmission(); + + struct SGGXG2XQuery + { + using scalar_type = scalar_type; + + scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } + scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } + BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } + + scalar_type devsh_v; + scalar_type devsh_l; + BxDFClampMode _clamp; + }; + + ndf_type ggx_ndf = __base.getNDF(); + SGGXG2XQuery g2_query; + g2_query.devsh_v = query.getDevshV(); + g2_query.devsh_l = query.getDevshL(); + g2_query._clamp = _clamp; + + scalar_type quo; + quo = ggx_ndf.template G2_over_G1(g2_query, _sample, interaction, cache); + + return quotient_pdf_type::create(quo, _pdf); + } + + SCookTorrance __base; +}; + +} + +template +struct traits > +{ + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; +}; + +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/transmission/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl new file mode 100644 index 0000000000..143e0767c2 --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl @@ -0,0 +1,101 @@ +// 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/bxdf/config.hlsl" +#include "nbl/builtin/hlsl/bxdf/bxdf_traits.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" +#include "nbl/builtin/hlsl/bxdf/reflection.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template) +struct SLambertianBxDF +{ + using this_t = SLambertianBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; + + static this_t create() + { + this_t retval; + // nothing here, just keeping convention with others + return retval; + } + + scalar_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + return _sample.getNdotL(_clamp) * numbers::inv_pi * 0.5; + } + scalar_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return eval(_sample, interaction.isotropic); + } + + sample_type generate_wo_clamps(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u) + { + ray_dir_info_type L; + L.direction = sampling::ProjectedSphere::generate(u); + return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u) + { + return generate_wo_clamps(interaction, u); + } + + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector3_type u) + { + return generate_wo_clamps(anisotropic_interaction_type::create(interaction), u); + } + + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample) + { + return sampling::ProjectedSphere::pdf(_sample.getNdotL(_clamp)); + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + sampling::quotient_and_pdf qp = sampling::ProjectedSphere::template quotient_and_pdf(_sample.getNdotL(_clamp)); + return quotient_pdf_type::create(qp.quotient[0], qp.pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return quotient_and_pdf(_sample, interaction.isotropic); + } +}; + +} + +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; +}; + +} +} +} + +#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..f4ebf1dfdc --- /dev/null +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -0,0 +1,218 @@ +// 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/bxdf/bxdf_traits.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace bxdf +{ +namespace transmission +{ + +template) +struct SSmoothDielectricBxDF +{ + using this_t = SSmoothDielectricBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; + + static this_t create(scalar_type eta) + { + this_t retval; + retval.eta = eta; + return retval; + } + + spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + return hlsl::promote(0); + } + spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return hlsl::promote(0); + } + + sample_type __generate_wo_clamps(const vector3_type V, const vector3_type T, const vector3_type B, const 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*orientedEta.value, absNdotV)[0]; + + scalar_type rcpChoiceProb; + transmitted = math::partitionRandVariable(reflectance, u.z, rcpChoiceProb); + + ray_dir_info_type L; + Refract r = Refract::create(V, N); + bxdf::ReflectRefract rr; + rr.refract = r; + L.direction = rr(transmitted, orientedEta.rcp[0]); + return sample_type::create(L, T, B, N); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) + { + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(interaction.getNdotV(_clamp), hlsl::promote(eta)); + fresnel::OrientedEtaRcps rcpEta = fresnel::OrientedEtaRcps::create(interaction.getNdotV(_clamp), hlsl::promote(eta)); + bool dummy; + return __generate_wo_clamps(interaction.getV().getDirection(), interaction.getT(), interaction.getB(), interaction.getN(), interaction.getNdotV(), + interaction.getNdotV(_clamp), u, orientedEta, rcpEta, dummy); + } + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) 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(sample_type) _sample) + { + return 0; + } + + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(interaction.getNdotV(), _sample.getNdotL()); + + fresnel::OrientedEtaRcps rcpOrientedEtas = fresnel::OrientedEtaRcps::create(interaction.getNdotV(_clamp), 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(quo, _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return quotient_and_pdf(_sample, interaction.isotropic); + } + + scalar_type eta; +}; + +template) +struct SSmoothThinDielectricBxDF +{ + using this_t = SSmoothThinDielectricBxDF; + NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); + NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); + NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); + NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); + NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); + NBL_BXDF_CONFIG_ALIAS(sample_type, Config); + NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); + NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; + + 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(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + return hlsl::promote(0); + } + spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return hlsl::promote(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(const vector3_type V, const vector3_type T, const vector3_type B, const 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 ? (hlsl::promote(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, T, B, N); + } + + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) + { + vector3_type dummy; + return __generate_wo_clamps(interaction.getV().getDirection(), interaction.getT(), interaction.getB(), interaction.getN(), interaction.getNdotV(), interaction.getNdotV(_clamp), u, eta2, luminosityContributionHint, dummy); + } + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) + { + return generate(anisotropic_interaction_type::create(interaction), u); + } + + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample) + { + return 0; + } + + // isotropic only? + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) + { + const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(interaction.getNdotV(), _sample.getNdotL()); + const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel::Dielectric::__call(eta2, interaction.getNdotV(_clamp))); + const spectral_type sampleValue = hlsl::mix(reflectance, hlsl::promote(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(sampleValue / sampleProb, _pdf); + } + quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) + { + return quotient_and_pdf(_sample, interaction.isotropic); + } + + spectral_type eta2; + spectral_type luminosityContributionHint; +}; + +} + +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/concepts.hlsl b/include/nbl/builtin/hlsl/concepts.hlsl index 7fd725dc2b..c51364bb5c 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 {}; \ @@ -115,6 +118,24 @@ NBL_CONSTEXPR bool NBL_CONCEPT_NAME = BOOST_PP_SEQ_FOR_EACH_I(NBL_IMPL_CONCEPT_E // TODO: counterparts of all the other concepts #endif + +#include +#include + +#define NBL_IMPL_EXPR_DECLVAL(r,data,i,_T) BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(i,0)) experimental::declval<_T>() +#define NBL_IMPL_EXPR_DECL_TEMP_ARG(r,data,i,_T) BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(i,0)) typename _T +#define NBL_IMPL_EXPR_ITER_TEMP_ARG(r,data,i,_T) BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(i,0)) _T +#define NBL_VALID_EXPRESSION(CONCEPT_NAME, FUNCTION_NAME, ARG_TYPE_LIST, ARG_TYPE_SET)\ +namespace impl\ +{\ +template\ +struct CONCEPT_NAME : false_type {};\ +template\ +struct CONCEPT_NAME(BOOST_PP_SEQ_FOR_EACH_I(NBL_IMPL_EXPR_DECLVAL, _, ARG_TYPE_SET)))> > : true_type {};\ +}\ +template\ +NBL_BOOL_CONCEPT CONCEPT_NAME = impl::CONCEPT_NAME::value;\ + } } } 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/basic.h b/include/nbl/builtin/hlsl/cpp_compat/basic.h index 3802bd69ea..87baa1f0d6 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/basic.h +++ b/include/nbl/builtin/hlsl/cpp_compat/basic.h @@ -43,6 +43,7 @@ inline To _static_cast(From v) #define NBL_CONSTEXPR_INLINE_FUNC constexpr inline #define NBL_CONSTEXPR_FORCED_INLINE_FUNC NBL_FORCE_INLINE constexpr #define NBL_CONST_MEMBER_FUNC const +#define NBL_IF_CONSTEXPR(...) if constexpr (__VA_ARGS__) namespace nbl::hlsl { @@ -73,6 +74,7 @@ namespace nbl::hlsl #define NBL_CONSTEXPR_INLINE_FUNC inline #define NBL_CONSTEXPR_FORCED_INLINE_FUNC inline #define NBL_CONST_MEMBER_FUNC +#define NBL_IF_CONSTEXPR(...) if (__VA_ARGS__) namespace nbl { 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 fba9c2f5bd..75823f448e 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl @@ -39,6 +39,21 @@ inline bool isinf_uint_impl(UnsignedInteger val) return (val & (~ieee754::traits::signMask)) == ieee754::traits::inf; } +namespace impl +{ +#ifndef __HLSL_VERSION +template +NBL_BOOL_CONCEPT MixIsCallable = always_true(), declval(), declval()))>; +#endif +template +NBL_BOOL_CONCEPT MixCallingBuiltins = +#ifdef __HLSL_VERSION +(spirv::FMixIsCallable && is_same_v) || spirv::SelectIsCallable; +#else +MixIsCallable; +#endif +} + template struct dot_helper; template @@ -141,8 +156,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) @@ -230,8 +245,8 @@ struct inverse_helper NBL_PARTIAL_REQ_TOP(always_true(experimental::declval(), experimental::declval(), experimental::declval()))>) -struct mix_helper(experimental::declval(), experimental::declval(), experimental::declval()))>) > +template NBL_PARTIAL_REQ_TOP(spirv::FMixIsCallable) +struct mix_helper) > { using return_t = conditional_t, vector::scalar_type, vector_traits::Dimension>, T>; static inline return_t __call(const T x, const T y, const T a) @@ -241,8 +256,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(spirv::SelectIsCallable) +struct mix_helper) > { 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 @@ -451,13 +466,13 @@ struct bitCount_helper }; template -requires (concepts::FloatingPointScalar && (concepts::FloatingPointScalar || concepts::BooleanScalar)) +requires (impl::MixIsCallable) struct mix_helper { using return_t = T; static inline return_t __call(const T x, const T y, const U a) { - return glm::mix(x, y, a); + return glm::mix(x, y, a); } }; @@ -867,8 +882,8 @@ struct smoothStep_helper }; template -NBL_PARTIAL_REQ_TOP(VECTOR_SPECIALIZATION_CONCEPT) -struct mix_helper +NBL_PARTIAL_REQ_TOP(VECTOR_SPECIALIZATION_CONCEPT && !impl::MixCallingBuiltins) +struct mix_helper) > { using return_t = T; static return_t __call(NBL_CONST_REF_ARG(T) x, NBL_CONST_REF_ARG(T) y, NBL_CONST_REF_ARG(T) a) @@ -886,8 +901,27 @@ struct mix_helper }; 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) > +NBL_PARTIAL_REQ_TOP(VECTOR_SPECIALIZATION_CONCEPT && !impl::MixCallingBuiltins && concepts::BooleanScalar) +struct mix_helper && 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 && !impl::MixCallingBuiltins && concepts::Boolean && concepts::Vectorial && vector_traits::Dimension == vector_traits::Dimension) +struct mix_helper && concepts::Boolean && concepts::Vectorial && vector_traits::Dimension == vector_traits::Dimension) > { 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) diff --git a/include/nbl/builtin/hlsl/glsl_compat/core.hlsl b/include/nbl/builtin/hlsl/glsl_compat/core.hlsl index 337db5e22f..9c124c6ae3 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(spirv::EqualIntrinsicCallable && concepts::Vectorial && concepts::Integral) +struct equal_helper && concepts::Vectorial && 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(spirv::EqualIntrinsicCallable && concepts::Vectorial && concepts::FloatingPoint) +struct equal_helper && concepts::Vectorial && 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..6bdfcf2514 100644 --- a/include/nbl/builtin/hlsl/ieee754.hlsl +++ b/include/nbl/builtin/hlsl/ieee754.hlsl @@ -3,6 +3,7 @@ #include #include +#include namespace nbl { @@ -142,13 +143,72 @@ 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); + // can't use mix_helper because circular dep +#ifdef __HLSL_VERSION + return bit_cast(asUint ^ spirv::select(AsUint(0ull), ieee754::traits::signMask, flip)); +#else + return bit_cast(asUint ^ (flip ? ieee754::traits::signMask : AsUint(0ull))); +#endif + } +}; + +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/macros.h b/include/nbl/builtin/hlsl/macros.h index cb4b684d54..944f06cdc9 100644 --- a/include/nbl/builtin/hlsl/macros.h +++ b/include/nbl/builtin/hlsl/macros.h @@ -34,6 +34,12 @@ inline auto functionAlias(Args&&... args) -> decltype(origFunctionName(std::forw #endif +#ifdef __HLSL_VERSION +#define NBL_UNROLL [[unroll]] +#else +#define NBL_UNROLL +#endif + #ifdef __HLSL_VERSION // cause DXC is insane #define NBL_FP64_LITERAL(LIT) LIT##l #else // and nobody except GCC supports C++23 `f64` suffix on float literals diff --git a/include/nbl/builtin/hlsl/math/angle_adding.hlsl b/include/nbl/builtin/hlsl/math/angle_adding.hlsl new file mode 100644 index 0000000000..9c1da04a0e --- /dev/null +++ b/include/nbl/builtin/hlsl/math/angle_adding.hlsl @@ -0,0 +1,127 @@ +// 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_MATH_ANGLE_ADDING_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MATH_ANGLE_ADDING_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include "nbl/builtin/hlsl/numbers.hlsl" +#include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" +#include "nbl/builtin/hlsl/concepts/vector.hlsl" +#include "nbl/builtin/hlsl/spirv_intrinsics/core.hlsl" +#include "nbl/builtin/hlsl/ieee754.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ + +namespace impl +{ +struct sincos_accumulator +{ + using this_t = sincos_accumulator; + + static this_t create() + { + this_t retval; + retval.tmp0 = 0; + retval.tmp1 = 0; + retval.tmp2 = 0; + retval.tmp3 = 0; + retval.tmp4 = 0; + retval.tmp5 = 0; + return retval; + } + + static this_t create(float cosA, float cosB, float cosC, float sinA, float sinB, float sinC) + { + this_t retval; + retval.tmp0 = cosA; + retval.tmp1 = cosB; + retval.tmp2 = cosC; + retval.tmp3 = sinA; + retval.tmp4 = sinB; + retval.tmp5 = sinC; + return retval; + } + + float getArccosSumofABC_minus_PI() + { + const bool AltminusB = tmp0 < (-tmp1); + const float cosSumAB = tmp0 * tmp1 - tmp3 * tmp4; + const bool ABltminusC = cosSumAB < (-tmp2); + const bool ABltC = cosSumAB < tmp2; + // apply triple angle formula + const float absArccosSumABC = acos(clamp(cosSumAB * tmp2 - (tmp0 * tmp4 + tmp3 * tmp1) * tmp5, -1.f, 1.f)); + return ((AltminusB ? ABltC : ABltminusC) ? (-absArccosSumABC) : absArccosSumABC) + ((AltminusB || ABltminusC) ? numbers::pi : (-numbers::pi)); + } + + static void combineCosForSumOfAcos(float cosA, float cosB, float biasA, float biasB, NBL_REF_ARG(float) out0, NBL_REF_ARG(float) out1) + { + const float bias = biasA + biasB; + const float a = cosA; + const float b = cosB; + const bool reverse = abs(min(a, b)) > max(a, b); + const float c = a * b - sqrt((1.0f - a * a) * (1.0f - b * b)); + + if (reverse) + { + out0 = -c; + out1 = bias + numbers::pi; + } + else + { + out0 = c; + out1 = bias; + } + } + + float tmp0; + float tmp1; + float tmp2; + float tmp3; + float tmp4; + float tmp5; +}; +} + +float getArccosSumofABC_minus_PI(float cosA, float cosB, float cosC, float sinA, float sinB, float sinC) +{ + impl::sincos_accumulator acc = impl::sincos_accumulator::create(cosA, cosB, cosC, sinA, sinB, sinC); + return acc.getArccosSumofABC_minus_PI(); +} + +void combineCosForSumOfAcos(float cosA, float cosB, float biasA, float biasB, NBL_REF_ARG(float) out0, NBL_REF_ARG(float) out1) +{ + impl::sincos_accumulator acc = impl::sincos_accumulator::create(); + impl::sincos_accumulator::combineCosForSumOfAcos(cosA, cosB, biasA, biasB, acc.tmp0, acc.tmp1); + out0 = acc.tmp0; + out1 = acc.tmp1; +} + +// returns acos(a) + acos(b) +float getSumofArccosAB(float cosA, float cosB) +{ + impl::sincos_accumulator acc = impl::sincos_accumulator::create(); + impl::sincos_accumulator::combineCosForSumOfAcos(cosA, cosB, 0.0f, 0.0f, acc.tmp0, acc.tmp1); + return acos(acc.tmp0) + acc.tmp1; +} + +// returns acos(a) + acos(b) + acos(c) + acos(d) +float getSumofArccosABCD(float cosA, float cosB, float cosC, float cosD) +{ + impl::sincos_accumulator acc = impl::sincos_accumulator::create(); + impl::sincos_accumulator::combineCosForSumOfAcos(cosA, cosB, 0.0f, 0.0f, acc.tmp0, acc.tmp1); + impl::sincos_accumulator::combineCosForSumOfAcos(cosC, cosD, 0.0f, 0.0f, acc.tmp2, acc.tmp3); + impl::sincos_accumulator::combineCosForSumOfAcos(acc.tmp0, acc.tmp2, acc.tmp1, acc.tmp3, acc.tmp4, acc.tmp5); + return acos(acc.tmp4) + acc.tmp5; +} + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/math/functions.hlsl b/include/nbl/builtin/hlsl/math/functions.hlsl index be341b6a12..20442c467b 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; @@ -177,73 +177,6 @@ struct conditionalAbsOrMax_helper(condAbs, limit); } }; - -struct trigonometry -{ - using this_t = trigonometry; - - static this_t create() - { - this_t retval; - retval.tmp0 = 0; - retval.tmp1 = 0; - retval.tmp2 = 0; - retval.tmp3 = 0; - retval.tmp4 = 0; - retval.tmp5 = 0; - return retval; - } - - static this_t create(float cosA, float cosB, float cosC, float sinA, float sinB, float sinC) - { - this_t retval; - retval.tmp0 = cosA; - retval.tmp1 = cosB; - retval.tmp2 = cosC; - retval.tmp3 = sinA; - retval.tmp4 = sinB; - retval.tmp5 = sinC; - return retval; - } - - float getArccosSumofABC_minus_PI() - { - const bool AltminusB = tmp0 < (-tmp1); - const float cosSumAB = tmp0 * tmp1 - tmp3 * tmp4; - const bool ABltminusC = cosSumAB < (-tmp2); - const bool ABltC = cosSumAB < tmp2; - // apply triple angle formula - const float absArccosSumABC = acos(clamp(cosSumAB * tmp2 - (tmp0 * tmp4 + tmp3 * tmp1) * tmp5, -1.f, 1.f)); - return ((AltminusB ? ABltC : ABltminusC) ? (-absArccosSumABC) : absArccosSumABC) + ((AltminusB || ABltminusC) ? numbers::pi : (-numbers::pi)); - } - - static void combineCosForSumOfAcos(float cosA, float cosB, float biasA, float biasB, NBL_REF_ARG(float) out0, NBL_REF_ARG(float) out1) - { - const float bias = biasA + biasB; - const float a = cosA; - const float b = cosB; - const bool reverse = abs(min(a, b)) > max(a, b); - const float c = a * b - sqrt((1.0f - a * a) * (1.0f - b * b)); - - if (reverse) - { - out0 = -c; - out1 = bias + numbers::pi; - } - else - { - out0 = c; - out1 = bias; - } - } - - float tmp0; - float tmp1; - float tmp2; - float tmp3; - float tmp4; - float tmp5; -}; } // @ return abs(x) if cond==true, max(x,0.0) otherwise @@ -253,38 +186,6 @@ T conditionalAbsOrMax(bool cond, T x, T limit) return impl::conditionalAbsOrMax_helper::__call(cond, x, limit); } -float getArccosSumofABC_minus_PI(float cosA, float cosB, float cosC, float sinA, float sinB, float sinC) -{ - impl::trigonometry trig = impl::trigonometry::create(cosA, cosB, cosC, sinA, sinB, sinC); - return trig.getArccosSumofABC_minus_PI(); -} - -void combineCosForSumOfAcos(float cosA, float cosB, float biasA, float biasB, NBL_REF_ARG(float) out0, NBL_REF_ARG(float) out1) -{ - impl::trigonometry trig = impl::trigonometry::create(); - impl::trigonometry::combineCosForSumOfAcos(cosA, cosB, biasA, biasB, trig.tmp0, trig.tmp1); - out0 = trig.tmp0; - out1 = trig.tmp1; -} - -// returns acos(a) + acos(b) -float getSumofArccosAB(float cosA, float cosB) -{ - impl::trigonometry trig = impl::trigonometry::create(); - impl::trigonometry::combineCosForSumOfAcos(cosA, cosB, 0.0f, 0.0f, trig.tmp0, trig.tmp1); - return acos(trig.tmp0) + trig.tmp1; -} - -// returns acos(a) + acos(b) + acos(c) + acos(d) -float getSumofArccosABCD(float cosA, float cosB, float cosC, float cosD) -{ - impl::trigonometry trig = impl::trigonometry::create(); - impl::trigonometry::combineCosForSumOfAcos(cosA, cosB, 0.0f, 0.0f, trig.tmp0, trig.tmp1); - impl::trigonometry::combineCosForSumOfAcos(cosC, cosD, 0.0f, 0.0f, trig.tmp2, trig.tmp3); - impl::trigonometry::combineCosForSumOfAcos(trig.tmp0, trig.tmp2, trig.tmp1, trig.tmp3, trig.tmp4, trig.tmp5); - return acos(trig.tmp4) + trig.tmp5; -} - template && concepts::Matricial && (matrix_traits::ColumnCount == matrix_traits::RowCount)) typename cpp_compat_intrinsics_impl::mul_helper::return_t applyChainRule(Lhs dFdG, Rhs dGdR) { diff --git a/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl b/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl index a8134ee871..f9d7cd3546 100644 --- a/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl +++ b/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl @@ -18,12 +18,6 @@ namespace math { namespace linalg { -// TODO: move to macros -#ifdef __HLSL_VERSION -#define NBL_UNROLL [[unroll]] -#else -#define NBL_UNROLL -#endif // Multiply matrices as-if extended to be filled with identity elements template @@ -82,7 +76,6 @@ vector promoted_mul(NBL_CONST_REF_ARG(matrix) lhs, const vector } return retval; } -#undef NBL_UNROLL // useful for fast computation of a Normal Matrix template diff --git a/include/nbl/builtin/hlsl/mpl.hlsl b/include/nbl/builtin/hlsl/mpl.hlsl index 2015b05b3d..8fb13db872 100644 --- a/include/nbl/builtin/hlsl/mpl.hlsl +++ b/include/nbl/builtin/hlsl/mpl.hlsl @@ -99,6 +99,14 @@ struct min }; template NBL_CONSTEXPR T min_v = min::value; + +template +struct find_lsb +{ + NBL_CONSTEXPR_STATIC_INLINE uint16_t value = log2::value; +}; +template +NBL_CONSTEXPR uint64_t find_lsb_v = find_lsb::value; } } } diff --git a/include/nbl/builtin/hlsl/numbers.hlsl b/include/nbl/builtin/hlsl/numbers.hlsl index e85e63246d..6671a44756 100644 --- a/include/nbl/builtin/hlsl/numbers.hlsl +++ b/include/nbl/builtin/hlsl/numbers.hlsl @@ -25,6 +25,8 @@ NBL_CONSTEXPR float_t inv_sqrtpi = float_t(0.5641895835477563); template NBL_CONSTEXPR float_t ln2 = float_t(0.6931471805599453); template +NBL_CONSTEXPR float_t inv_ln2 = float_t(1.44269504088896); +template NBL_CONSTEXPR float_t ln10 = float_t(2.302585092994046); template NBL_CONSTEXPR float_t sqrt2 = float_t(1.4142135623730951); @@ -35,7 +37,7 @@ NBL_CONSTEXPR float_t inv_sqrt3 = float_t(0.5773502691896257); template NBL_CONSTEXPR float_t egamma = float_t(0.5772156649015329); template -NBL_CONSTEXPR float_t phi = float_t(1.618033988749895); +NBL_CONSTEXPR float_t phi = float_t(1.618033988749895); } } 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..cb0b522e68 --- /dev/null +++ b/include/nbl/builtin/hlsl/random/dim_adaptor_recursive.hlsl @@ -0,0 +1,45 @@ +#ifndef _NBL_HLSL_RANDOM_DIM_ADAPTOR_RECURSIVE_INCLUDED_ +#define _NBL_HLSL_RANDOM_DIM_ADAPTOR_RECURSIVE_INCLUDED_ + +#include +#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(NBL_REF_ARG(rng_type) rng) + { + DimAdaptorRecursive retval; + retval.rng = rng; + return retval; + } + + return_type operator()() + { + array_set setter; + + return_type retval; + NBL_UNROLL 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..8d11d8eed2 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(const 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..f579890b1a 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(const 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..6cf3eff842 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(const 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(const 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_spheres.hlsl b/include/nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl new file mode 100644 index 0000000000..80a3cd2c52 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/cos_weighted_spheres.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_SPHERES_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_COS_WEIGHTED_SPHERES_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(hlsl::promote(1.0), pdf(L)); + } + + template > + static sampling::quotient_and_pdf quotient_and_pdf(vector_t3 L) + { + return sampling::quotient_and_pdf::create(hlsl::promote(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(hlsl::promote(1.0), pdf(L)); + } + + template > + static sampling::quotient_and_pdf quotient_and_pdf(vector_t3 L) + { + return sampling::quotient_and_pdf::create(hlsl::promote(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..26a62ea617 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl @@ -0,0 +1,54 @@ +// 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 +{ + +// finally fixed the semantic F-up, value/pdf = quotient not remainder +template && concepts::FloatingPointLikeScalar

) +struct quotient_and_pdf +{ + using this_t = quotient_and_pdf; + using scalar_q = typename vector_traits::scalar_type; + + static this_t create(const Q _quotient, const P _pdf) + { + this_t retval; + retval.quotient = _quotient; + retval.pdf = _pdf; + return retval; + } + + static this_t create(const scalar_q _quotient, const P _pdf) + { + this_t retval; + retval.quotient = hlsl::promote(_quotient); + retval.pdf = _pdf; + return retval; + } + + Q value() + { + return quotient*pdf; + } + + Q quotient; + P pdf; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/uniform_spheres.hlsl b/include/nbl/builtin/hlsl/sampling/uniform_spheres.hlsl new file mode 100644 index 0000000000..df4100db9b --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/uniform_spheres.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_SPHERES_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_UNIFORM_SPHERES_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(hlsl::promote(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(hlsl::promote(1.0), pdf()); + } +}; +} + +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/shapes/aabb.hlsl b/include/nbl/builtin/hlsl/shapes/aabb.hlsl index d07b38df37..5b4b1be39d 100644 --- a/include/nbl/builtin/hlsl/shapes/aabb.hlsl +++ b/include/nbl/builtin/hlsl/shapes/aabb.hlsl @@ -90,7 +90,6 @@ struct union_helper> return retval; } }; -#define NBL_UNROLL [[unroll]] // without a translation component template struct transform_helper,matrix > @@ -131,7 +130,6 @@ struct transform_helper,matrix > return retval; } }; -#undef NBL_UNROLL } } diff --git a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl index 4885fc11f8..738efab706 100644 --- a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl +++ b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace nbl @@ -332,11 +333,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)]] @@ -347,19 +348,26 @@ template) SubBorrowOutput subBorrow(T operand1, T operand2); -template && !is_matrix_v) +template || concepts::IntVector) [[vk::ext_instruction(spv::OpIEqual)]] conditional_t, vector::Dimension>, bool> IEqual(T lhs, T rhs); -template && !is_matrix_v) +template || concepts::FloatingPointVector) [[vk::ext_instruction(spv::OpFOrdEqual)]] conditional_t, vector::Dimension>, bool> FOrdEqual(T lhs, T rhs); +NBL_VALID_EXPRESSION(IEqualIsCallable, IEqual, (T), (T)(T)) +NBL_VALID_EXPRESSION(FOrdEqualIsCallable, FOrdEqual, (T), (T)(T)) -template && !is_matrix_v && is_same_v::scalar_type, bool>) +template +NBL_BOOL_CONCEPT EqualIntrinsicCallable = IEqualIsCallable || FOrdEqualIsCallable; + +template && (!concepts::Vector || (concepts::Vector && vector_traits::Dimension==vector_traits::Dimension))) [[vk::ext_instruction(spv::OpSelect)]] T select(U a, T x, T y); +NBL_VALID_EXPRESSION(SelectIsCallable, select, (T)(U), (U)(T)(T)) + } #endif diff --git a/include/nbl/builtin/hlsl/spirv_intrinsics/glsl.std.450.hlsl b/include/nbl/builtin/hlsl/spirv_intrinsics/glsl.std.450.hlsl index feda59e45c..b08087a299 100644 --- a/include/nbl/builtin/hlsl/spirv_intrinsics/glsl.std.450.hlsl +++ b/include/nbl/builtin/hlsl/spirv_intrinsics/glsl.std.450.hlsl @@ -95,6 +95,9 @@ template) [[vk::ext_instruction(GLSLstd450::GLSLstd450FMix, "GLSL.std.450")]] T fMix(T x, T y, T a); +template +NBL_BOOL_CONCEPT FMixIsCallable = always_true(experimental::declval(), experimental::declval(), experimental::declval()))>; + template::Square) [[vk::ext_instruction(GLSLstd450::GLSLstd450Determinant, "GLSL.std.450")]] typename matrix_traits::scalar_type determinant(NBL_CONST_REF_ARG(SquareMatrix) mat); diff --git a/include/nbl/builtin/hlsl/tgmath.hlsl b/include/nbl/builtin/hlsl/tgmath.hlsl index c2f784cfc0..04765f3ba7 100644 --- a/include/nbl/builtin/hlsl/tgmath.hlsl +++ b/include/nbl/builtin/hlsl/tgmath.hlsl @@ -231,6 +231,24 @@ inline FrexpOutput frexpStruct(NBL_CONST_REF_ARG(T) val) return tgmath_impl::frexpStruct_helper::__call(val); } +template +inline T l2gamma(NBL_CONST_REF_ARG(T) val) +{ + return tgmath_impl::l2gamma_helper::__call(val); +} + +template +inline T lgamma(NBL_CONST_REF_ARG(T) val) +{ + return tgmath_impl::lgamma_helper::__call(val); +} + +template +inline T beta(NBL_CONST_REF_ARG(T) v1, NBL_CONST_REF_ARG(T) v2) +{ + return tgmath_impl::beta_helper::__call(v1, v2)/tgmath_impl::beta_helper::__call(T(1.0), T(1.0)); // ensure beta(1,1)==1 +} + } } diff --git a/include/nbl/builtin/hlsl/tgmath/impl.hlsl b/include/nbl/builtin/hlsl/tgmath/impl.hlsl index e0bdaf731a..68639e7d43 100644 --- a/include/nbl/builtin/hlsl/tgmath/impl.hlsl +++ b/include/nbl/builtin/hlsl/tgmath/impl.hlsl @@ -8,6 +8,7 @@ #include #include #include +#include // C++ includes #ifndef __HLSL_VERSION @@ -90,6 +91,13 @@ struct modfStruct_helper; template struct frexpStruct_helper; +template +struct l2gamma_helper; +template +struct lgamma_helper; +template +struct beta_helper; + #ifdef __HLSL_VERSION #define DECLVAL(r,data,i,_T) BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(i,0)) experimental::declval<_T>() @@ -203,7 +211,6 @@ struct erf_helper +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) +struct l2gamma_helper) > +{ + // implementation derived from Numerical Recipes in C, transformed for log2 + static T __call(T x) + { + // TODO: need a way to silence warning about thresholds being too large for T + const T thresholds[4] = { 0, 5e4, 1e36, 1e305 }; // threshold values gotten from testing when the function returns nan/inf + if (x > thresholds[mpl::find_lsb_v]) + return bit_cast(numeric_limits::infinity); + + T a = x + T(5.5); + a = a * numbers::inv_ln2 - (x + T(0.5)) * log2_helper::__call(a); + + T b = x; + T ser = T(1.000000000190015); + ser += T(76.18009172947146) / ++b; + ser += T(-86.50532032941677) / ++b; + ser += T(24.01409824083091) / ++b; + ser += T(-1.231739572450155) / ++b; + if (sizeof(T) > 4) + { + ser += T(0.1208650973866179e-2) / ++b; + ser += T(-0.5395239384953e-5) / ++b; + } + return -a + log2_helper::__call(T(2.5066282746310005) * ser / x); + } +}; + +// logn-gamma function +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) +struct lgamma_helper) > +{ + static T __call(T val) + { + const T r = T(1.0) / log2_helper::__call(numbers::e); + return r * l2gamma_helper::__call(val); + } +}; + +// beta function +template +NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar) +struct beta_helper) > +{ + // implementation from Numerical Recipes in C, 2nd ed. + static T __call(T v1, T v2) + { + const T thresholds[4] = { 0, 2e4, 1e6, 1e15 }; // threshold values gotten from testing when the function returns nan/inf/1 + if (v1+v2 > thresholds[mpl::find_lsb_v]) + return T(0.0); + + return exp2_helper::__call(l2gamma_helper::__call(v1) + l2gamma_helper::__call(v2) - l2gamma_helper::__call(v1+v2)); + } +}; + #ifdef __HLSL_VERSION // SPIR-V already defines specializations for builtin vector types #define VECTOR_SPECIALIZATION_CONCEPT concepts::Vectorial && !is_vector_v diff --git a/include/nbl/builtin/hlsl/type_traits.hlsl b/include/nbl/builtin/hlsl/type_traits.hlsl index a6f9ad0655..a9701619dd 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 {}; @@ -619,12 +598,6 @@ using is_aggregate = std::is_aggregate; template using type_identity = std::type_identity; -template -using rank = std::rank; - -template -struct extent : std::extent {}; - template using enable_if = std::enable_if; @@ -663,7 +636,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 +653,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 +688,51 @@ template NBL_CONSTEXPR bool is_matrix_v = is_matrix::value; +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> {}; + + +// 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 3b9fe1c39a..4c645c1c9d 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -295,6 +295,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/linalg/transform.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/functions.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/geometry.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/intutil.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/angle_adding.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/quadratic.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/cubic.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/math/equations/quartic.hlsl") @@ -317,6 +318,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_spheres.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/uniform_spheres.hlsl") # LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/ndarray_addressing.hlsl") # @@ -326,6 +331,27 @@ 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/config.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/cook_torrance_base.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/bxdf_traits.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/ndf/beckmann.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/ndf/ggx.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/beckmann.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/ggx.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/lambertian.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/reflection/oren_nayar.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/beckmann.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/ggx.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/lambertian.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/bxdf/transmission/smooth_dielectric.hlsl") #subgroup LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/subgroup/ballot.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/subgroup/basic.hlsl")