diff --git a/66_HLSLBxDFTests/app_resources/test_compile.comp.hlsl b/66_HLSLBxDFTests/app_resources/test_compile.comp.hlsl index fcf510b21..68fe8b633 100644 --- a/66_HLSLBxDFTests/app_resources/test_compile.comp.hlsl +++ b/66_HLSLBxDFTests/app_resources/test_compile.comp.hlsl @@ -8,14 +8,14 @@ using namespace nbl::hlsl; +using spectral_t = vector; using ray_dir_info_t = bxdf::ray_dir_info::SBasic; -using iso_interaction = bxdf::surface_interactions::SIsotropic; +using iso_interaction = bxdf::surface_interactions::SIsotropic; using aniso_interaction = bxdf::surface_interactions::SAnisotropic; using sample_t = bxdf::SLightSample; using iso_cache = bxdf::SIsotropicMicrofacetCache; using aniso_cache = bxdf::SAnisotropicMicrofacetCache; using quotient_pdf_t = sampling::quotient_and_pdf; -using spectral_t = vector; using iso_config_t = bxdf::SConfiguration; using aniso_config_t = bxdf::SConfiguration; @@ -32,6 +32,7 @@ void main(uint32_t3 ID : SV_DispatchThreadID) bxdf::reflection::SBeckmannAnisotropic beckmannAnisoBRDF; bxdf::reflection::SGGXIsotropic ggxIsoBRDF; bxdf::reflection::SGGXAnisotropic ggxAnisoBRDF; + bxdf::reflection::SIridescent iridBRDF; bxdf::transmission::SLambertian lambertianBSDF; bxdf::transmission::SOrenNayar orenNayarBSDF; @@ -42,6 +43,7 @@ void main(uint32_t3 ID : SV_DispatchThreadID) bxdf::transmission::SBeckmannDielectricAnisotropic beckmannAnisoBSDF; bxdf::transmission::SGGXDielectricIsotropic ggxIsoBSDF; bxdf::transmission::SGGXDielectricAnisotropic ggxAnisoBSDF; + // bxdf::transmission::SIridescent iridBSDF; // do some nonsense calculations, but call all the relevant functions @@ -76,6 +78,9 @@ void main(uint32_t3 ID : SV_DispatchThreadID) s = ggxAnisoBRDF.generate(anisointer, u.xy, cache); L += s.L.direction; + qp = iridBRDF.quotient_and_pdf(s, anisointer, cache); + L -= qp.quotient; + qp = ggxAnisoBRDF.quotient_and_pdf(s, anisointer, cache); L -= qp.quotient; diff --git a/66_HLSLBxDFTests/app_resources/test_components.hlsl b/66_HLSLBxDFTests/app_resources/test_components.hlsl index 9631db05d..a2db7ef53 100644 --- a/66_HLSLBxDFTests/app_resources/test_components.hlsl +++ b/66_HLSLBxDFTests/app_resources/test_components.hlsl @@ -3,11 +3,6 @@ #include "tests_common.hlsl" -namespace nbl -{ -namespace hlsl -{ - template // only for cook torrance bxdfs struct TestNDF : TestBxDF { @@ -75,7 +70,7 @@ struct TestNDF : TestBxDF } else if (traits_t::type == bxdf::BT_BSDF) { - if (abs(s.getNdotL()) <= bit_cast(numeric_limits::min)) + if (hlsl::abs(s.getNdotL()) <= bit_cast(numeric_limits::min)) return BET_INVALID; } @@ -87,12 +82,13 @@ struct TestNDF : TestBxDF float reflectance; bool transmitted; + bool isNdfInfinity; NBL_IF_CONSTEXPR(aniso) { dg1_query_type dq = base_t::bxdf.ndf.template createDG1Query(base_t::anisointer, cache); - fresnel_type _f = bxdf::impl::getOrientedFresnel::__call(base_t::bxdf.fresnel, base_t::anisointer.getNdotV()); - quant_query_type qq = bxdf::impl::quant_query_helper::template __call(base_t::bxdf.ndf, _f, cache); - quant_type DG1 = base_t::bxdf.ndf.template DG1(dq, qq, s, base_t::anisointer); + fresnel_type _f = base_t::bxdf_t::__getOrientedFresnel(base_t::bxdf.fresnel, base_t::anisointer.getNdotV()); + quant_query_type qq = bxdf::impl::quant_query_helper::template __call(base_t::bxdf.ndf, _f, base_t::anisointer, cache); + quant_type DG1 = base_t::bxdf.ndf.template DG1(dq, qq, s, base_t::anisointer, isNdfInfinity); dg1 = DG1.microfacetMeasure * hlsl::abs(cache.getVdotH() / base_t::anisointer.getNdotV()); reflectance = _f(cache.getVdotH())[0]; NdotH = cache.getAbsNdotH(); @@ -101,15 +97,18 @@ struct TestNDF : TestBxDF else { dg1_query_type dq = base_t::bxdf.ndf.template createDG1Query(base_t::isointer, isocache); - fresnel_type _f = bxdf::impl::getOrientedFresnel::__call(base_t::bxdf.fresnel, base_t::isointer.getNdotV()); - quant_query_type qq = bxdf::impl::quant_query_helper::template __call(base_t::bxdf.ndf, _f, isocache); - quant_type DG1 = base_t::bxdf.ndf.template DG1(dq, qq, s, base_t::isointer); + fresnel_type _f = base_t::bxdf_t::__getOrientedFresnel(base_t::bxdf.fresnel, base_t::isointer.getNdotV()); + quant_query_type qq = bxdf::impl::quant_query_helper::template __call(base_t::bxdf.ndf, _f, base_t::isointer, isocache); + quant_type DG1 = base_t::bxdf.ndf.template DG1(dq, qq, s, base_t::isointer, isNdfInfinity); dg1 = DG1.microfacetMeasure * hlsl::abs(isocache.getVdotH() / base_t::isointer.getNdotV()); reflectance = _f(isocache.getVdotH())[0]; NdotH = isocache.getAbsNdotH(); transmitted = isocache.isTransmission(); } + if (isNdfInfinity) + return BET_INVALID; + if (transmitted) { float eta = base_t::rc.eta.x; @@ -148,7 +147,7 @@ struct TestNDF : TestBxDF } else if (traits_t::type == bxdf::BT_BSDF) { - if (abs(base_t::isointer.getNdotV()) <= bit_cast(numeric_limits::min)) + if (hlsl::abs(base_t::isointer.getNdotV()) <= bit_cast(numeric_limits::min)) return BET_INVALID; } @@ -181,13 +180,13 @@ struct TestNDF : TestBxDF static void run(NBL_CONST_REF_ARG(STestInitParams) initparams, NBL_REF_ARG(FailureCallback) cb) { - random::PCG32 pcg = random::PCG32::construct(initparams.state); + random::PCG32 pcg = random::PCG32::construct(initparams.halfSeed); random::DimAdaptorRecursive rand2d = random::DimAdaptorRecursive::construct(pcg); uint32_t2 state = rand2d(); this_t t; t.init(state); - t.rc.state = initparams.state; + t.rc.halfSeed = initparams.halfSeed; t.verbose = initparams.verbose; t.initBxDF(t.rc); @@ -322,7 +321,7 @@ struct TestCTGenerateH : TestBxDF if (base_t::isointer.getNdotV() <= numeric_limits::min) return BET_INVALID; else if (traits_t::type == bxdf::BT_BSDF) - if (abs(base_t::isointer.getNdotV()) <= numeric_limits::min) + if (hlsl::abs(base_t::isointer.getNdotV()) <= numeric_limits::min) return BET_INVALID; ErrorType res = compute(); @@ -334,13 +333,13 @@ struct TestCTGenerateH : TestBxDF static void run(NBL_CONST_REF_ARG(STestInitParams) initparams, NBL_REF_ARG(FailureCallback) cb) { - random::PCG32 pcg = random::PCG32::construct(initparams.state); + random::PCG32 pcg = random::PCG32::construct(initparams.halfSeed); random::DimAdaptorRecursive rand2d = random::DimAdaptorRecursive::construct(pcg); uint32_t2 state = rand2d(); this_t t; t.init(state); - t.rc.state = initparams.state; + t.rc.halfSeed = initparams.halfSeed; t.numSamples = initparams.samples; t.immediateFail = initparams.immediateFail; t.initBxDF(t.rc); @@ -376,7 +375,4 @@ struct TestCTGenerateH : TestBxDF }; #endif -} -} - #endif \ No newline at end of file diff --git a/66_HLSLBxDFTests/app_resources/tests.hlsl b/66_HLSLBxDFTests/app_resources/tests.hlsl index 9011aa2e5..8f26bc4ee 100644 --- a/66_HLSLBxDFTests/app_resources/tests.hlsl +++ b/66_HLSLBxDFTests/app_resources/tests.hlsl @@ -3,11 +3,6 @@ #include "tests_common.hlsl" -namespace nbl -{ -namespace hlsl -{ - template struct TestJacobian : TestBxDF { @@ -69,7 +64,6 @@ struct TestJacobian : TestBxDF if (!(s.isValid() && sx.isValid() && sy.isValid())) return BET_INVALID; - // TODO: add checks with need clamp trait if (traits_t::type == bxdf::BT_BRDF) { if (s.getNdotL() <= bit_cast(numeric_limits::min)) @@ -77,7 +71,7 @@ struct TestJacobian : TestBxDF } else if (traits_t::type == bxdf::BT_BSDF) { - if (abs(s.getNdotL()) <= bit_cast(numeric_limits::min)) + if (hlsl::abs(s.getNdotL()) <= bit_cast(numeric_limits::min)) return BET_INVALID; } @@ -115,7 +109,7 @@ struct TestJacobian : TestBxDF } else if (traits_t::type == bxdf::BT_BSDF) { - if (abs(base_t::isointer.getNdotV()) <= bit_cast(numeric_limits::min)) + if (hlsl::abs(base_t::isointer.getNdotV()) <= bit_cast(numeric_limits::min)) return BET_INVALID; } @@ -163,13 +157,13 @@ struct TestJacobian : TestBxDF static void run(NBL_CONST_REF_ARG(STestInitParams) initparams, NBL_REF_ARG(FailureCallback) cb) { - random::PCG32 pcg = random::PCG32::construct(initparams.state); + random::PCG32 pcg = random::PCG32::construct(initparams.halfSeed); random::DimAdaptorRecursive rand2d = random::DimAdaptorRecursive::construct(pcg); uint32_t2 state = rand2d(); this_t t; t.init(state); - t.rc.state = initparams.state; + t.rc.halfSeed = initparams.halfSeed; t.verbose = initparams.verbose; t.initBxDF(t.rc); @@ -245,7 +239,6 @@ struct TestReciprocity : TestBxDF if (!s.isValid()) return BET_INVALID; - // TODO: add checks with need clamp trait if (bxdf::traits::type == bxdf::BT_BRDF) { if (s.getNdotL() <= bit_cast(numeric_limits::min)) @@ -253,7 +246,7 @@ struct TestReciprocity : TestBxDF } else if (bxdf::traits::type == bxdf::BT_BSDF) { - if (abs(s.getNdotL()) <= bit_cast(numeric_limits::min)) + if (hlsl::abs(s.getNdotL()) <= bit_cast(numeric_limits::min)) return BET_INVALID; } @@ -264,6 +257,7 @@ struct TestReciprocity : TestBxDF rec_s = sample_t::createFromTangentSpace(rec_localL, anisointer.getFromTangentSpace()); rec_isointer = iso_interaction_t::create(rec_V, base_t::rc.N); + rec_isointer.luminosityContributionHint = isointer.luminosityContributionHint; rec_anisointer = aniso_interaction_t::create(rec_isointer, base_t::rc.T, base_t::rc.B); rec_cache = cache; rec_cache.iso_cache.VdotH = cache.iso_cache.getLdotH(); @@ -330,7 +324,7 @@ struct TestReciprocity : TestBxDF } else if (traits_t::type == bxdf::BT_BSDF) { - if (abs(base_t::isointer.getNdotV()) <= bit_cast(numeric_limits::min)) + if (hlsl::abs(base_t::isointer.getNdotV()) <= bit_cast(numeric_limits::min)) return BET_INVALID; } @@ -363,13 +357,13 @@ struct TestReciprocity : TestBxDF static void run(NBL_CONST_REF_ARG(STestInitParams) initparams, NBL_REF_ARG(FailureCallback) cb) { - random::PCG32 pcg = random::PCG32::construct(initparams.state); + random::PCG32 pcg = random::PCG32::construct(initparams.halfSeed); random::DimAdaptorRecursive rand2d = random::DimAdaptorRecursive::construct(pcg); uint32_t2 state = rand2d(); this_t t; t.init(state); - t.rc.state = initparams.state; + t.rc.halfSeed = initparams.halfSeed; t.verbose = initparams.verbose; t.initBxDF(t.rc); @@ -517,7 +511,7 @@ struct TestBucket : TestBxDF } else if (traits_t::type == bxdf::BT_BSDF) { - if (abs(base_t::isointer.getNdotV()) <= bit_cast(numeric_limits::min)) + if (hlsl::abs(base_t::isointer.getNdotV()) <= bit_cast(numeric_limits::min)) return BET_INVALID; } @@ -530,13 +524,13 @@ struct TestBucket : TestBxDF static void run(NBL_CONST_REF_ARG(STestInitParams) initparams, NBL_REF_ARG(FailureCallback) cb) { - random::PCG32 pcg = random::PCG32::construct(initparams.state); + random::PCG32 pcg = random::PCG32::construct(initparams.halfSeed); random::DimAdaptorRecursive rand2d = random::DimAdaptorRecursive::construct(pcg); uint32_t2 state = rand2d(); this_t t; t.init(state); - t.rc.state = initparams.state; + t.rc.halfSeed = initparams.halfSeed; t.numSamples = initparams.samples; t.initBxDF(t.rc); @@ -735,7 +729,7 @@ struct TestChi2 : TestBxDF void writeToEXR() { - std::string filename = std::format("chi2test_{}_{}.exr", base_t::rc.state, base_t::name); + std::string filename = std::format("chi2test_{}_{}.exr", base_t::rc.halfSeed, base_t::name); int totalWidth = phiSplits; int totalHeight = 2 * thetaSplits + 1; @@ -869,7 +863,7 @@ struct TestChi2 : TestBxDF cache.iso_cache.absNdotH = hlsl::abs(hlsl::dot(N, H)); cache.iso_cache.NdotH2 = cache.iso_cache.absNdotH * cache.iso_cache.absNdotH; - if (!cache.isValid(bxdf::fresnel::OrientedEtas >::create(1.f, hlsl::promote >(eta)))) + if (!cache.isValid(bxdf::fresnel::OrientedEtas >::create(1.f, hlsl::promote >(eta)))) return 0.f; const float32_t3 T = base_t::anisointer.getT(); @@ -911,7 +905,7 @@ struct TestChi2 : TestBxDF if (base_t::isointer.getNdotV() <= numeric_limits::min) return BET_INVALID; else if (traits_t::type == bxdf::BT_BSDF) - if (abs(base_t::isointer.getNdotV()) <= numeric_limits::min) + if (hlsl::abs(base_t::isointer.getNdotV()) <= numeric_limits::min) return BET_INVALID; ErrorType res = compute(); @@ -994,13 +988,13 @@ struct TestChi2 : TestBxDF static void run(NBL_CONST_REF_ARG(STestInitParams) initparams, NBL_REF_ARG(FailureCallback) cb) { - random::PCG32 pcg = random::PCG32::construct(initparams.state); + random::PCG32 pcg = random::PCG32::construct(initparams.halfSeed); random::DimAdaptorRecursive rand2d = random::DimAdaptorRecursive::construct(pcg); uint32_t2 state = rand2d(); this_t t; t.init(state); - t.rc.state = initparams.state; + t.rc.halfSeed = initparams.halfSeed; t.numSamples = initparams.samples; t.thetaSplits = initparams.thetaSplits; t.phiSplits = initparams.phiSplits; @@ -1034,7 +1028,4 @@ struct TestChi2 : TestBxDF }; #endif -} -} - #endif \ No newline at end of file diff --git a/66_HLSLBxDFTests/app_resources/tests_common.hlsl b/66_HLSLBxDFTests/app_resources/tests_common.hlsl index c0a8d9614..f15451fce 100644 --- a/66_HLSLBxDFTests/app_resources/tests_common.hlsl +++ b/66_HLSLBxDFTests/app_resources/tests_common.hlsl @@ -41,31 +41,29 @@ using namespace IMATH; using json = nlohmann::json; #endif -namespace nbl -{ -namespace hlsl -{ +using namespace nbl; +using namespace hlsl; +using spectral_t = hlsl::vector; using ray_dir_info_t = bxdf::ray_dir_info::SBasic; -using iso_interaction = bxdf::surface_interactions::SIsotropic; +using iso_interaction = bxdf::surface_interactions::SIsotropic; using aniso_interaction = bxdf::surface_interactions::SAnisotropic; using sample_t = bxdf::SLightSample; using iso_cache = bxdf::SIsotropicMicrofacetCache; using aniso_cache = bxdf::SAnisotropicMicrofacetCache; using quotient_pdf_t = sampling::quotient_and_pdf; -using spectral_t = vector; using iso_config_t = bxdf::SConfiguration; using aniso_config_t = bxdf::SConfiguration; using iso_microfacet_config_t = bxdf::SMicrofacetConfiguration; using aniso_microfacet_config_t = bxdf::SMicrofacetConfiguration; -using bool32_t3 = vector; +using bool32_t3 = hlsl::vector; template struct ConvertToFloat01 { - using ret_t = conditional_t::Dimension==1, float, vector::Dimension> >; + using ret_t = conditional_t::Dimension==1, float, hlsl::vector::Dimension> >; static ret_t __call(T x) { @@ -78,19 +76,27 @@ bool checkEq(T a, T b, float32_t eps) { T _a = hlsl::abs(a); T _b = hlsl::abs(b); - return nbl::hlsl::all::Dimension> >(nbl::hlsl::max(_a / _b, _b / _a) <= hlsl::promote(1 + eps)); + return nbl::hlsl::all::Dimension> >(nbl::hlsl::max(_a / _b, _b / _a) <= hlsl::promote(1 + eps)); +} + +template<> +bool checkEq(float32_t a, float32_t b, float32_t eps) +{ + float32_t _a = hlsl::abs(a); + float32_t _b = hlsl::abs(b); + return nbl::hlsl::max(_a / _b, _b / _a) <= float32_t(1 + eps); } template bool checkLt(T a, T b) { - return nbl::hlsl::all::Dimension> >(a < b); + return nbl::hlsl::all::Dimension> >(a < b); } template bool checkZero(T a, float32_t eps) { - return nbl::hlsl::all::Dimension> >(nbl::hlsl::abs(a) < hlsl::promote(eps)); + return nbl::hlsl::all::Dimension> >(nbl::hlsl::abs(a) < hlsl::promote(eps)); } template<> @@ -110,12 +116,9 @@ struct SBxDFTestResources retval.u = ConvertToFloat01::__call(rng_vec3()); retval.u.x = hlsl::clamp(retval.u.x, retval.eps, 1.f-retval.eps); retval.u.y = hlsl::clamp(retval.u.y, retval.eps, 1.f-retval.eps); - // retval.u.z = 0.0; retval.V.direction = nbl::hlsl::normalize(sampling::UniformSphere::generate(ConvertToFloat01::__call(rng_vec2()))); retval.N = nbl::hlsl::normalize(sampling::UniformSphere::generate(ConvertToFloat01::__call(rng_vec2()))); - // if (hlsl::dot(retval.N, retval.V.direction) < 0) - // retval.V.direction = -retval.V.direction; float32_t3 tangent, bitangent; math::frisvad(retval.N, tangent, bitangent); @@ -131,11 +134,14 @@ struct SBxDFTestResources retval.alpha.y = ConvertToFloat01::__call(retval.rng()); retval.eta = ConvertToFloat01::__call(rng_vec2()) * hlsl::promote(1.5) + hlsl::promote(1.1); // range [1.1,2.6], also only do eta = eta/1.0 (air) retval.luma_coeff = float32_t3(0.2126, 0.7152, 0.0722); // luma coefficients for Rec. 709 + + retval.Dinc = ConvertToFloat01::__call(retval.rng()) * 2400.0f + 100.0f; + retval.etaThinFilm = ConvertToFloat01::__call(retval.rng()) * 0.5 + 1.1f; // range [1.1,1.6] return retval; } float eps = 1e-3; // epsilon - uint32_t state; // init state seed, for debugging + uint32_t halfSeed; // init state seed, for debugging nbl::hlsl::Xoroshiro64Star rng; ray_dir_info_t V; @@ -147,12 +153,16 @@ struct SBxDFTestResources float32_t2 alpha; float32_t2 eta; // (eta, etak) float32_t3 luma_coeff; + + // thin film stuff; + float Dinc; // in nm [100, 2500] + float etaThinFilm; }; struct STestInitParams { bool logInfo; - uint32_t state; + uint32_t halfSeed; uint32_t samples; uint32_t thetaSplits; uint32_t phiSplits; @@ -184,6 +194,7 @@ struct TestBase rc = SBxDFTestResources::create(seed); isointer = iso_interaction::create(rc.V, rc.N); + isointer.luminosityContributionHint = rc.luma_coeff; anisointer = aniso_interaction::create(isointer, rc.T, rc.B); } @@ -315,6 +326,28 @@ struct TestBxDF> : } }; +template<> +struct TestBxDF> : TestBxDFBase> +{ + using base_t = TestBxDFBase>; + + void initBxDF(SBxDFTestResources _rc) + { + base_t::bxdf.ndf = base_t::bxdf_t::ndf_type::create(_rc.alpha.x); + using fresnel_base_t = base_t::bxdf_t::fresnel_type::base_type; + fresnel_base_t base; + base.Dinc = _rc.Dinc; + base.thinFilmIor = hlsl::promote(_rc.etaThinFilm); + base.eta12 = hlsl::promote(_rc.etaThinFilm) / hlsl::promote(1.0); + base.eta23 = hlsl::promote(_rc.eta.x) / hlsl::promote(_rc.etaThinFilm); + base.etak23 = hlsl::promote(_rc.eta.y) / hlsl::promote(_rc.etaThinFilm); + base_t::bxdf.fresnel.__base = base; +#ifndef __HLSL_VERSION + base_t::name = "Iridescent BRDF"; +#endif + } +}; + template<> struct TestBxDF> : TestBxDFBase> { @@ -354,7 +387,6 @@ struct TestBxDF> : TestB { using spectral_type = typename base_t::bxdf_t::spectral_type; base_t::bxdf.fresnel = bxdf::fresnel::Dielectric::create(bxdf::fresnel::OrientedEtas::create(base_t::isointer.getNdotV(bxdf::BxDFClampMode::BCM_ABS), hlsl::promote(_rc.eta.x))); - base_t::bxdf.luminosityContributionHint = _rc.luma_coeff; #ifndef __HLSL_VERSION base_t::name = "Thin smooth dielectric BSDF"; #endif @@ -438,37 +470,63 @@ struct TestBxDF> : TestBxD } }; +template +struct TestBxDF> : TestBxDFBase> +{ + using base_t = TestBxDFBase>; + + void initBxDF(SBxDFTestResources _rc) + { + base_t::bxdf.ndf = base_t::bxdf_t::ndf_type::create(_rc.alpha.x); + using fresnel_base_t = base_t::bxdf_t::fresnel_type::base_type; + fresnel_base_t base; + base.Dinc = _rc.Dinc; + base.thinFilmIor = hlsl::promote(_rc.etaThinFilm); + base.eta12 = hlsl::promote(_rc.etaThinFilm) / hlsl::promote(1.0); + base.eta23 = hlsl::promote(_rc.eta.x) / hlsl::promote(_rc.etaThinFilm); + base.etak23 = hlsl::promote(0.0); + base_t::bxdf.fresnel.__base = base; +#ifndef __HLSL_VERSION + base_t::name = "Iridescent BSDF"; +#endif + } +}; + namespace reciprocity_test_impl { -template) +template && concepts::FloatingPointLikeVectorial) struct SIsotropic { + using this_t = SIsotropic; using ray_dir_info_type = RayDirInfo; using scalar_type = typename RayDirInfo::scalar_type; using vector3_type = typename RayDirInfo::vector3_type; + using spectral_type = Spectrum; // WARNING: Changed since GLSL, now arguments need to be normalized! - static SIsotropic create(NBL_CONST_REF_ARG(RayDirInfo) normalizedV, const vector3_type normalizedN) + static this_t create(NBL_CONST_REF_ARG(RayDirInfo) normalizedV, const vector3_type normalizedN) { - SIsotropic retval; + this_t retval; retval.V = normalizedV; retval.N = normalizedN; retval.NdotV = nbl::hlsl::dot(retval.N, retval.V.getDirection()); retval.NdotV2 = retval.NdotV * retval.NdotV; + retval.luminosityContributionHint = hlsl::promote(1.0); return retval; } template) - static SIsotropic copy(NBL_CONST_REF_ARG(I) other) + static this_t copy(NBL_CONST_REF_ARG(I) other) { - SIsotropic retval; + this_t retval; retval.V = other.getV(); retval.N = other.getN(); retval.NdotV = other.getNdotV(); retval.NdotV2 = other.getNdotV2(); retval.pathOrigin = bxdf::PathOrigin::PO_SENSOR; + retval.luminosityContributionHint = other.luminosityContributionHint; return retval; } @@ -481,12 +539,14 @@ struct SIsotropic scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return NdotV2; } bxdf::PathOrigin getPathOrigin() NBL_CONST_MEMBER_FUNC { return pathOrigin; } + spectral_type getLuminosityContributionHint() NBL_CONST_MEMBER_FUNC { return luminosityContributionHint; } RayDirInfo V; vector3_type N; scalar_type NdotV; scalar_type NdotV2; bxdf::PathOrigin pathOrigin; + spectral_type luminosityContributionHint; }; template) @@ -497,7 +557,8 @@ struct SAnisotropic 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; + using matrix3x3_type = hlsl::matrix; + using spectral_type = typename isotropic_interaction_type::spectral_type; // WARNING: Changed since GLSL, now arguments need to be normalized! static this_t create( @@ -551,6 +612,7 @@ struct SAnisotropic scalar_type getNdotV(bxdf::BxDFClampMode _clamp = bxdf::BxDFClampMode::BCM_NONE) NBL_CONST_MEMBER_FUNC { return isotropic.getNdotV(_clamp); } scalar_type getNdotV2() NBL_CONST_MEMBER_FUNC { return isotropic.getNdotV2(); } bxdf::PathOrigin getPathOrigin() NBL_CONST_MEMBER_FUNC { return isotropic.getPathOrigin(); } + spectral_type getLuminosityContributionHint() NBL_CONST_MEMBER_FUNC { return isotropic.getLuminosityContributionHint(); } vector3_type getT() NBL_CONST_MEMBER_FUNC { return T; } vector3_type getB() NBL_CONST_MEMBER_FUNC { return B; } @@ -585,10 +647,10 @@ struct CustomIsoMicrofacetConfiguration; - using vector3_type = vector; - using monochrome_type = vector; - using matrix3x3_type = matrix; + using vector2_type = hlsl::vector; + using vector3_type = hlsl::vector; + using monochrome_type = hlsl::vector; + using matrix3x3_type = hlsl::matrix; using isotropic_interaction_type = Interaction; using anisotropic_interaction_type = reciprocity_test_impl::SAnisotropic; using sample_type = LS; @@ -599,12 +661,9 @@ struct CustomIsoMicrofacetConfiguration; +using rectest_iso_interaction = reciprocity_test_impl::SIsotropic; using rectest_aniso_interaction = reciprocity_test_impl::SAnisotropic; using rectest_iso_microfacet_config_t = reciprocity_test_impl::CustomIsoMicrofacetConfiguration; using rectest_aniso_microfacet_config_t = bxdf::SMicrofacetConfiguration; -} -} - #endif diff --git a/66_HLSLBxDFTests/main.cpp b/66_HLSLBxDFTests/main.cpp index a65b443c9..8989cfe1e 100644 --- a/66_HLSLBxDFTests/main.cpp +++ b/66_HLSLBxDFTests/main.cpp @@ -33,34 +33,34 @@ struct PrintFailureCallback : FailureCallback { case BET_INVALID: if (logInfo) - fprintf(stderr, "[INFO] seed %u: %s skipping test due to invalid NdotV/NdotL config\n", failedFor.rc.state, failedFor.name.c_str()); + fprintf(stderr, "[INFO] seed %u: %s skipping test due to invalid NdotV/NdotL config\n", failedFor.rc.halfSeed, failedFor.name.c_str()); break; case BET_NEGATIVE_VAL: - fprintf(stderr, "[ERROR] seed %u: %s pdf/quotient/eval < 0\n", failedFor.rc.state, failedFor.name.c_str()); + fprintf(stderr, "[ERROR] seed %u: %s pdf/quotient/eval < 0\n", failedFor.rc.halfSeed, failedFor.name.c_str()); break; case BET_PDF_ZERO: - fprintf(stderr, "[ERROR] seed %u: %s pdf = 0\n", failedFor.rc.state, failedFor.name.c_str()); + fprintf(stderr, "[ERROR] seed %u: %s pdf = 0\n", failedFor.rc.halfSeed, failedFor.name.c_str()); break; case BET_QUOTIENT_INF: - fprintf(stderr, "[ERROR] seed %u: %s quotient -> inf\n", failedFor.rc.state, failedFor.name.c_str()); + fprintf(stderr, "[ERROR] seed %u: %s quotient -> inf\n", failedFor.rc.halfSeed, failedFor.name.c_str()); break; case BET_JACOBIAN: - fprintf(stderr, "[ERROR] seed %u: %s failed the jacobian * pdf test %s\n", failedFor.rc.state, failedFor.name.c_str(), failedFor.errMsg.c_str()); + fprintf(stderr, "[ERROR] seed %u: %s failed the jacobian * pdf test %s\n", failedFor.rc.halfSeed, failedFor.name.c_str(), failedFor.errMsg.c_str()); break; case BET_PDF_EVAL_DIFF: - fprintf(stderr, "[ERROR] seed %u: %s quotient * pdf != eval %s\n", failedFor.rc.state, failedFor.name.c_str(), failedFor.errMsg.c_str()); + fprintf(stderr, "[ERROR] seed %u: %s quotient * pdf != eval %s\n", failedFor.rc.halfSeed, failedFor.name.c_str(), failedFor.errMsg.c_str()); break; case BET_RECIPROCITY: - fprintf(stderr, "[ERROR] seed %u: %s failed the reciprocity test %s\n", failedFor.rc.state, failedFor.name.c_str(), failedFor.errMsg.c_str()); + fprintf(stderr, "[ERROR] seed %u: %s failed the reciprocity test %s\n", failedFor.rc.halfSeed, failedFor.name.c_str(), failedFor.errMsg.c_str()); break; case BET_PRINT_MSG: - fprintf(stderr, "[ERROR] seed %u: %s error message\n%s\n", failedFor.rc.state, failedFor.name.c_str(), failedFor.errMsg.c_str()); + fprintf(stderr, "[ERROR] seed %u: %s error message\n%s\n", failedFor.rc.halfSeed, failedFor.name.c_str(), failedFor.errMsg.c_str()); break; case BET_GENERATE_H: - fprintf(stderr, "[ERROR] seed %u: %s failed invalid H configuration generated %s\n", failedFor.rc.state, failedFor.name.c_str(), failedFor.errMsg.c_str()); + fprintf(stderr, "[ERROR] seed %u: %s failed invalid H configuration generated %s\n", failedFor.rc.halfSeed, failedFor.name.c_str(), failedFor.errMsg.c_str()); break; default: - fprintf(stderr, "[ERROR] seed %u: %s unknown error\n", failedFor.rc.state, failedFor.name.c_str()); + fprintf(stderr, "[ERROR] seed %u: %s unknown error\n", failedFor.rc.halfSeed, failedFor.name.c_str()); } #ifdef _NBL_DEBUG @@ -183,7 +183,7 @@ int main(int argc, char** argv) auto rJacobian = std::ranges::views::iota(0u, runs); FOR_EACH_BEGIN(rJacobian) STestInitParams initparams{ .logInfo = logInfo }; - initparams.state = i; + initparams.halfSeed = i; initparams.verbose = testconfigs["TestJacobian"]["verbose"]; TestJacobian>::run(initparams, cb); @@ -193,6 +193,7 @@ int main(int argc, char** argv) TestJacobian, true>::run(initparams, cb); TestJacobian, false>::run(initparams, cb); TestJacobian,true>::run(initparams, cb); + TestJacobian, false>::run(initparams, cb); TestJacobian>::run(initparams, cb); TestJacobian>::run(initparams, cb); @@ -203,6 +204,7 @@ int main(int argc, char** argv) TestJacobian, true>::run(initparams, cb); TestJacobian, false>::run(initparams, cb); TestJacobian,true>::run(initparams, cb); + TestJacobian, false>::run(initparams, cb); FOR_EACH_END @@ -211,7 +213,7 @@ int main(int argc, char** argv) auto rReciprocity = std::ranges::views::iota(0u, runs); FOR_EACH_BEGIN(rReciprocity) STestInitParams initparams{ .logInfo = logInfo }; - initparams.state = 3; + initparams.halfSeed = i; initparams.verbose = testconfigs["TestReciprocity"]["verbose"]; TestReciprocity>::run(initparams, cb); @@ -221,16 +223,18 @@ int main(int argc, char** argv) TestReciprocity, true>::run(initparams, cb); TestReciprocity, false>::run(initparams, cb); TestReciprocity, true>::run(initparams, cb); + TestReciprocity, false>::run(initparams, cb); TestReciprocity>::run(initparams, cb); TestReciprocity>::run(initparams, cb); - TestReciprocity>::run(initparams, cb); + TestReciprocity>::run(initparams, cb); TestReciprocity>::run(initparams, cb); TestReciprocity>::run(initparams, cb); TestReciprocity, false>::run(initparams, cb); TestReciprocity, true>::run(initparams, cb); TestReciprocity, false>::run(initparams, cb); TestReciprocity, true>::run(initparams, cb); + //TestReciprocity, false>::run(initparams, cb); // viewing angle changes result? FOR_EACH_END @@ -240,7 +244,7 @@ int main(int argc, char** argv) auto rBucket = std::ranges::views::iota(0u, runs); FOR_EACH_BEGIN(rBucket) STestInitParams initparams{ .logInfo = logInfo }; - initparams.state = i; + initparams.halfSeed = i; initparams.samples = testconfigs["TestBucket"]["samples"]; TestBucket>::run(initparams, cb); @@ -249,6 +253,7 @@ int main(int argc, char** argv) TestBucket, true>::run(initparams, cb); TestBucket, false>::run(initparams, cb); TestBucket, true>::run(initparams, cb); + TestBucket, false>::run(initparams, cb); TestBucket>::run(initparams, cb); TestBucket>::run(initparams, cb); @@ -256,6 +261,7 @@ int main(int argc, char** argv) TestBucket, true>::run(initparams, cb); TestBucket, false>::run(initparams, cb); TestBucket, true>::run(initparams, cb); + TestBucket, false>::run(initparams, cb); FOR_EACH_END @@ -264,7 +270,7 @@ int main(int argc, char** argv) auto rChi2 = std::ranges::views::iota(0u, runs); FOR_EACH_BEGIN_EX(rChi2, std::execution::par_unseq) STestInitParams initparams{ .logInfo = logInfo }; - initparams.state = i; + initparams.halfSeed = i; initparams.samples = testconfigs["TestChi2"]["samples"]; initparams.thetaSplits = testconfigs["TestChi2"]["thetaSplits"]; initparams.phiSplits = testconfigs["TestChi2"]["phiSplits"]; @@ -276,6 +282,7 @@ int main(int argc, char** argv) TestChi2, true>::run(initparams, cb); TestChi2, false>::run(initparams, cb); TestChi2, true>::run(initparams, cb); + TestChi2, false>::run(initparams, cb); TestChi2>::run(initparams, cb); TestChi2>::run(initparams, cb); @@ -283,15 +290,15 @@ int main(int argc, char** argv) TestChi2, true>::run(initparams, cb); TestChi2, false>::run(initparams, cb); TestChi2, true>::run(initparams, cb); + TestChi2, false>::run(initparams, cb); FOR_EACH_END -#if 0 // testing ndf jacobian * dg1, ONLY for cook torrance bxdfs runs = testconfigs["TestNDF"]["runs"]; auto rNdf = std::ranges::views::iota(0u, runs); FOR_EACH_BEGIN(rNdf) STestInitParams initparams{ .logInfo = logInfo }; - initparams.state = i; + initparams.halfSeed = i; initparams.verbose = testconfigs["TestNDF"]["verbose"]; TestNDF, false>::run(initparams, cb); @@ -304,14 +311,13 @@ int main(int argc, char** argv) TestNDF, false>::run(initparams, cb); TestNDF, true>::run(initparams, cb); FOR_EACH_END -#endif -#if 0 + // test generated H that NdotV*VdotH>=0.0, VdotL calculation runs = testconfigs["TestCTGenerateH"]["runs"]; auto rGenerateH = std::ranges::views::iota(0u, runs); FOR_EACH_BEGIN_EX(rGenerateH, std::execution::par_unseq) STestInitParams initparams{ .logInfo = logInfo }; - initparams.state = i; + initparams.halfSeed = i; initparams.samples = testconfigs["TestCTGenerateH"]["samples"]; initparams.immediateFail = testconfigs["TestCTGenerateH"]["immediateFail"]; @@ -325,27 +331,39 @@ int main(int argc, char** argv) TestCTGenerateH, false>::run(initparams, cb); TestCTGenerateH, true>::run(initparams, cb); FOR_EACH_END -#endif // test arccos angle sums { Xoroshiro64Star rng = Xoroshiro64Star::construct(uint32_t2(4, 2)); + math::sincos_accumulator angle_adder; + + auto Sin = [&](const float cosA) -> float + { + return nbl::hlsl::sqrt(1.f - cosA * cosA); + }; + for (uint32_t i = 0; i < 10; i++) { - const float a = rng() * numbers::pi; - const float b = rng() * numbers::pi; - const float c = rng() * numbers::pi; - const float d = rng() * numbers::pi; + const float a = ConvertToFloat01::__call(rng()) * 2.f - 1.f; + const float b = ConvertToFloat01::__call(rng()) * 2.f - 1.f; + const float c = ConvertToFloat01::__call(rng()) * 2.f - 1.f; + const float d = ConvertToFloat01::__call(rng()) * 2.f - 1.f; const float exAB = acos(a) + acos(b); - float res = math::getSumofArccosAB(a, b); - if (res != exAB) - fprintf(stderr, "[ERROR] math::getSumofArccosAB failed! expected %f, got %f\n", exAB, res); + angle_adder = math::sincos_accumulator::create(a, Sin(a)); + angle_adder.addAngle(b, Sin(b)); + float res = angle_adder.getSumofArccos(); + if (!checkEq(res, exAB, 1e-3)) + fprintf(stderr, "[ERROR] angle adding (2 angles) failed! expected %f, got %f\n", exAB, res); const float exABCD = exAB + acos(c) + acos(d); - res = math::getSumofArccosABCD(a, b, c, d); - if (res != exABCD) - fprintf(stderr, "[ERROR] math::getSumofArccosABCD failed! expected %f, got %f\n", exABCD, res); + angle_adder = math::sincos_accumulator::create(a, Sin(a)); + angle_adder.addAngle(b, Sin(b)); + angle_adder.addAngle(c, Sin(c)); + angle_adder.addAngle(d, Sin(d)); + res = angle_adder.getSumofArccos(); + if (!checkEq(res, exABCD, 1e-3)) + fprintf(stderr, "[ERROR] angle adding (4 angles) failed! expected %f, got %f\n", exABCD, res); } }