diff --git a/examples_tests b/examples_tests index 829ea34183..d4e5754b04 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 829ea34183a0a62a3bd68ded4dd9e451b97126d4 +Subproject commit d4e5754b0400499ec8d8bbae4331fe3526944773 diff --git a/include/nbl/builtin/glsl/utils/morton.glsl b/include/nbl/builtin/glsl/utils/morton.glsl index de3be8b9c7..fd07a9cad8 100644 --- a/include/nbl/builtin/glsl/utils/morton.glsl +++ b/include/nbl/builtin/glsl/utils/morton.glsl @@ -22,6 +22,18 @@ uint nbl_glsl_morton_decode2d8bComponent(in uint x) return x; } +uint nbl_glsl_morton_decode2d32bComponent(in uint x) +{ + x &= 0x55555555u; + x = (x ^ (x >> 1u)) & 0x33333333u; + x = (x ^ (x >> 2u)) & 0x0f0f0f0fu; + x = (x ^ (x >> 4u)) & 0x00ff00ffu; + x = (x ^ (x >> 8u)) & 0x0000ffffu; + x = (x ^ (x >> 16u)); + return x; +} + + uvec2 nbl_glsl_morton_decode2d4b(in uint x) { return uvec2(nbl_glsl_morton_decode2d4bComponent(x), nbl_glsl_morton_decode2d4bComponent(x >> 1u)); @@ -32,4 +44,9 @@ uvec2 nbl_glsl_morton_decode2d8b(in uint x) return uvec2(nbl_glsl_morton_decode2d8bComponent(x), nbl_glsl_morton_decode2d8bComponent(x >> 1u)); } +uvec2 nbl_glsl_morton_decode2d32b(in uint x) +{ + return uvec2(nbl_glsl_morton_decode2d32bComponent(x), nbl_glsl_morton_decode2d32bComponent(x >> 1u)); +} + #endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl index a185dc8d98..e88d5fccb7 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -130,19 +130,22 @@ struct SCookTorrance template, typename C=bool_constant NBL_FUNC_REQUIRES(C::value && !fresnel_type::ReturnsMonochrome) - static scalar_type __getScaledReflectance(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(Interaction) interaction, scalar_type clampedVdotH) + static scalar_type __getScaledReflectance(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(Interaction) interaction, scalar_type clampedVdotH, bool transmitted, NBL_REF_ARG(spectral_type) outFresnelVal) { spectral_type throughputWeights = interaction.getLuminosityContributionHint(); - return hlsl::dot(impl::__implicit_promote::__call(orientedFresnel(clampedVdotH)), throughputWeights); + spectral_type reflectance = orientedFresnel(clampedVdotH); + outFresnelVal = hlsl::mix(reflectance, hlsl::promote(1.0)-reflectance, transmitted); + return hlsl::dot(outFresnelVal, throughputWeights); } template, typename C=bool_constant NBL_FUNC_REQUIRES(C::value && fresnel_type::ReturnsMonochrome) - static scalar_type __getScaledReflectance(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(Interaction) interaction, scalar_type clampedVdotH) + static scalar_type __getScaledReflectance(NBL_CONST_REF_ARG(fresnel_type) orientedFresnel, NBL_CONST_REF_ARG(Interaction) interaction, scalar_type clampedVdotH, bool transmitted, NBL_REF_ARG(spectral_type) outFresnelVal) { - return orientedFresnel(clampedVdotH)[0]; + scalar_type reflectance = orientedFresnel(clampedVdotH)[0]; + return hlsl::mix(reflectance, scalar_type(1.0)-reflectance, transmitted); } - bool __dotIsUnity(const vector3_type a, const vector3_type b, const scalar_type value) + bool __dotIsValue(const vector3_type a, const vector3_type b, const scalar_type value) { const scalar_type ab = hlsl::dot(a, b); return hlsl::max(ab, value / ab) <= scalar_type(value + 1e-3); @@ -209,11 +212,11 @@ struct SCookTorrance ray_dir_info_type V = interaction.getV(); const matrix3x3_type fromTangent = interaction.getFromTangentSpace(); // tangent frame orthonormality - assert(__dotIsUnity(fromTangent[0],fromTangent[1],0.0)); - assert(__dotIsUnity(fromTangent[1],fromTangent[2],0.0)); - assert(__dotIsUnity(fromTangent[2],fromTangent[0],0.0)); + assert(__dotIsValue(fromTangent[0],fromTangent[1],0.0)); + assert(__dotIsValue(fromTangent[1],fromTangent[2],0.0)); + assert(__dotIsValue(fromTangent[2],fromTangent[0],0.0)); // NDF sampling produced a unit length direction - assert(__dotIsUnity(localH,localH,1.0)); + assert(__dotIsValue(localH,localH,1.0)); const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH); Refract r = Refract::create(V.getDirection(), H); @@ -294,7 +297,8 @@ struct SCookTorrance assert(NdotV*VdotH >= scalar_type(0.0)); } - const scalar_type reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(VdotH)); + spectral_type dummy; + const scalar_type reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(VdotH), false, dummy); scalar_type rcpChoiceProb; scalar_type z = u.z; @@ -337,8 +341,9 @@ struct SCookTorrance NBL_IF_CONSTEXPR(IsBSDF) { - const scalar_type reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(cache.getVdotH())); - return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * DG1.projectedLightMeasure; + spectral_type dummy; + const scalar_type reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(cache.getVdotH()), cache.isTransmission(), dummy); + return reflectance * DG1.projectedLightMeasure; } else { @@ -389,10 +394,9 @@ struct SCookTorrance quo = hlsl::promote(G2_over_G1); else { - const scalar_type scaled_reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(cache.getVdotH())); - spectral_type reflectance = impl::__implicit_promote::__call(_f(hlsl::abs(cache.getVdotH()))); - quo = hlsl::mix(reflectance / scaled_reflectance, - (hlsl::promote(1.0) - reflectance) / (scalar_type(1.0) - scaled_reflectance), cache.isTransmission()) * G2_over_G1; + spectral_type reflectance; + const scalar_type scaled_reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(cache.getVdotH()), cache.isTransmission(), reflectance); + quo = reflectance / scaled_reflectance * G2_over_G1; } } else @@ -409,6 +413,18 @@ struct SCookTorrance fresnel_type fresnel; // always front-facing }; + +template +struct traits > +{ + using __type = SCookTorrance; + + NBL_CONSTEXPR_STATIC_INLINE BxDFType type = conditional_value<__type::IsBSDF, BxDFType, BxDFType::BT_BSDF, BxDFType::BT_BRDF>::value; + NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = !__type::IsBSDF; + NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = !__type::IsBSDF; +}; + } } } diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index ebad0a925c..c114222c7c 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -615,7 +615,7 @@ struct SIsotropicMicrofacetCache // not coming from the medium (reflected) OR // exiting at the macro scale AND ( (not L outside the cone of possible directions given IoR with constraint VdotH*LdotH<0.0) OR (microfacet not facing toward the macrosurface, i.e. non heightfield profile of microsurface) ) - const bool valid = ComputeMicrofacetNormal::isValidMicrofacet(transmitted, VdotL, retval.absNdotH, computeMicrofacetNormal.orientedEta); + const bool valid = ComputeMicrofacetNormal::isValidMicrofacet(transmitted, VdotL, retval.absNdotH, fresnel::OrientedEtas::create(1.0, computeMicrofacetNormal.orientedEta)); if (valid) { retval.VdotH = hlsl::dot(computeMicrofacetNormal.V,H); @@ -638,7 +638,7 @@ struct SIsotropicMicrofacetCache const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(NdotV,NdotL); ComputeMicrofacetNormal computeMicrofacetNormal = ComputeMicrofacetNormal::create(V,L,N,1.0); - computeMicrofacetNormal.orientedEta = orientedEtas; + computeMicrofacetNormal.orientedEta = orientedEtas.value[0]; return create(transmitted, computeMicrofacetNormal, VdotL, N, H); } @@ -664,7 +664,7 @@ struct SIsotropicMicrofacetCache const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(interaction.getNdotV(),_sample.getNdotL()); ComputeMicrofacetNormal computeMicrofacetNormal = ComputeMicrofacetNormal::create(V,L,N,1.0); - computeMicrofacetNormal.orientedEta = orientedEtas; + computeMicrofacetNormal.orientedEta = orientedEtas.value[0]; return create(transmitted, computeMicrofacetNormal, hlsl::dot(V, L), N, H); } diff --git a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl index 56ea88080c..f7655e9978 100644 --- a/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl @@ -141,7 +141,7 @@ struct ComputeMicrofacetNormal 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); + const scalar_type etaFactor = hlsl::mix(scalar_type(1.0), orientedEta, _refract); vector_type tmpH = V + L * etaFactor; tmpH = ieee754::flipSign(tmpH, _refract && orientedEta > scalar_type(1.0)); return tmpH; @@ -497,7 +497,7 @@ struct Dielectric }; // adapted from https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html -template +template struct Iridescent; namespace impl @@ -543,47 +543,50 @@ struct iridescent_helper return xyz / scalar_type(1.0685e-7); } - template - static T __call(NBL_CONST_REF_ARG(Params) params, const scalar_type clampedCosTheta) + template + static T __call(const vector_type _D, const vector_type eta12, const vector_type eta23, const vector_type etak23, const scalar_type clampedCosTheta) { - const vector_type wavelengths = vector_type(colorspace::scRGB::wavelength_R, colorspace::scRGB::wavelength_G, colorspace::scRGB::wavelength_B); + const vector_type wavelengths = vector_type(Colorspace::wavelength_R, Colorspace::wavelength_G, Colorspace::wavelength_B); - const vector_type eta12 = params.getEta12(); - const vector_type eta23 = params.getEta23(); - const vector_type etak23 = params.getEtak23(); const scalar_type cosTheta_1 = clampedCosTheta; - vector_type cosTheta_2; - vector_type R12p, R23p, R12s, R23s; - const vector_type scale = scalar_type(1.0)/eta12; - const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(1.0-cosTheta_1*cosTheta_1) * scale * scale; - - cosTheta_2 = hlsl::sqrt(hlsl::max(cosTheta2_2, hlsl::promote(0.0))); - Dielectric::__polarized(eta12, hlsl::promote(cosTheta_1), R12p, R12s); + vector_type cosTheta_2; + vector::Dimension> notTIR; + { + const vector_type scale = scalar_type(1.0)/eta12; + const vector_type cosTheta2_2 = hlsl::promote(1.0) - hlsl::promote(scalar_type(1.0)-cosTheta_1*cosTheta_1) * scale * scale; + notTIR = cosTheta2_2 > hlsl::promote(0.0); + cosTheta_2 = hlsl::sqrt(hlsl::max(cosTheta2_2, hlsl::promote(0.0))); + } - // Reflected part by the base - // if kappa==0, base material is dielectric - NBL_IF_CONSTEXPR(SupportsTransmission) - Dielectric::__polarized(eta23 * eta23, cosTheta_2, R23p, R23s); - else + if (hlsl::any(notTIR)) { - vector_type etaLen2 = eta23 * eta23 + etak23 * etak23; - Conductor::__polarized(eta23, etaLen2, cosTheta_2, R23p, R23s); + Dielectric::__polarized(eta12, hlsl::promote(cosTheta_1), R12p, R12s); + + // Reflected part by the base + // if kappa==0, base material is dielectric + NBL_IF_CONSTEXPR(SupportsTransmission) + Dielectric::__polarized(eta23 * eta23, cosTheta_2, R23p, R23s); + else + { + vector_type etaLen2 = eta23 * eta23 + etak23 * etak23; + Conductor::__polarized(eta23, etaLen2, cosTheta_2, R23p, R23s); + } } // Check for total internal reflection - R12s = hlsl::mix(R12s, hlsl::promote(1.0), cosTheta2_2 <= hlsl::promote(0.0)); - R12p = hlsl::mix(R12p, hlsl::promote(1.0), cosTheta2_2 <= hlsl::promote(0.0)); - - R23s = hlsl::mix(R23s, hlsl::promote(0.0), cosTheta2_2 <= hlsl::promote(0.0)); - R23p = hlsl::mix(R23p, hlsl::promote(0.0), cosTheta2_2 <= hlsl::promote(0.0)); + const vector_type notTIRFactor = vector_type(notTIR); // 0 when TIR, 1 otherwise + R12s = R12s * notTIRFactor; + R12p = R12p * notTIRFactor; + R23s = R23s * notTIRFactor; + R23p = R23p * notTIRFactor; // Compute the transmission coefficients vector_type T121p = hlsl::promote(1.0) - R12p; vector_type T121s = hlsl::promote(1.0) - R12s; // Optical Path Difference - const vector_type D = hlsl::promote(2.0 * params.getDinc()) * params.getThinFilmIor() * cosTheta_2; + const vector_type D = _D * cosTheta_2; const vector_type Dphi = hlsl::promote(2.0 * numbers::pi) * D / wavelengths; vector_type phi21p, phi21s, phi23p, phi23s, r123s, r123p, Rs; @@ -634,82 +637,115 @@ struct iridescent_helper } }; -template) +template) struct iridescent_base { using scalar_type = typename vector_traits::scalar_type; using vector_type = T; - scalar_type getDinc() NBL_CONST_MEMBER_FUNC { return Dinc; } - vector_type getThinFilmIor() NBL_CONST_MEMBER_FUNC { return thinFilmIor; } + vector_type getD() NBL_CONST_MEMBER_FUNC { return D; } vector_type getEta12() NBL_CONST_MEMBER_FUNC { return eta12; } vector_type getEta23() NBL_CONST_MEMBER_FUNC { return eta23; } - vector_type getEtak23() NBL_CONST_MEMBER_FUNC - { - NBL_IF_CONSTEXPR(SupportsTransmission) - return hlsl::promote(0.0); - else - return etak23; - } - scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm - vector_type thinFilmIor; + vector_type D; vector_type eta12; // outside (usually air 1.0) -> thin-film IOR vector_type eta23; // thin-film -> base material IOR - vector_type etak23; // thin-film -> complex component, k==0 makes dielectric }; } -template +template NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial) -struct Iridescent) > +struct Iridescent) > : impl::iridescent_base { - using this_t = Iridescent; + using this_t = Iridescent; using scalar_type = typename vector_traits::scalar_type; using vector_type = T; // assert dim==3? using eta_type = vector_type; - using base_type = impl::iridescent_base; + using base_type = impl::iridescent_base; NBL_CONSTEXPR_STATIC_INLINE bool ReturnsMonochrome = vector_traits::Dimension == 1; + struct SCreationParams + { + scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm + vector_type ior1; // outside (usually air 1.0) + vector_type ior2; // thin-film ior + vector_type ior3; // base mat ior + vector_type iork3; + }; + using creation_params_type = SCreationParams; + + static this_t create(NBL_CONST_REF_ARG(creation_params_type) params) + { + this_t retval; + retval.D = hlsl::promote(2.0 * params.Dinc) * params.ior2; + retval.eta12 = params.ior2/params.ior1; + retval.eta23 = params.ior3/params.ior2; + retval.etak23 = params.iork3/params.ior2; + return retval; + } + T operator()(const scalar_type clampedCosTheta) NBL_CONST_MEMBER_FUNC { - return impl::iridescent_helper::template __call(__base, clampedCosTheta); + return impl::iridescent_helper::template __call(base_type::getD(), base_type::getEta12(), base_type::getEta23(), getEtak23(), clampedCosTheta); } OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { OrientedEtaRcps rcpEta; - rcpEta.value = hlsl::promote(1.0) / __base.eta23; + rcpEta.value = hlsl::promote(1.0) / base_type::eta23; rcpEta.value2 = rcpEta.value * rcpEta.value; return rcpEta; } - base_type __base; + vector_type getEtak23() NBL_CONST_MEMBER_FUNC + { + return etak23; + } + + vector_type etak23; // thin-film -> complex component }; -template +template NBL_PARTIAL_REQ_TOP(concepts::FloatingPointLikeVectorial) -struct Iridescent) > +struct Iridescent) > : impl::iridescent_base { - using this_t = Iridescent; + using this_t = Iridescent; using scalar_type = typename vector_traits::scalar_type; using vector_type = T; // assert dim==3? using eta_type = vector; - using base_type = impl::iridescent_base; + using base_type = impl::iridescent_base; NBL_CONSTEXPR_STATIC_INLINE bool ReturnsMonochrome = vector_traits::Dimension == 1; + struct SCreationParams + { + scalar_type Dinc; // thickness of thin film in nanometers, rec. 100-25000nm + vector_type ior1; // outside (usually air 1.0) + vector_type ior2; // thin-film ior + vector_type ior3; // base mat ior + }; + using creation_params_type = SCreationParams; + + static this_t create(NBL_CONST_REF_ARG(creation_params_type) params) + { + this_t retval; + retval.D = hlsl::promote(2.0 * params.Dinc) * params.ior2; + retval.eta12 = params.ior2/params.ior1; + retval.eta23 = params.ior3/params.ior2; + return retval; + } + T operator()(const scalar_type clampedCosTheta) NBL_CONST_MEMBER_FUNC { - return impl::iridescent_helper::template __call(__base, clampedCosTheta); + return impl::iridescent_helper::template __call(base_type::getD(), base_type::getEta12(), base_type::getEta23(), getEtak23(), clampedCosTheta); } - scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return __base.eta23[0]; } + scalar_type getRefractionOrientedEta() NBL_CONST_MEMBER_FUNC { return base_type::eta23[0]; } OrientedEtaRcps getOrientedEtaRcps() NBL_CONST_MEMBER_FUNC { OrientedEtaRcps rcpEta; - rcpEta.value = hlsl::promote(1.0) / __base.eta23[0]; + rcpEta.value = hlsl::promote(1.0) / base_type::eta23[0]; rcpEta.value2 = rcpEta.value * rcpEta.value; return rcpEta; } @@ -718,15 +754,16 @@ struct Iridescent(1.0)/__base.eta12, flip); - orientedFresnel.__base.eta23 = hlsl::mix(__base.eta23, hlsl::promote(1.0)/__base.eta23, flip); - orientedFresnel.__base.etak23 = hlsl::promote(0.0); + orientedFresnel.D = base_type::D; + orientedFresnel.eta12 = hlsl::mix(base_type::eta12, hlsl::promote(1.0)/base_type::eta12, flip); + orientedFresnel.eta23 = hlsl::mix(base_type::eta23, hlsl::promote(1.0)/base_type::eta23, flip); return orientedFresnel; } - base_type __base; + vector_type getEtak23() NBL_CONST_MEMBER_FUNC + { + return hlsl::promote(0.0); + } }; diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl index 1406bc8d4f..c719bbfd4e 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/beckmann.hlsl @@ -339,8 +339,8 @@ struct Beckmann if (isInfinity) { quant_type dmq; - dmq.microfacetMeasure = scalar_type(0.0); - dmq.projectedLightMeasure = scalar_type(0.0); + dmq.microfacetMeasure = bit_cast(numeric_limits::infinity); + dmq.projectedLightMeasure = bit_cast(numeric_limits::infinity); return dmq; } scalar_type dg1 = D / (scalar_type(1.0) + query.getLambdaV()); diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index 0e2e9d1291..4ad4bb341e 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -279,8 +279,8 @@ struct GGX quant_type dmq; if (isInfinity) { - dmq.microfacetMeasure = scalar_type(0.0); - dmq.projectedLightMeasure = scalar_type(0.0); + dmq.microfacetMeasure = bit_cast(numeric_limits::infinity); + dmq.projectedLightMeasure = bit_cast(numeric_limits::infinity); return dmq; } @@ -337,8 +337,8 @@ struct GGX if (isInfinity) { quant_type dmq; - dmq.microfacetMeasure = scalar_type(0.0); - dmq.projectedLightMeasure = scalar_type(0.0); + dmq.microfacetMeasure = bit_cast(numeric_limits::infinity); + dmq.projectedLightMeasure = bit_cast(numeric_limits::infinity); return dmq; } dg *= correlated_wo_numerator(query, _sample, interaction, cache); diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl index f37d0d9fd8..cb7743e02d 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/beckmann.hlsl @@ -27,23 +27,7 @@ using SBeckmannAnisotropic = SCookTorrance -struct traits > -{ - NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; - NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; -}; - -template -struct traits > -{ - NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; - NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; -}; +// inherit trait from cook torrance base } } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl index 049480afab..0f49d0be43 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/ggx.hlsl @@ -27,23 +27,7 @@ using SGGXAnisotropic = SCookTorrance -struct traits > -{ - NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; - NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; -}; - -template -struct traits > -{ - NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; - NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; -}; +// inherit trait from cook torrance base } } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl index 07762d1298..a6120233bb 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/iridescent.hlsl @@ -16,18 +16,11 @@ namespace reflection { template -using SIridescent = SCookTorrance, fresnel::Iridescent >; +using SIridescent = SCookTorrance, fresnel::Iridescent >; } -template -struct traits > -{ - NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BRDF; - NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; -}; +// inherit trait from cook torrance base } } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl index fa315b40ea..8c61692c5c 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/beckmann.hlsl @@ -27,23 +27,7 @@ using SBeckmannDielectricAnisotropic = SCookTorrance -struct traits > -{ - NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; - NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; -}; - -template -struct traits > -{ - NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; - NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; -}; +// inherit trait from cook torrance base } } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl index 51f096532b..cdd4483c7f 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/ggx.hlsl @@ -27,23 +27,7 @@ using SGGXDielectricAnisotropic = SCookTorrance -struct traits > -{ - NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; - NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; -}; - -template -struct traits > -{ - NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; - NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; -}; +// inherit trait from cook torrance base } } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl index 2e7aa0e56e..05b1753aca 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/iridescent.hlsl @@ -16,18 +16,11 @@ namespace transmission { template -using SIridescent = SCookTorrance, fresnel::Iridescent >; +using SIridescent = SCookTorrance, fresnel::Iridescent >; } -template -struct traits > -{ - NBL_CONSTEXPR_STATIC_INLINE BxDFType type = BT_BSDF; - NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; - NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; -}; +// inherit trait from cook torrance base } } diff --git a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl index c272eeb1ab..8c7251214d 100644 --- a/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl +++ b/include/nbl/builtin/hlsl/concepts/accessors/loadable_image.hlsl @@ -16,8 +16,15 @@ namespace concepts { namespace accessors { + +// concept `LoadableImage` translates to smth like this: +//template +//concept LoadableImage = requires(U a, vector uv, uint16_t layer) { +// ::nbl::hlsl::is_same_v().template get(uv,layer)), vector>; +//}; + // declare concept -#define NBL_CONCEPT_NAME StorableImage +#define NBL_CONCEPT_NAME LoadableImage #define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t) #define NBL_CONCEPT_TPLT_PRM_NAMES (U)(T)(Dims) // not the greatest syntax but works @@ -26,12 +33,12 @@ namespace accessors #define NBL_CONCEPT_PARAM_2 (layer,uint16_t) // start concept NBL_CONCEPT_BEGIN(3) -// need to be defined AFTER the cocnept begins +// need to be defined AFTER the concept begins #define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define uv NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define layer NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer)) , ::nbl::hlsl::is_same_v, vector)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((a.template get(uv,layer)), ::nbl::hlsl::is_same_v, vector)) ); #undef layer #undef uv @@ -39,7 +46,7 @@ NBL_CONCEPT_END( #include // declare concept -#define NBL_CONCEPT_NAME MipmappedStorableImage +#define NBL_CONCEPT_NAME MipmappedLoadableImage #define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t) #define NBL_CONCEPT_TPLT_PRM_NAMES (U)(T)(Dims) // not the greatest syntax but works 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 cd89ce45d1..0c595bb0e2 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl @@ -256,8 +256,8 @@ struct mix_helper) > }; template -NBL_PARTIAL_REQ_TOP(spirv::SelectIsCallable) -struct mix_helper) > +NBL_PARTIAL_REQ_TOP(spirv::SelectIsCallable && concepts::Boolean) +struct mix_helper && concepts::Boolean) > { 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 diff --git a/include/nbl/builtin/hlsl/limits.hlsl b/include/nbl/builtin/hlsl/limits.hlsl index ebc6f931e1..fa9edc3bde 100644 --- a/include/nbl/builtin/hlsl/limits.hlsl +++ b/include/nbl/builtin/hlsl/limits.hlsl @@ -146,7 +146,7 @@ struct num_base : type_identity // (TODO) think about what this means for HLSL // identifies floating-point types that can represent the special value "quiet not-a-number" (NaN) - NBL_CONSTEXPR_STATIC_INLINE bool has_quiet_NaN = !is_integer; + NBL_CONSTEXPR_STATIC_INLINE bool has_quiet_NaN = !is_integer; // identifies floating-point types that can represent the special value "signaling not-a-number" (NaN) NBL_CONSTEXPR_STATIC_INLINE bool has_signaling_NaN = !is_integer; // identifies the denormalization style used by the floating-point type diff --git a/include/nbl/builtin/hlsl/math/angle_adding.hlsl b/include/nbl/builtin/hlsl/math/angle_adding.hlsl index 27d4f2a465..5ab661facb 100644 --- a/include/nbl/builtin/hlsl/math/angle_adding.hlsl +++ b/include/nbl/builtin/hlsl/math/angle_adding.hlsl @@ -44,7 +44,7 @@ struct sincos_accumulator const T cosB = runningSum.real(); const T sinB = runningSum.imag(); // TODO: prove if we infer overflow from sign of `d` instead - const bool overflow = abs(min(a, cosB)) > max(a, cosB); + const bool overflow = abs(min(cosA, cosB)) > max(cosA, cosB); const T c = cosA * cosB - sinA * sinB; const T d = sinA * cosB + cosA * sinB; diff --git a/include/nbl/builtin/hlsl/math/functions.hlsl b/include/nbl/builtin/hlsl/math/functions.hlsl index 6eee1fae6e..046c72c527 100644 --- a/include/nbl/builtin/hlsl/math/functions.hlsl +++ b/include/nbl/builtin/hlsl/math/functions.hlsl @@ -122,11 +122,7 @@ void frisvad(NBL_CONST_REF_ARG(T) normal, NBL_REF_ARG(T) tangent, NBL_REF_ARG(T) bool partitionRandVariable(float leftProb, NBL_REF_ARG(float) xi, NBL_REF_ARG(float) rcpChoiceProb) { -#ifdef __HLSL_VERSION - NBL_CONSTEXPR float NEXT_ULP_AFTER_UNITY = asfloat(0x3f800001u); -#else - NBL_CONSTEXPR float32_t NEXT_ULP_AFTER_UNITY = bit_cast(0x3f800001u); -#endif + const float32_t NEXT_ULP_AFTER_UNITY = bit_cast(0x3f800001u); const bool pickRight = xi >= leftProb * NEXT_ULP_AFTER_UNITY; // This is all 100% correct taking into account the above NEXT_ULP_AFTER_UNITY diff --git a/include/nbl/builtin/hlsl/math/morton.hlsl b/include/nbl/builtin/hlsl/math/morton.hlsl new file mode 100644 index 0000000000..4a6cb5dfd3 --- /dev/null +++ b/include/nbl/builtin/hlsl/math/morton.hlsl @@ -0,0 +1,68 @@ +// 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_MORTON_INCLUDED_ +#define _NBL_BUILTIN_HLSL_MATH_MORTON_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace math +{ + +namespace impl +{ + +template +struct MortonComponent; + +template +struct MortonComponent +{ + static T decode2d(T x) + { + x &= 0x55555555u; + x = (x ^ (x >> 1u)) & 0x33333333u; + x = (x ^ (x >> 2u)) & 0x0f0f0f0fu; + x = (x ^ (x >> 4u)) & 0x00ff00ffu; + return x; + } +}; + +template +struct MortonComponent +{ + static T decode2d(T x) + { + x &= 0x55555555u; + x = (x ^ (x >> 1u)) & 0x33333333u; + x = (x ^ (x >> 2u)) & 0x0f0f0f0fu; + x = (x ^ (x >> 4u)) & 0x00ff00ffu; + x = (x ^ (x >> 8u)) & 0x0000ffffu; + x = (x ^ (x >> 16u)); + return x; + } +}; + +} + +template +struct Morton +{ + using vector2_type = vector; + using component_type = impl::MortonComponent; + + static vector2_type decode2d(T x) + { + return vector2_type(component_type::decode2d(x), component_type::decode2d(x >> 1u)); + } +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl new file mode 100644 index 0000000000..77cfb3c283 --- /dev/null +++ b/include/nbl/builtin/hlsl/rwmc/CascadeAccumulator.hlsl @@ -0,0 +1,98 @@ +#ifndef _NBL_HLSL_RWMC_CASCADE_ACCUMULATOR_INCLUDED_ +#define _NBL_HLSL_RWMC_CASCADE_ACCUMULATOR_INCLUDED_ +#include +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace rwmc +{ + +template) +struct CascadeAccumulator +{ + struct CascadeEntry + { + uint32_t cascadeSampleCounter[CascadeCount]; + CascadeLayerType data[CascadeCount]; + + void addSampleIntoCascadeEntry(CascadeLayerType _sample, uint32_t lowerCascadeIndex, float lowerCascadeLevelWeight, float higherCascadeLevelWeight, uint32_t sampleCount) + { + const float reciprocalSampleCount = 1.0f / float(sampleCount); + + uint32_t lowerCascadeSampleCount = cascadeSampleCounter[lowerCascadeIndex]; + data[lowerCascadeIndex] += (_sample * lowerCascadeLevelWeight - (sampleCount - lowerCascadeSampleCount) * data[lowerCascadeIndex]) * reciprocalSampleCount; + cascadeSampleCounter[lowerCascadeIndex] = sampleCount; + + uint32_t higherCascadeIndex = lowerCascadeIndex + 1u; + if (higherCascadeIndex < CascadeCount) + { + uint32_t higherCascadeSampleCount = cascadeSampleCounter[higherCascadeIndex]; + data[higherCascadeIndex] += (_sample * higherCascadeLevelWeight - (sampleCount - higherCascadeSampleCount) * data[higherCascadeIndex]) * reciprocalSampleCount; + cascadeSampleCounter[higherCascadeIndex] = sampleCount; + } + } + }; + + using cascade_layer_scalar_type = typename vector_traits::scalar_type; + using this_t = CascadeAccumulator; + using output_storage_type = CascadeEntry; + using initialization_data = SplattingParameters; + output_storage_type accumulation; + + SplattingParameters splattingParameters; + + static this_t create(NBL_CONST_REF_ARG(SplattingParameters) settings) + { + this_t retval; + for (int i = 0; i < CascadeCount; ++i) + { + retval.accumulation.data[i] = promote(0.0f); + retval.accumulation.cascadeSampleCounter[i] = 0u; + } + retval.splattingParameters = settings; + + return retval; + } + + cascade_layer_scalar_type getLuma(NBL_CONST_REF_ARG(CascadeLayerType) col) + { + return hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); + } + + // most of this code is stolen from https://cg.ivd.kit.edu/publications/2018/rwmc/tool/split.cpp + void addSample(uint32_t sampleCount, CascadeLayerType _sample) + { + const cascade_layer_scalar_type log2Start = splattingParameters.log2Start; + const cascade_layer_scalar_type log2Base = splattingParameters.log2Base; + const cascade_layer_scalar_type luma = getLuma(_sample); + const cascade_layer_scalar_type log2Luma = log2(luma); + const cascade_layer_scalar_type cascade = log2Luma * 1.f / log2Base - log2Start / log2Base; + const cascade_layer_scalar_type clampedCascade = clamp(cascade, 0, CascadeCount - 1); + // c<=0 -> 0, c>=Count-1 -> Count-1 + uint32_t lowerCascadeIndex = floor(cascade); + // 0 whenever clamped or `cascade` is integer (when `clampedCascade` is integer) + cascade_layer_scalar_type higherCascadeWeight = clampedCascade - floor(clampedCascade); + // never 0 thanks to magic of `1-fract(x)` + cascade_layer_scalar_type lowerCascadeWeight = cascade_layer_scalar_type(1) - higherCascadeWeight; + + // handle super bright sample case + if (cascade > CascadeCount - 1) + lowerCascadeWeight = exp2(log2Start + log2Base * (CascadeCount - 1) - log2Luma); + + accumulation.addSampleIntoCascadeEntry(_sample, lowerCascadeIndex, lowerCascadeWeight, higherCascadeWeight, sampleCount); + } + + +}; + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl new file mode 100644 index 0000000000..7509eac493 --- /dev/null +++ b/include/nbl/builtin/hlsl/rwmc/ResolveParameters.hlsl @@ -0,0 +1,45 @@ +#ifndef _NBL_BUILTIN_HLSL_RWMC_RESOLVE_PARAMETERS_HLSL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_RWMC_RESOLVE_PARAMETERS_HLSL_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace rwmc +{ + +struct ResolveParameters +{ + uint32_t lastCascadeIndex; + float initialEmin; // a minimum image brightness that we always consider reliable + float reciprocalBase; + float reciprocalN; + float reciprocalKappa; + float colorReliabilityFactor; + float NOverKappa; +}; + +ResolveParameters computeResolveParameters(float base, uint32_t sampleCount, float minReliableLuma, float kappa, uint32_t cascadeSize) +{ + ResolveParameters retval; + retval.lastCascadeIndex = cascadeSize - 1u; + retval.initialEmin = minReliableLuma; + retval.reciprocalBase = 1.f / base; + const float N = float(sampleCount); + retval.reciprocalN = 1.f / N; + retval.reciprocalKappa = 1.f / kappa; + // if not interested in exact expected value estimation (kappa!=1.f), can usually accept a bit more variance relative to the image brightness we already have + // allow up to ~ more energy in one sample to lessen bias in some cases + retval.colorReliabilityFactor = base + (1.f - base) * retval.reciprocalKappa; + retval.NOverKappa = N * retval.reciprocalKappa; + + return retval; +} + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl new file mode 100644 index 0000000000..e74dd0e5bd --- /dev/null +++ b/include/nbl/builtin/hlsl/rwmc/SplattingParameters.hlsl @@ -0,0 +1,23 @@ +#ifndef _NBL_BUILTIN_HLSL_RWMC_SPLATTING_PARAMETERS_HLSL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_RWMC_SPLATTING_PARAMETERS_HLSL_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace rwmc +{ + +struct SplattingParameters +{ + float log2Start; + float log2Base; +}; + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/rwmc/resolve.hlsl b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl new file mode 100644 index 0000000000..6484ef38b7 --- /dev/null +++ b/include/nbl/builtin/hlsl/rwmc/resolve.hlsl @@ -0,0 +1,189 @@ +#ifndef _NBL_BUILTIN_HLSL_RWMC_RESOLVE_HLSL_INCLUDED_ +#define _NBL_BUILTIN_HLSL_RWMC_RESOLVE_HLSL_INCLUDED_ + +#include "nbl/builtin/hlsl/cpp_compat.hlsl" +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace rwmc +{ + // declare concept +#define NBL_CONCEPT_NAME ResolveAccessorBase +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)(typename)(int32_t) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T)(VectorScalarType)(Dims) +// not the greatest syntax but works +#define NBL_CONCEPT_PARAM_0 (a,T) +#define NBL_CONCEPT_PARAM_1 (scalar,VectorScalarType) +#define NBL_CONCEPT_PARAM_2 (vec,vector) +// start concept + NBL_CONCEPT_BEGIN(2) +// need to be defined AFTER the concept begins +#define a NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define scalar NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define vec NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_EXPR)((a.calcLuma(vec)))) +); +#undef a +#undef vec +#include + +/* ResolveAccessor is required to: +* - satisfy `LoadableImage` concept requirements +* - implement function called `calcLuma` which calculates luma from a pixel value +*/ + +template +NBL_BOOL_CONCEPT ResolveAccessor = ResolveAccessorBase && concepts::accessors::LoadableImage; + +template +struct ResolveAccessorAdaptor +{ + using output_scalar_type = OutputScalar; + using output_type = vector; + NBL_CONSTEXPR int32_t image_dimension = 2; + + RWTexture2DArray cascade; + + float32_t calcLuma(in float32_t3 col) + { + return hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], col); + } + + template + output_type get(vector uv, uint16_t layer) + { + uint32_t imgWidth, imgHeight, layers; + cascade.GetDimensions(imgWidth, imgHeight, layers); + int16_t2 cascadeImageDimension = int16_t2(imgWidth, imgHeight); + + if (any(uv < int16_t2(0, 0)) || any(uv > cascadeImageDimension)) + return vector(0, 0, 0, 0); + + return cascade.Load(int32_t3(uv, int32_t(layer))); + } +}; + +template //NBL_PRIMARY_REQUIRES(ResolveAccessor) +struct Resolver +{ + using output_type = OutputColorType; + + struct CascadeSample + { + float32_t3 centerValue; + float normalizedCenterLuma; + float normalizedNeighbourhoodAverageLuma; + }; + + static Resolver create(NBL_REF_ARG(ResolveParameters) resolveParameters) + { + Resolver retval; + retval.params = resolveParameters; + + return retval; + } + + output_type operator()(NBL_REF_ARG(CascadeAccessor) acc, const int16_t2 coord) + { + float reciprocalBaseI = 1.f; + CascadeSample curr = __sampleCascade(acc, coord, 0u, reciprocalBaseI); + + float32_t3 accumulation = float32_t3(0.0f, 0.0f, 0.0f); + float Emin = params.initialEmin; + + float prevNormalizedCenterLuma, prevNormalizedNeighbourhoodAverageLuma; + for (int16_t i = 0u; i <= params.lastCascadeIndex; i++) + { + const bool notFirstCascade = i != 0; + const bool notLastCascade = i != params.lastCascadeIndex; + + CascadeSample next; + if (notLastCascade) + { + reciprocalBaseI *= params.reciprocalBase; + next = __sampleCascade(acc, coord, int16_t(i + 1), reciprocalBaseI); + } + + float reliability = 1.f; + // sample counting-based reliability estimation + if (params.reciprocalKappa <= 1.f) + { + float localReliability = curr.normalizedCenterLuma; + // reliability in 3x3 pixel block (see robustness) + float globalReliability = curr.normalizedNeighbourhoodAverageLuma; + if (notFirstCascade) + { + localReliability += prevNormalizedCenterLuma; + globalReliability += prevNormalizedNeighbourhoodAverageLuma; + } + if (notLastCascade) + { + localReliability += next.normalizedCenterLuma; + globalReliability += next.normalizedNeighbourhoodAverageLuma; + } + // check if above minimum sampling threshold (avg 9 sample occurences in 3x3 neighbourhood), then use per-pixel reliability (NOTE: tertiary op is in reverse) + reliability = globalReliability < params.reciprocalN ? globalReliability : localReliability; + { + const float accumLuma = acc.calcLuma(accumulation); + if (accumLuma > Emin) + Emin = accumLuma; + + const float colorReliability = Emin * reciprocalBaseI * params.colorReliabilityFactor; + + reliability += colorReliability; + reliability *= params.NOverKappa; + reliability -= params.reciprocalKappa; + reliability = clamp(reliability * 0.5f, 0.f, 1.f); + } + } + accumulation += curr.centerValue * reliability; + + prevNormalizedCenterLuma = curr.normalizedCenterLuma; + prevNormalizedNeighbourhoodAverageLuma = curr.normalizedNeighbourhoodAverageLuma; + curr = next; + } + + return accumulation; + } + + ResolveParameters params; + + // pseudo private stuff: + + CascadeSample __sampleCascade(NBL_REF_ARG(CascadeAccessor) acc, int16_t2 coord, uint16_t cascadeIndex, float reciprocalBaseI) + { + typename CascadeAccessor::output_type tmp; + output_type neighbourhood[9]; + neighbourhood[0] = acc.template get(coord + int16_t2(-1, -1), cascadeIndex); + neighbourhood[1] = acc.template get(coord + int16_t2(0, -1), cascadeIndex); + neighbourhood[2] = acc.template get(coord + int16_t2(1, -1), cascadeIndex); + neighbourhood[3] = acc.template get(coord + int16_t2(-1, 0), cascadeIndex); + neighbourhood[4] = acc.template get(coord + int16_t2(0, 0), cascadeIndex); + neighbourhood[5] = acc.template get(coord + int16_t2(1, 0), cascadeIndex); + neighbourhood[6] = acc.template get(coord + int16_t2(-1, 1), cascadeIndex); + neighbourhood[7] = acc.template get(coord + int16_t2(0, 1), cascadeIndex); + neighbourhood[8] = acc.template get(coord + int16_t2(1, 1), cascadeIndex); + + // numerical robustness + float32_t3 excl_hood_sum = ((neighbourhood[0] + neighbourhood[1]) + (neighbourhood[2] + neighbourhood[3])) + + ((neighbourhood[5] + neighbourhood[6]) + (neighbourhood[7] + neighbourhood[8])); + + CascadeSample retval; + retval.centerValue = neighbourhood[4]; + retval.normalizedNeighbourhoodAverageLuma = retval.normalizedCenterLuma = acc.calcLuma(neighbourhood[4]) * reciprocalBaseI; + retval.normalizedNeighbourhoodAverageLuma = (acc.calcLuma(excl_hood_sum) * reciprocalBaseI + retval.normalizedNeighbourhoodAverageLuma) / 9.f; + return retval; + } +}; + +} +} +} + +#endif \ No newline at end of file diff --git a/include/nbl/builtin/hlsl/sampling/bilinear.hlsl b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl new file mode 100644 index 0000000000..42a923f650 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/bilinear.hlsl @@ -0,0 +1,62 @@ +// 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_SAMPLING_BILINEAR_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_BILINEAR_INCLUDED_ + +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct Bilinear +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + using vector4_type = vector; + + static Bilinear create(NBL_CONST_REF_ARG(vector4_type) bilinearCoeffs) + { + Bilinear retval; + retval.bilinearCoeffs = bilinearCoeffs; + return retval; + } + + vector2_type generate(NBL_REF_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector2_type) _u) + { + vector2_type u = _u; + const vector2_type twiceAreasUnderXCurve = vector2_type(bilinearCoeffs[0] + bilinearCoeffs[1], bilinearCoeffs[2] + bilinearCoeffs[3]); + Linear lineary = Linear::create(twiceAreasUnderXCurve); + u.y = lineary.generate(u.y); + + const vector2_type ySliceEndPoints = vector2_type(nbl::hlsl::mix(bilinearCoeffs[0], bilinearCoeffs[2], u.y), nbl::hlsl::mix(bilinearCoeffs[1], bilinearCoeffs[3], u.y)); + Linear linearx = Linear::create(ySliceEndPoints); + u.x = linearx.generate(u.x); + + rcpPdf = (twiceAreasUnderXCurve[0] + twiceAreasUnderXCurve[1]) / (4.0 * nbl::hlsl::mix(ySliceEndPoints[0], ySliceEndPoints[1], u.x)); + + return u; + } + + scalar_type pdf(NBL_CONST_REF_ARG(vector2_type) u) + { + return 4.0 * nbl::hlsl::mix(nbl::hlsl::mix(bilinearCoeffs[0], bilinearCoeffs[1], u.x), nbl::hlsl::mix(bilinearCoeffs[2], bilinearCoeffs[3], u.x), u.y) / (bilinearCoeffs[0] + bilinearCoeffs[1] + bilinearCoeffs[2] + bilinearCoeffs[3]); + } + + vector4_type bilinearCoeffs; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl new file mode 100644 index 0000000000..dcac2279be --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/box_muller_transform.hlsl @@ -0,0 +1,27 @@ +// 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_BOX_MULLER_TRANSFORM_INCLUDED_ +#define _NBL_BUILTIN_HLSL_BOX_MULLER_TRANSFORM_INCLUDED_ + +#include "nbl/builtin/hlsl/math/functions.hlsl" +#include "nbl/builtin/hlsl/numbers.hlsl" + +namespace nbl +{ +namespace hlsl +{ + +template +vector boxMullerTransform(vector xi, T stddev) +{ + T sinPhi, cosPhi; + math::sincos(2.0 * numbers::pi * xi.y - numbers::pi, sinPhi, cosPhi); + return vector(cosPhi, sinPhi) * nbl::hlsl::sqrt(-2.0 * nbl::hlsl::log(xi.x)) * stddev; +} + +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/linear.hlsl b/include/nbl/builtin/hlsl/sampling/linear.hlsl new file mode 100644 index 0000000000..12d445eefe --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/linear.hlsl @@ -0,0 +1,45 @@ +// 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_SAMPLING_LINEAR_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_LINEAR_INCLUDED_ + +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct Linear +{ + using scalar_type = T; + using vector2_type = vector; + + static Linear create(NBL_CONST_REF_ARG(vector2_type) linearCoeffs) + { + Linear retval; + retval.linearCoeffs = linearCoeffs; + return retval; + } + + scalar_type generate(scalar_type u) + { + const scalar_type rcpDiff = 1.0 / (linearCoeffs[0] - linearCoeffs[1]); + const vector2_type squaredCoeffs = linearCoeffs * linearCoeffs; + return nbl::hlsl::abs(rcpDiff) < numeric_limits::max ? (linearCoeffs[0] - nbl::hlsl::sqrt(nbl::hlsl::mix(squaredCoeffs[0], squaredCoeffs[1], u))) * rcpDiff : u; + } + + vector2_type linearCoeffs; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl new file mode 100644 index 0000000000..f2f29ed12b --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl @@ -0,0 +1,97 @@ +// 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_SAMPLING_PROJECTED_SPHERICAL_TRIANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_PROJECTED_SPHERICAL_TRIANGLE_INCLUDED_ + +#include +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct ProjectedSphericalTriangle +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + using vector4_type = vector; + + static ProjectedSphericalTriangle create(NBL_CONST_REF_ARG(shapes::SphericalTriangle) tri) + { + ProjectedSphericalTriangle retval; + retval.tri = tri; + return retval; + } + + vector4_type computeBilinearPatch(NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF) + { + const scalar_type minimumProjSolidAngle = 0.0; + + matrix m = matrix(tri.vertex0, tri.vertex1, tri.vertex2); + const vector3_type bxdfPdfAtVertex = math::conditionalAbsOrMax(isBSDF, nbl::hlsl::mul(m, receiverNormal), (vector3_type)minimumProjSolidAngle); + + return bxdfPdfAtVertex.yyxz; + } + + vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REF_ARG(vector2_type) _u) + { + vector2_type u; + // pre-warp according to proj solid angle approximation + vector4_type patch = computeBilinearPatch(receiverNormal, isBSDF); + Bilinear bilinear = Bilinear::create(patch); + u = bilinear.generate(rcpPdf, u); + + // now warp the points onto a spherical triangle + const vector3_type L = sphtri.generate(solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); + rcpPdf *= solidAngle; + + return L; + } + + vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool isBSDF, NBL_CONST_REF_ARG(vector2_type) u) + { + scalar_type cos_a, cos_c, csc_b, csc_c; + vector3_type cos_vertices, sin_vertices; + const scalar_type solidAngle = tri.solidAngleOfTriangle(cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c); + return generate(rcpPdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, receiverNormal, isBSDF, u); + } + + scalar_type pdf(scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REF_ARG(vector3_type) L) + { + scalar_type pdf; + const vector2_type u = sphtri.generateInverse(pdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, L); + + vector4_type patch = computeBilinearPatch(receiverNormal, receiverWasBSDF); + Bilinear bilinear = Bilinear::create(patch); + return pdf * bilinear.pdf(u); + } + + scalar_type pdf(NBL_CONST_REF_ARG(vector3_type) receiverNormal, bool receiverWasBSDF, NBL_CONST_REF_ARG(vector3_type) L) + { + scalar_type pdf; + const vector2_type u = sphtri.generateInverse(pdf, L); + + vector4_type patch = computeBilinearPatch(receiverNormal, receiverWasBSDF); + Bilinear bilinear = Bilinear::create(patch); + return pdf * bilinear.pdf(u); + } + + shapes::SphericalTriangle tri; + sampling::SphericalTriangle sphtri; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl new file mode 100644 index 0000000000..127a7194b2 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl @@ -0,0 +1,90 @@ +// 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_SAMPLING_SPHERICAL_RECTANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_SPHERICAL_RECTANGLE_INCLUDED_ + +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct SphericalRectangle +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + using vector4_type = vector; + + static SphericalRectangle create(NBL_CONST_REF_ARG(shapes::SphericalRectangle) rect) + { + SphericalRectangle retval; + retval.rect = rect; + return retval; + } + + vector2_type generate(NBL_CONST_REF_ARG(vector2_type) rectangleExtents, NBL_CONST_REF_ARG(vector2_type) uv, NBL_REF_ARG(scalar_type) S) + { + const vector4_type denorm_n_z = vector4_type(-rect.r0.y, rect.r0.x + rectangleExtents.x, rect.r0.y + rectangleExtents.y, -rect.r0.x); + const vector4_type n_z = denorm_n_z / hlsl::sqrt((vector4_type)(rect.r0.z * rect.r0.z) + denorm_n_z * denorm_n_z); + const vector4_type cosGamma = vector4_type( + -n_z[0] * n_z[1], + -n_z[1] * n_z[2], + -n_z[2] * n_z[3], + -n_z[3] * n_z[0] + ); + + math::sincos_accumulator angle_adder = math::sincos_accumulator::create(cosGamma[0]); + angle_adder.addCosine(cosGamma[1]); + scalar_type p = angle_adder.getSumofArccos(); + angle_adder = math::sincos_accumulator::create(cosGamma[2]); + angle_adder.addCosine(cosGamma[3]); + scalar_type q = angle_adder.getSumofArccos(); + + const scalar_type k = 2 * numbers::pi - q; + const scalar_type b0 = n_z[0]; + const scalar_type b1 = n_z[2]; + S = p + q - 2 * numbers::pi; + + const scalar_type CLAMP_EPS = 1e-5f; + + // flip z axsis if rect.r0.z > 0 + const uint32_t zFlipMask = (bit_cast(rect.r0.z) ^ 0x80000000u) & 0x80000000u; + rect.r0.z = bit_cast(bit_cast(rect.r0.z) ^ zFlipMask); + vector3_type r1 = rect.r0 + vector3_type(rectangleExtents.x, rectangleExtents.y, 0); + + const scalar_type au = uv.x * S + k; + const scalar_type fu = (hlsl::cos(au) * b0 - b1) / hlsl::sin(au); + const scalar_type cu_2 = hlsl::max(fu * fu + b0 * b0, 1.f); // forces `cu` to be in [-1,1] + const scalar_type cu = bit_cast(bit_cast(1.0 / hlsl::sqrt(cu_2)) ^ (bit_cast(fu) & 0x80000000u)); + + scalar_type xu = -(cu * rect.r0.z) * 1.0 / hlsl::sqrt(1 - cu * cu); + xu = hlsl::clamp(xu, rect.r0.x, r1.x); // avoid Infs + const scalar_type d_2 = xu * xu + rect.r0.z * rect.r0.z; + const scalar_type d = hlsl::sqrt(d_2); + + const scalar_type h0 = rect.r0.y / hlsl::sqrt(d_2 + rect.r0.y * rect.r0.y); + const scalar_type h1 = r1.y / hlsl::sqrt(d_2 + r1.y * r1.y); + const scalar_type hv = h0 + uv.y * (h1 - h0), hv2 = hv * hv; + const scalar_type yv = (hv2 < 1 - CLAMP_EPS) ? (hv * d) / hlsl::sqrt(1 - hv2) : r1.y; + + return vector2_type((xu - rect.r0.x) / rectangleExtents.x, (yv - rect.r0.y) / rectangleExtents.y); + } + + shapes::SphericalRectangle rect; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl new file mode 100644 index 0000000000..fd3a616e8d --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/spherical_triangle.hlsl @@ -0,0 +1,135 @@ +// 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_SAMPLING_SPHERICAL_TRIANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_SPHERICAL_TRIANGLE_INCLUDED_ + +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct SphericalTriangle +{ + using scalar_type = T; + using vector2_type = vector; + using vector3_type = vector; + + static SphericalTriangle create(NBL_CONST_REF_ARG(shapes::SphericalTriangle) tri) + { + SphericalTriangle retval; + retval.tri = tri; + return retval; + } + + vector3_type slerp_delta(NBL_CONST_REF_ARG(vector3_type) start, NBL_CONST_REF_ARG(vector3_type) preScaledWaypoint, scalar_type cosAngleFromStart) + { + vector3_type planeNormal = nbl::hlsl::cross(start,preScaledWaypoint); + + cosAngleFromStart *= 0.5; + const scalar_type sinAngle = nbl::hlsl::sqrt(0.5 - cosAngleFromStart); + const scalar_type cosAngle = nbl::hlsl::sqrt(0.5 + cosAngleFromStart); + + planeNormal *= sinAngle; + const vector3_type precompPart = nbl::hlsl::cross(planeNormal, start) * 2.0; + + return precompPart * cosAngle + nbl::hlsl::cross(planeNormal, precompPart); + } + + // WARNING: can and will return NAN if one or three of the triangle edges are near zero length + vector3_type generate(scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector2_type) u) + { + scalar_type negSinSubSolidAngle,negCosSubSolidAngle; + math::sincos(solidAngle * u.x - numbers::pi, negSinSubSolidAngle, negCosSubSolidAngle); + + const scalar_type p = negCosSubSolidAngle * sin_vertices[0] - negSinSubSolidAngle * cos_vertices[0]; + const scalar_type q = -negSinSubSolidAngle * sin_vertices[0] - negCosSubSolidAngle * cos_vertices[0]; + + // TODO: we could optimize everything up and including to the first slerp, because precision here is just godawful + scalar_type u_ = q - cos_vertices[0]; + scalar_type v_ = p + sin_vertices[0] * cos_c; + + // the slerps could probably be optimized by sidestepping `normalize` calls and accumulating scaling factors + vector3_type C_s = tri.vertex0; + if (csc_b < numeric_limits::max) + { + const scalar_type cosAngleAlongAC = ((v_ * q - u_ * p) * cos_vertices[0] - v_) / ((v_ * p + u_ * q) * sin_vertices[0]); + if (nbl::hlsl::abs(cosAngleAlongAC) < 1.f) + C_s += slerp_delta(tri.vertex0, tri.vertex2 * csc_b, cosAngleAlongAC); + } + + vector3_type retval = tri.vertex1; + const scalar_type cosBC_s = nbl::hlsl::dot(C_s, tri.vertex1); + const scalar_type csc_b_s = 1.0 / nbl::hlsl::sqrt(1.0 - cosBC_s * cosBC_s); + if (csc_b_s < numeric_limits::max) + { + const scalar_type cosAngleAlongBC_s = nbl::hlsl::clamp(1.0 + cosBC_s * u.y - u.y, -1.f, 1.f); + if (nbl::hlsl::abs(cosAngleAlongBC_s) < 1.f) + retval += slerp_delta(tri.vertex1, C_s * csc_b_s, cosAngleAlongBC_s); + } + return retval; + } + + vector3_type generate(NBL_REF_ARG(scalar_type) rcpPdf, NBL_CONST_REF_ARG(vector2_type) u) + { + scalar_type cos_a, cos_c, csc_b, csc_c; + vector3_type cos_vertices, sin_vertices; + + rcpPdf = tri.solidAngleOfTriangle(cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c); + + return generate(rcpPdf, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, u); + } + + vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, scalar_type solidAngle, NBL_CONST_REF_ARG(vector3_type) cos_vertices, NBL_CONST_REF_ARG(vector3_type) sin_vertices, scalar_type cos_a, scalar_type cos_c, scalar_type csc_b, scalar_type csc_c, NBL_CONST_REF_ARG(vector3_type) L) + { + pdf = 1.0 / solidAngle; + + const scalar_type cosAngleAlongBC_s = nbl::hlsl::dot(L, tri.vertex1); + const scalar_type csc_a_ = 1.0 / nbl::hlsl::sqrt(1.0 - cosAngleAlongBC_s * cosAngleAlongBC_s); + const scalar_type cos_b_ = nbl::hlsl::dot(L, tri.vertex0); + + const scalar_type cosB_ = (cos_b_ - cosAngleAlongBC_s * cos_c) * csc_a_ * csc_c; + const scalar_type sinB_ = nbl::hlsl::sqrt(1.0 - cosB_ * cosB_); + + const scalar_type cosC_ = sin_vertices[0] * sinB_* cos_c - cos_vertices[0] * cosB_; + const scalar_type sinC_ = nbl::hlsl::sqrt(1.0 - cosC_ * cosC_); + + math::sincos_accumulator angle_adder = math::sincos_accumulator::create(cos_vertices[0], sin_vertices[0]); + angle_adder.addAngle(cosB_, sinB_); + angle_adder.addAngle(cosC_, sinC_); + const scalar_type subTriSolidAngleRatio = (angle_adder.getSumofArccos() - numbers::pi) * pdf; + const scalar_type u = subTriSolidAngleRatio > numeric_limits::min ? subTriSolidAngleRatio : 0.0; + + const scalar_type cosBC_s = (cos_vertices[0] + cosB_ * cosC_) / (sinB_ * sinC_); + const scalar_type v = (1.0 - cosAngleAlongBC_s) / (1.0 - (cosBC_s < bit_cast(0x3f7fffff) ? cosBC_s : cos_c)); + + return vector2_type(u,v); + } + + vector2_type generateInverse(NBL_REF_ARG(scalar_type) pdf, NBL_CONST_REF_ARG(vector3_type) L) + { + scalar_type cos_a, cos_c, csc_b, csc_c; + vector3_type cos_vertices, sin_vertices; + + const scalar_type solidAngle = tri.solidAngleOfTriangle(cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c); + + return generateInverse(pdf, solidAngle, cos_vertices, sin_vertices, cos_a, cos_c, csc_b, csc_c, L); + } + + shapes::SphericalTriangle tri; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/shapes/rectangle.hlsl b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl new file mode 100644 index 0000000000..434918cc09 --- /dev/null +++ b/include/nbl/builtin/hlsl/shapes/rectangle.hlsl @@ -0,0 +1,67 @@ +// 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_SHAPES_RECTANGLE_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SHAPES_RECTANGLE_INCLUDED_ + +#include +#include +#include +#include + +namespace nbl +{ +namespace hlsl +{ +namespace shapes +{ + +template +struct SphericalRectangle +{ + using scalar_type = Scalar; + using vector3_type = vector; + using vector4_type = vector; + using matrix3x3_type = matrix; + + static SphericalRectangle create(NBL_CONST_REF_ARG(vector3_type) observer, NBL_CONST_REF_ARG(vector3_type) rectangleOrigin, NBL_CONST_REF_ARG(matrix3x3_type) basis) + { + SphericalRectangle retval; + retval.r0 = nbl::hlsl::mul(basis, rectangleOrigin - observer); + return retval; + } + + static SphericalRectangle create(NBL_CONST_REF_ARG(vector3_type) observer, NBL_CONST_REF_ARG(vector3_type) rectangleOrigin, NBL_CONST_REF_ARG(vector3_type) T, NBL_CONST_REF_ARG(vector3_type) B, NBL_CONST_REF_ARG(vector3_type) N) + { + SphericalRectangle retval; + matrix3x3_type TBN = nbl::hlsl::transpose(matrix3x3_type(T, B, N)); + retval.r0 = nbl::hlsl::mul(TBN, rectangleOrigin - observer); + return retval; + } + + scalar_type solidAngleOfRectangle(NBL_CONST_REF_ARG(vector) rectangleExtents) + { + const vector4_type denorm_n_z = vector4_type(-r0.y, r0.x + rectangleExtents.x, r0.y + rectangleExtents.y, -r0.x); + const vector4_type n_z = denorm_n_z / nbl::hlsl::sqrt((vector4_type)(r0.z * r0.z) + denorm_n_z * denorm_n_z); + const vector4_type cosGamma = vector4_type( + -n_z[0] * n_z[1], + -n_z[1] * n_z[2], + -n_z[2] * n_z[3], + -n_z[3] * n_z[0] + ); + math::sincos_accumulator angle_adder = math::sincos_accumulator::create(cosGamma[0]); + angle_adder.addCosine(cosGamma[1]); + angle_adder.addCosine(cosGamma[2]); + angle_adder.addCosine(cosGamma[3]); + return angle_adder.getSumofArccos() - scalar_type(2.0) * numbers::pi; + } + + vector3_type r0; +}; + +} +} +} + +#endif diff --git a/include/nbl/builtin/hlsl/shapes/triangle.hlsl b/include/nbl/builtin/hlsl/shapes/triangle.hlsl index 4677b0e155..a6896fbc8a 100644 --- a/include/nbl/builtin/hlsl/shapes/triangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/triangle.hlsl @@ -5,8 +5,11 @@ #ifndef _NBL_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ #define _NBL_BUILTIN_HLSL_SHAPES_TRIANGLE_INCLUDED_ -#include #include +#include +#include +#include +#include #include namespace nbl @@ -16,6 +19,89 @@ namespace hlsl namespace shapes { +template +struct SphericalTriangle +{ + using scalar_type = T; + using vector3_type = vector; + + static SphericalTriangle create(NBL_CONST_REF_ARG(vector3_type) vertex0, NBL_CONST_REF_ARG(vector3_type) vertex1, NBL_CONST_REF_ARG(vector3_type) vertex2, NBL_CONST_REF_ARG(vector3_type) origin) + { + SphericalTriangle retval; + retval.vertex0 = nbl::hlsl::normalize(vertex0 - origin); + retval.vertex1 = nbl::hlsl::normalize(vertex1 - origin); + retval.vertex2 = nbl::hlsl::normalize(vertex2 - origin); + return retval; + } + + bool pyramidAngles(NBL_REF_ARG(vector3_type) cos_sides, NBL_REF_ARG(vector3_type) csc_sides) + { + cos_sides = vector3_type(hlsl::dot(vertex1, vertex2), hlsl::dot(vertex2, vertex0), hlsl::dot(vertex0, vertex1)); + csc_sides = (vector3_type)(1.f) - cos_sides * cos_sides; + csc_sides.x = hlsl::rsqrt(csc_sides.x); + csc_sides.y = hlsl::rsqrt(csc_sides.y); + csc_sides.z = hlsl::rsqrt(csc_sides.z); + + return hlsl::any >(csc_sides >= (vector3_type)(numeric_limits::max)); + } + + scalar_type solidAngleOfTriangle(NBL_REF_ARG(vector3_type) cos_vertices, NBL_REF_ARG(vector3_type) sin_vertices, NBL_REF_ARG(scalar_type) cos_a, NBL_REF_ARG(scalar_type) cos_c, NBL_REF_ARG(scalar_type) csc_b, NBL_REF_ARG(scalar_type) csc_c) + { + vector3_type cos_sides,csc_sides; + if (pyramidAngles(cos_sides, csc_sides)) + return 0.f; + + // these variables might eventually get optimized out + cos_a = cos_sides[0]; + cos_c = cos_sides[2]; + csc_b = csc_sides[1]; + csc_c = csc_sides[2]; + + // Both vertices and angles at the vertices are denoted by the same upper case letters A, B, and C. The angles A, B, C of the triangle are equal to the angles between the planes that intersect the surface of the sphere or, equivalently, the angles between the tangent vectors of the great circle arcs where they meet at the vertices. Angles are in radians. The angles of proper spherical triangles are (by convention) less than PI + cos_vertices = hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, (vector3_type)(-1.f), (vector3_type)1.f); // using Spherical Law of Cosines (TODO: do we need to clamp anymore? since the pyramid angles method introduction?) + sin_vertices = hlsl::sqrt((vector3_type)1.f - cos_vertices * cos_vertices); + + math::sincos_accumulator angle_adder = math::sincos_accumulator::create(cos_vertices[0], sin_vertices[0]); + angle_adder.addAngle(cos_vertices[1], sin_vertices[1]); + angle_adder.addAngle(cos_vertices[2], sin_vertices[2]); + return angle_adder.getSumofArccos() - numbers::pi; + } + + scalar_type solidAngleOfTriangle() + { + vector3_type dummy0,dummy1; + scalar_type dummy2,dummy3,dummy4,dummy5; + return solidAngleOfTriangle(dummy0,dummy1,dummy2,dummy3,dummy4,dummy5); + } + + scalar_type projectedSolidAngleOfTriangle(NBL_CONST_REF_ARG(vector3_type) receiverNormal, NBL_REF_ARG(vector3_type) cos_sides, NBL_REF_ARG(vector3_type) csc_sides, NBL_REF_ARG(vector3_type) cos_vertices) + { + if (pyramidAngles(cos_sides, csc_sides)) + return 0.f; + + vector3_type awayFromEdgePlane0 = hlsl::cross(vertex1, vertex2) * csc_sides[0]; + vector3_type awayFromEdgePlane1 = hlsl::cross(vertex2, vertex0) * csc_sides[1]; + vector3_type awayFromEdgePlane2 = hlsl::cross(vertex0, vertex1) * csc_sides[2]; + + // useless here but could be useful somewhere else + cos_vertices[0] = hlsl::dot(awayFromEdgePlane1, awayFromEdgePlane2); + cos_vertices[1] = hlsl::dot(awayFromEdgePlane2, awayFromEdgePlane0); + cos_vertices[2] = hlsl::dot(awayFromEdgePlane0, awayFromEdgePlane1); + // TODO: above dot products are in the wrong order, either work out which is which, or try all 6 permutations till it works + cos_vertices = hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, (vector3_type)(-1.f), (vector3_type)1.f); + + matrix awayFromEdgePlane = matrix(awayFromEdgePlane0, awayFromEdgePlane1, awayFromEdgePlane2); + const vector3_type externalProducts = hlsl::abs(hlsl::mul(/* transposed already */awayFromEdgePlane, receiverNormal)); + + const vector3_type pyramidAngles = acos(cos_sides); + return hlsl::dot(pyramidAngles, externalProducts) / (2.f * numbers::pi); + } + + vector3_type vertex0; + vector3_type vertex1; + vector3_type vertex2; +}; + namespace util { // Use this convetion e_i = v_{i+2}-v_{i+1}. vertex index is modulo by 3. diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index e8798499f9..ec79f814ae 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -249,9 +249,16 @@ 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") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/triangle.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/rectangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/shapes/aabb.hlsl") #sampling +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/linear.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/bilinear.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/concentric_mapping.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/box_muller_transform.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/spherical_triangle.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/projected_spherical_triangle.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/spherical_rectangle.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/cos_weighted_spheres.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/quotient_and_pdf.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/uniform_spheres.hlsl") @@ -351,5 +358,10 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/tgmath/output_structs.hlsl") #blur LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/prefix_sum_blur/blur.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/prefix_sum_blur/box_sampler.hlsl") +#rwmc +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/rwmc/Resolve.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/rwmc/CascadeAccumulator.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/rwmc/SplattingParameters.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/rwmc/ResolveParameters.hlsl") ADD_CUSTOM_BUILTIN_RESOURCES(nblBuiltinResourceData NBL_RESOURCES_TO_EMBED "${NBL_ROOT_PATH}/include" "nbl/builtin" "nbl::builtin" "${NBL_ROOT_PATH_BINARY}/include" "${NBL_ROOT_PATH_BINARY}/src" "STATIC" "INTERNAL") \ No newline at end of file