From 8e7615d38b1c134bbf3d586292d6274ff4e4d5ea Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 10 Mar 2026 16:26:56 +0700 Subject: [PATCH 01/55] fix spirv qualifiers for HitTriangleVertexPositionsKHR --- include/nbl/builtin/hlsl/spirv_intrinsics/raytracing.hlsl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/nbl/builtin/hlsl/spirv_intrinsics/raytracing.hlsl b/include/nbl/builtin/hlsl/spirv_intrinsics/raytracing.hlsl index 2e77ef6d25..86a5eb2299 100644 --- a/include/nbl/builtin/hlsl/spirv_intrinsics/raytracing.hlsl +++ b/include/nbl/builtin/hlsl/spirv_intrinsics/raytracing.hlsl @@ -77,6 +77,8 @@ float2 rayQueryGetIntersectionBarycentricsKHR([[vk::ext_reference]] RayQueryKHR float2 rayQueryGetIntersectionFrontFaceKHR([[vk::ext_reference]] RayQueryKHR query, uint32_t committed); // position fetch for ray tracing uses gl_HitTriangleVertexPositionsEXT -> HitTriangleVertexPositionsKHR decorated OpVariable +[[vk::ext_capability(spv::CapabilityRayTracingPositionFetchKHR)]] +[[vk::ext_extension("SPV_KHR_ray_tracing_position_fetch")]] [[vk::ext_builtin_input(spv::BuiltInHitTriangleVertexPositionsKHR)]] static const float32_t3 HitTriangleVertexPositionsKHR[3]; From b9ec10e1b7998f28ddae82a32e1624771d4c9362 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 11 Mar 2026 11:58:44 +0700 Subject: [PATCH 02/55] fix transpose when matrix N!=M --- .../hlsl/cpp_compat/impl/intrinsics_impl.hlsl | 12 +++++++++++- include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl | 6 ++++-- 2 files changed, 15 insertions(+), 3 deletions(-) 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 cbaa0a7b7b..61081ea327 100644 --- a/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl +++ b/include/nbl/builtin/hlsl/cpp_compat/impl/intrinsics_impl.hlsl @@ -159,7 +159,6 @@ template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(find_lsb_helper, findIL template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(bitReverse_helper, bitReverse, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(dot_helper, dot, (T), (T)(T), typename vector_traits::scalar_type) -template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(transpose_helper, transpose, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(length_helper, length, (T), (T), typename vector_traits::scalar_type) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(normalize_helper, normalize, (T), (T), T) template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(rsqrt_helper, inverseSqrt, (T), (T), T) @@ -204,6 +203,17 @@ template AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER(bitCount_helper, bitCou #undef ARG #undef AUTO_SPECIALIZE_TRIVIAL_CASE_HELPER +template NBL_PARTIAL_REQ_TOP(concepts::Matrix) +struct transpose_helper) > +{ + using transposed_t = typename matrix_traits::transposed_type; + + static transposed_t __call(NBL_CONST_REF_ARG(Matrix) m) + { + using traits = matrix_traits; + return spirv::transpose(m); + } +}; template NBL_PARTIAL_REQ_TOP(is_same_v) struct find_msb_helper) > { diff --git a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl index 9190a4ec73..3497babfeb 100644 --- a/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl +++ b/include/nbl/builtin/hlsl/spirv_intrinsics/core.hlsl @@ -10,9 +10,11 @@ #include "spirv/unified1/spirv.hpp" #include +#include #include #include #include +#include namespace nbl { @@ -331,9 +333,9 @@ template) [[vk::ext_instruction( spv::OpDot )]] typename vector_traits::scalar_type dot(Vector lhs, Vector rhs); -template +template) [[vk::ext_instruction( spv::OpTranspose )]] -Matrix transpose(Matrix mat); +typename matrix_traits::transposed_type transpose(Matrix mat); template [[vk::ext_instruction(spv::OpBitCount)]] From cb13a6bbaf0bc0de0ab00a1a92709b6e02c2feaf Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 11 Mar 2026 11:59:48 +0700 Subject: [PATCH 03/55] fix pseudoInverse3x4, some bugs in linalg/basic.hlsl --- .../nbl/builtin/hlsl/math/linalg/basic.hlsl | 4 ++-- .../builtin/hlsl/math/linalg/fast_affine.hlsl | 22 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/include/nbl/builtin/hlsl/math/linalg/basic.hlsl b/include/nbl/builtin/hlsl/math/linalg/basic.hlsl index 64f923a521..61dd2544b1 100644 --- a/include/nbl/builtin/hlsl/math/linalg/basic.hlsl +++ b/include/nbl/builtin/hlsl/math/linalg/basic.hlsl @@ -23,7 +23,7 @@ MatT diagonal(typename matrix_traits::scalar_type diagonal = 1) { MatT output; output[0][1] = 124; - using RowT = matrix_traits::row_type; + using RowT = typename matrix_traits::row_type; NBL_UNROLL for (uint32_t i = 0; i < matrix_traits::RowCount; ++i) { @@ -84,7 +84,7 @@ matrix promote_affine(const matrix inMatrix) { matrix retval; - using out_row_t = hlsl::vector; + using out_row_t = vector; NBL_UNROLL for (uint32_t row_i = 0; row_i < NIn; row_i++) { diff --git a/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl b/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl index 605a107b83..1559a29d8b 100644 --- a/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl +++ b/include/nbl/builtin/hlsl/math/linalg/fast_affine.hlsl @@ -9,7 +9,7 @@ #include #include #include - +#include namespace nbl { @@ -177,17 +177,23 @@ struct cofactors template) // TODO: allow any matrix type AND our emulated ones Mat3x4 pseudoInverse3x4(NBL_CONST_REF_ARG(Mat3x4) tform, NBL_CONST_REF_ARG(matrix,3,3>) sub3x3Inv) { - Mat3x4 retval; - retval[0] = sub3x3Inv[0]; - retval[1] = sub3x3Inv[1]; - retval[2] = sub3x3Inv[2]; - retval[3] = -hlsl::mul(sub3x3Inv,tform[3]); - return retval; + using scalar_type = scalar_type_t; + using Mat4x3 = matrix; + Mat4x3 retval_T; + retval_T[0] = sub3x3Inv[0]; + retval_T[1] = sub3x3Inv[1]; + retval_T[2] = sub3x3Inv[2]; + const vector tform3 = vector(tform[0][3], tform[1][3], tform[2][3]); + retval_T[3] = -hlsl::mul(sub3x3Inv,tform3); + return hlsl::transpose(retval_T); } template) // TODO: allow any matrix type AND our emulated ones Mat3x4 pseudoInverse3x4(NBL_CONST_REF_ARG(Mat3x4) tform) { - return pseudoInverse3x4(tform,inverse(matrix,3,3>(tform))); + using scalar_type = scalar_type_t; + using Mat3x3 = matrix; + Mat3x3 tform3x3 = math::linalg::truncate<3,3,3,4,scalar_type>(tform); + return pseudoInverse3x4(tform,inverse(tform3x3)); } From c545a597751aa72f91bfb985d5d06e98de046e26 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 12 Mar 2026 15:00:01 +0700 Subject: [PATCH 04/55] fixes lookAt calculation for loading toWorld property in mitsuba scene --- src/nbl/ext/MitsubaLoader/PropertyElement.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/nbl/ext/MitsubaLoader/PropertyElement.cpp b/src/nbl/ext/MitsubaLoader/PropertyElement.cpp index 5ea0353a48..1f2d741dbb 100644 --- a/src/nbl/ext/MitsubaLoader/PropertyElement.cpp +++ b/src/nbl/ext/MitsubaLoader/PropertyElement.cpp @@ -258,10 +258,14 @@ std::optional CPropertyElementManager::createPropertyData up[index] = 1.f; } // TODO: after the rm-core matrix PR we need to get rid of the tranpose (I transpose only because of GLM and HLSL mixup) - const auto lookAtGLM = reinterpret_cast(glm::lookAtLH(origin,target,up)); - const auto lookAt = hlsl::transpose(lookAtGLM); + //const auto lookAtGLM = reinterpret_cast(glm::lookAtLH(origin,target,up)); + //const auto lookAt = hlsl::transpose(lookAtGLM); + const auto lookAt = hlsl::math::linalg::rhLookAt(origin, target, up); // mitsuba understands look-at and right-handed camera little bit differently than I do const auto rotation = hlsl::inverse(hlsl::float32_t3x3(lookAt)); + for (auto i = 0; i < 3; i++) + for (auto j = 0; j < 3; j++) + result.mvalue[i][j] = rotation[i][j]; // set the origin to avoid numerical issues for (auto r=0; r<3; r++) { From 1e907f4e74bf5f2028ff6a057b9f7363a721ec8f Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 12 Mar 2026 15:46:00 +0700 Subject: [PATCH 05/55] remove obsolete comments --- src/nbl/ext/MitsubaLoader/PropertyElement.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/nbl/ext/MitsubaLoader/PropertyElement.cpp b/src/nbl/ext/MitsubaLoader/PropertyElement.cpp index 1f2d741dbb..bcfeb5160e 100644 --- a/src/nbl/ext/MitsubaLoader/PropertyElement.cpp +++ b/src/nbl/ext/MitsubaLoader/PropertyElement.cpp @@ -257,9 +257,6 @@ std::optional CPropertyElementManager::createPropertyData } up[index] = 1.f; } - // TODO: after the rm-core matrix PR we need to get rid of the tranpose (I transpose only because of GLM and HLSL mixup) - //const auto lookAtGLM = reinterpret_cast(glm::lookAtLH(origin,target,up)); - //const auto lookAt = hlsl::transpose(lookAtGLM); const auto lookAt = hlsl::math::linalg::rhLookAt(origin, target, up); // mitsuba understands look-at and right-handed camera little bit differently than I do const auto rotation = hlsl::inverse(hlsl::float32_t3x3(lookAt)); From 140948104f5f39f07bdab339e41f82d59fc9a115 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 13 Mar 2026 10:12:11 +0700 Subject: [PATCH 06/55] latest example --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index 1c36ceb370..147b5d1e44 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 1c36ceb370be1c402a8c8a3d76aa21d31a48f3ab +Subproject commit 147b5d1e4425f2c2411b2e93d0d828c96517d970 From 5def0b4238db86f7b2c378ac35757f5614f7a9dc Mon Sep 17 00:00:00 2001 From: kevyuu Date: Sat, 14 Mar 2026 10:44:48 +0700 Subject: [PATCH 07/55] Fix checking valid jpeg file logic --- src/nbl/asset/interchange/CImageLoaderJPG.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/nbl/asset/interchange/CImageLoaderJPG.cpp b/src/nbl/asset/interchange/CImageLoaderJPG.cpp index 1db5e16ac2..4c8445190b 100644 --- a/src/nbl/asset/interchange/CImageLoaderJPG.cpp +++ b/src/nbl/asset/interchange/CImageLoaderJPG.cpp @@ -157,10 +157,11 @@ bool CImageLoaderJPG::isALoadableFileFormat(system::IFile* _file, const system:: if (!_file) return false; - uint32_t header = 0; + uint16_t soiMarker = 0; system::IFile::success_t success; - _file->read(success, &header, 6, sizeof(uint32_t)); - return success && ((header&0x00FFD8FFu)==0x00FFD8FFu || header == 0x4a464946 || header == 0x4649464a || header == 0x66697845u || header == 0x70747468u); // maybe 0x4a464946 can go + _file->read(success, &soiMarker, 0, sizeof(uint16_t)); + constexpr auto JPEG_VALID_SOI_MARKER = 0xD8FF; + return success && (soiMarker == JPEG_VALID_SOI_MARKER); #endif } From f93f430793c90a1d913200982adbe57c1043dadf Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Sat, 14 Mar 2026 11:56:01 +0100 Subject: [PATCH 08/55] get opacity going! --- include/nbl/ext/MitsubaLoader/SContext.h | 1 + src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp | 227 ++++++++++++++---- .../CMitsubaMaterialCompilerFrontend.cpp | 48 +--- 3 files changed, 185 insertions(+), 91 deletions(-) diff --git a/include/nbl/ext/MitsubaLoader/SContext.h b/include/nbl/ext/MitsubaLoader/SContext.h index d91fd98a9b..cce42b3ca9 100644 --- a/include/nbl/ext/MitsubaLoader/SContext.h +++ b/include/nbl/ext/MitsubaLoader/SContext.h @@ -95,6 +95,7 @@ struct SContext final { Albedo, Opacity, + Weight, MitsubaExtraFactor, Count }; diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index 6fc11258d4..bad7f31590 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -371,6 +371,7 @@ auto SContext::getMaterial( { // only front-face on top layer get changed, Mistuba XML only allows for unobscured/uncoated emission { + // TODO replace with utility auto combinerH = frontPool.emplace(); auto* const combiner = frontPool.deref(combinerH); combiner->lhs = foundEmitter->second._const_cast(); @@ -624,22 +625,43 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW auto createFactorNode = [&](const CElementTexture::SpectrumOrTexture& factor, const ECommonDebug debug)->auto { - const auto mulH = frontPool.emplace(); - auto* mul = frontPool.deref(mulH); spectral_var_t::SCreationParams<3> params = {}; params.knots.uvTransform = getParameters(params.knots.params,factor); params.getSemantics() = spectral_var_t::Semantics::Fixed3_SRGB; const auto factorH = frontPool.emplace(std::move(params)); frontPool.deref(factorH)->debugInfo = commonDebugNames[uint16_t(debug)]._const_cast(); - mul->rhs = factorH; + return factorH; + }; + // TODO: Move this lambda to CFrontendIR for everyone to use + using expr_t = frontend_ir_t::typed_pointer_type; + auto mul = [&](const expr_t original, const expr_t factor)->frontend_ir_t::typed_pointer_type + { + // if there was no original, attenuating a black colour is useless + if (!original) + return {}; + const auto mulH = frontPool.emplace(); + auto* const mul = frontPool.deref(mulH); + mul->lhs = original; + mul->rhs = factor; return mulH; }; + auto add = [&](const expr_t lhs, const expr_t rhs)->expr_t + { + if (!lhs) + return rhs; + if (!rhs) + return lhs; + const auto addH = frontPool.emplace(); + auto* const add = frontPool.deref(addH); + add->lhs = lhs; + add->rhs = rhs; + return addH; + }; + // end TODO auto createCookTorrance = [&](const CElementBSDF::RoughSpecularBase* base, const frontend_ir_t::typed_pointer_type fresnelH, const CElementTexture::SpectrumOrTexture& specularReflectance)->auto { - const auto handle = createFactorNode(specularReflectance,ECommonDebug::MitsubaExtraFactor); - // rhs already filled, waiting for contributor in lhs subtree const auto mulH = frontPool.emplace(); - frontPool.deref(handle)->lhs = mulH; + const auto factorH = createFactorNode(specularReflectance,ECommonDebug::MitsubaExtraFactor); { auto* mul = frontPool.deref(mulH); const auto ctH = frontPool.emplace(); @@ -675,13 +697,13 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW mul->lhs = ctH; mul->rhs = fresnelH; } - return handle; + return mul(mulH,factorH); }; auto createOrenNayar = [&](const CElementTexture::SpectrumOrTexture& albedo, const CElementTexture::FloatOrTexture& alphaU, const CElementTexture::FloatOrTexture& alphaV)->auto { - const auto mulH = createFactorNode(albedo,ECommonDebug::Albedo); + const auto orenNayarH = frontPool.emplace(); + const auto factorH = createFactorNode(albedo,ECommonDebug::Albedo); { - const auto orenNayarH = frontPool.emplace(); auto* orenNayar = frontPool.deref(orenNayarH); // TODO: factor this out between Oren-Nayar and Cook Torrance auto roughness = orenNayar->ndParams.getRougness(); @@ -691,16 +713,16 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW // TODO: check if UV transform is the same and warn if not getParameters({roughness.data()+1,1},alphaV); } - frontPool.deref(mulH)->lhs = orenNayarH; } - return mulH; + return mul(orenNayarH,factorH); }; + using bxdf_t = frontend_ir_t::typed_pointer_type; // the layer returned will never have a bottom BRDF auto createMistubaLeaf = [&](const CElementBSDF* _bsdf/*TODO: debug source information*/)->frontend_ir_t::typed_pointer_type { auto retval = frontPool.emplace(); - auto* const leaf = frontPool.deref(retval); + auto* leaf = frontPool.deref(retval); switch (_bsdf->type) { case CElementBSDF::DIFFUSE: [[fallthrough]]; @@ -782,10 +804,10 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW if (!isConductor) { // want a specularTransmittance instead of SpecularReflectance factor - const auto btdfH = createFactorNode(_bsdf->dielectric.specularTransmittance,ECommonDebug::MitsubaExtraFactor); + const auto factorH = createFactorNode(_bsdf->dielectric.specularTransmittance,ECommonDebug::MitsubaExtraFactor); + bxdf_t btdfH; { const auto* const brdf = frontPool.deref(brdfH); - auto* const btdf = frontPool.deref(btdfH); // make the trans node refraction-less for thin dielectric and apply thin scattering correction to the transmissive fresnel if (_bsdf->type==CElementBSDF::THINDIELECTRIC) { @@ -804,12 +826,12 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW mul->rhs = thinInfiniteScatterH; } // we're rethreading the fresnel here - btdf->lhs = correctedTransmissionH; + btdfH = correctedTransmissionH; } - else // beautiful thing we can reuse the same nodes for a transmission function without touching Etas or anything! - btdf->lhs = brdf->lhs; + else // beautiful thing we can reuse the same BxDF nodes for a transmission function without touching Etas or anything! + btdfH = brdf->lhs; } - leaf->btdf = btdfH; + leaf->btdf = mul(btdfH,factorH); } break; } @@ -821,11 +843,10 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW leaf->brdfTop = dielectricH; const auto transH = frontPool.emplace(); { - auto* const mul = frontPool.deref(transH); - mul->lhs = deltaTransmission._const_cast(); - const auto transmittanceH = createFactorNode(_bsdf->plastic.specularTransmittance,ECommonDebug::MitsubaExtraFactor); - frontPool.deref(transmittanceH)->lhs = fresnelH; - mul->rhs = transmittanceH; + auto* const trans = frontPool.deref(transH); + trans->lhs = deltaTransmission._const_cast(); + const auto factorH = createFactorNode(_bsdf->plastic.specularTransmittance,ECommonDebug::MitsubaExtraFactor); + trans->rhs = mul(fresnelH,factorH); } leaf->btdf = transH; const auto substrateH = frontPool.emplace(); @@ -865,8 +886,11 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } default: assert(false); // we shouldn't get this case here - return errorMaterial._const_cast(); + retval = errorMaterial._const_cast(); + break; } + leaf = frontPool.deref(retval); + assert(leaf->brdfTop); assert(!leaf->brdfBottom); return retval; }; @@ -874,58 +898,160 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW // Post-order Depth First Traversal (create children first, then create parent) struct SStackEntry { + enum class ExpectedNodeType : uint64_t + { + Layer, + Count + }; const CElementBSDF* bsdf; + ExpectedNodeType expectedNodeType : 2 = ExpectedNodeType::Layer; uint64_t visited : 1 = false; - uint64_t expectedNodeType : 2 = false; - uint64_t unused : 61 = false; + uint64_t unused : 61 = 0; }; core::vector stack; stack.reserve(128); - stack.emplace_back() = { - .bsdf = bsdf - }; + stack.emplace_back() = {.bsdf=bsdf}; // for the static casts of handles using block_allocator_t = frontend_ir_t::obj_pool_type::mem_pool_type::block_allocator_type; using node_t = frontend_ir_t::typed_pointer_type; + using expected_e = SStackEntry::ExpectedNodeType; + const node_t errorNodes[size_t(expected_e::Count)] = {errorMaterial}; // TODO: {errorFactor,errorBRDF,errorLayer} core::unordered_map localCache; - while (!stack.front().visited) + localCache.reserve(16); + // + while (!stack.empty()) { - const auto entry = stack.back(); - assert(entry.bsdf); - if (!entry.visited) + auto& entry = stack.back(); + const auto* const _bsdf = entry.bsdf; + assert(_bsdf); + // we only do post-dfs for non-leafs + if (_bsdf->isMeta() && !entry.visited) + { + if (_bsdf->isMeta()) + { + const auto& meta_common = _bsdf->meta_common; + assert(meta_common.childCount); + switch(_bsdf->type) + { + case CElementBSDF::COATING: [[fallthrough]]; + case CElementBSDF::ROUGHCOATING: [[fallthrough]]; + case CElementBSDF::MASK: + { + assert(meta_common.childCount==1); + stack.emplace_back() = {.bsdf=meta_common.bsdf[0],.expectedNodeType=expected_e::Layer}; + break; + } + case CElementBSDF::BUMPMAP: + { + assert(false); // unimplemented + break; + } + case CElementBSDF::NORMALMAP: + { + assert(false); // unimplemented + break; + } + case CElementBSDF::MIXTURE_BSDF: + { + assert(false); // unimplemented + break; + } + case CElementBSDF::BLEND_BSDF: + { + assert(false); // unimplemented + break; + } + case CElementBSDF::TWO_SIDED: + { + assert(meta_common.childCount<=2); + stack.emplace_back() = {.bsdf=meta_common.bsdf[0],.expectedNodeType=expected_e::Layer}; + assert(false); // unimplemented + break; + } + default: + assert(false); // we shouldn't get this case here + break; + } + } + entry.visited = true; + } + else { - node_t newNodeH = {}; // TODO: {errorFactor,errorBRDF,errorLayer}[entry.expectedNodeType]; - if (entry.bsdf->isMeta()) + node_t newNodeH = {}; + if (_bsdf->isMeta()) { - return errorMaterial;// temporary - switch(entry.bsdf->type) + switch(_bsdf->type) { + case CElementBSDF::COATING: [[fallthrough]]; + case CElementBSDF::ROUGHCOATING: + { + const auto coatingH = frontPool.emplace(); + auto* const coating = frontPool.deref(coatingH); + // create BRDF thats cook torrance + // TODO + // BTDF thats transmission with absorption + // TODO + // attach leaf as layer + coating->coated = block_allocator_t::_static_cast(localCache[_bsdf->mask.bsdf[0]]); + newNodeH = coatingH; + break; + } case CElementBSDF::MASK: { - const auto maskH = createFactorNode(entry.bsdf->mask.opacity,ECommonDebug::Opacity); + const auto maskH = frontPool.emplace(); auto* const mask = frontPool.deref(maskH); -// mask->lhs = ; + // + const auto nestedH = block_allocator_t::_static_cast(localCache[_bsdf->mask.bsdf[0]]); + auto* const nested = frontPool.deref(nestedH); + assert(nested && nested->brdfTop); + const auto opacityH = createFactorNode(_bsdf->mask.opacity,ECommonDebug::Opacity); + mask->brdfTop = mul(nested->brdfTop,opacityH); + { + const auto transparencyH = frontPool.emplace(); + frontPool.deref(transparencyH)->child = opacityH; + mask->btdf = add(mul(deltaTransmission._const_cast(),transparencyH),mul(nested->btdf,opacityH)); + } + mask->brdfBottom = mul(nested->brdfBottom,opacityH); newNodeH = maskH; break; } - //case CElementBSDF::TWO_SIDED: + case CElementBSDF::BUMPMAP: + { + assert(false); // unimplemented + break; + } + case CElementBSDF::NORMALMAP: + { + assert(false); // unimplemented + break; + } + case CElementBSDF::MIXTURE_BSDF: + { + assert(false); // unimplemented + break; + } + case CElementBSDF::BLEND_BSDF: + { + assert(false); // unimplemented + break; + } + case CElementBSDF::TWO_SIDED: + { + // create a copy of the layer, with different settings (don't want to disturb the original) // material compiler is designed so that we don't need to reciprocate the BRDFs as we go through layers as long as observer is still on the same side - //break; + assert(false); // unimplemented + break; + } default: assert(false); // we shouldn't get this case here break; } } else - { - newNodeH = createMistubaLeaf(entry.bsdf); - // have to make it available somehow for parent - } - localCache[entry.bsdf] = newNodeH; - stack.back().visited = true; - } - else - { + newNodeH = createMistubaLeaf(_bsdf); + if (!newNodeH) + newNodeH = errorNodes[size_t(entry.expectedNodeType)]; + localCache[_bsdf] = newNodeH; stack.pop_back(); } } @@ -1096,6 +1222,7 @@ SContext::SContext( { #define ADD_DEBUG_NODE(NAME) commonDebugNames[uint16_t(ECommonDebug::NAME)] = frontPool.emplace(#NAME) ADD_DEBUG_NODE(Albedo); + ADD_DEBUG_NODE(Weight); ADD_DEBUG_NODE(Opacity); ADD_DEBUG_NODE(MitsubaExtraFactor); #undef ADD_DEBUG_NODE diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp index 2760c21041..c5a0631ff2 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp @@ -255,16 +255,16 @@ auto CMitsubaMaterialCompilerFrontend::compileToIRTree(asset::material_compiler: { const CElementBSDF* bsdf; IRNode* ir_node = nullptr; - uint32_t parent_ix = static_cast(-1); - uint32_t child_num = 0u; - bool twosided = false; + uint32_t parent_ix = static_cast(-1); // it skips twosided + uint32_t child_num = 0u; // which child am I ? + bool twosided = false; // any parent above twosided bool front = true; CElementBSDF::Type type() const { return bsdf->type; } }; auto node_parent = [](const SNode& node, core::vector& traversal) { return &traversal[node.parent_ix]; }; - core::vector bfs; + core::vector bfs; // skips twosided { core::queue q; { @@ -286,10 +286,13 @@ auto CMitsubaMaterialCompilerFrontend::compileToIRTree(asset::material_compiler: { SNode child_node; child_node.bsdf = (parent.bsdf->type == CElementBSDF::COATING) ? parent.bsdf->coating.bsdf[i] : parent.bsdf->meta_common.bsdf[i]; + // all of this is to traverse as-if twosided doesn't exist child_node.parent_ix = parent.type() == CElementBSDF::TWO_SIDED ? parent.parent_ix : bfs.size(); child_node.twosided = (child_node.type() == CElementBSDF::TWO_SIDED) || parent.twosided; child_node.child_num = (parent.type() == CElementBSDF::TWO_SIDED) ? parent.child_num : i; + // inherit frontsidedness from parent child_node.front = parent.front; + // second child of twosided is not frontsided if (parent.type() == CElementBSDF::TWO_SIDED && i == 1u) child_node.front = false; q.push(child_node); @@ -300,43 +303,6 @@ auto CMitsubaMaterialCompilerFrontend::compileToIRTree(asset::material_compiler: } } - auto createBackfaceNodeFromFrontface = [&ir](const IRNode* front) -> IRNode* - { - switch (front->symbol) - { - case IRNode::ES_BSDF_COMBINER: [[fallthrough]]; - case IRNode::ES_OPACITY: [[fallthrough]]; - case IRNode::ES_GEOM_MODIFIER: [[fallthrough]]; - case IRNode::ES_EMITTER: - case IRNode::ES_ROOT: - return ir->copyNode(front); - case IRNode::ES_BSDF: - { - auto* bsdf = static_cast(front); - if (bsdf->type == IR::CBSDFNode::ET_MICROFACET_DIELECTRIC) - { - auto* dielectric = static_cast(bsdf); - auto* copy = static_cast(ir->copyNode(front)); - if (!copy->thin) //we're always outside in case of thin dielectric - copy->eta = IRNode::color_t(1.f) / copy->eta; - - return copy; - } - else if (bsdf->type == IR::CBSDFNode::ET_MICROFACET_DIFFTRANS) - return ir->copyNode(front); - } - [[fallthrough]]; // intentional - default: - { - // black diffuse otherwise - auto* invalid = ir->allocNode(); - invalid->setSmooth(); - invalid->reflectance = IR::INode::color_t(0.f); - - return invalid; - } - } - }; //create frontface IR IRNode* frontroot = nullptr; From 1a7bf02ceb5ed626ae78049885974d82b1d5ab93 Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Sat, 14 Mar 2026 16:48:18 +0100 Subject: [PATCH 09/55] really struggling with TWO_SIDED --- src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp | 95 +++++++++++--------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index bad7f31590..4bcddf659f 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -832,6 +832,8 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW btdfH = brdf->lhs; } leaf->btdf = mul(btdfH,factorH); + // By default, all non-transmissive scattering models in Mitsuba are one-sided = all transmissive are two sided + leaf->brdfBottom = brdfH; } break; } @@ -882,6 +884,8 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } leaf->brdfTop = diffTransH; leaf->btdf = diffTransH; + // By default, all non-transmissive scattering models in Mitsuba are one-sided = all transmissive are two sided + leaf->brdfBottom = diffTransH; break; } default: @@ -891,32 +895,21 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } leaf = frontPool.deref(retval); assert(leaf->brdfTop); - assert(!leaf->brdfBottom); return retval; }; // Post-order Depth First Traversal (create children first, then create parent) struct SStackEntry { - enum class ExpectedNodeType : uint64_t - { - Layer, - Count - }; const CElementBSDF* bsdf; - ExpectedNodeType expectedNodeType : 2 = ExpectedNodeType::Layer; uint64_t visited : 1 = false; - uint64_t unused : 61 = 0; + uint64_t unused : 63 = 0; }; core::vector stack; stack.reserve(128); stack.emplace_back() = {.bsdf=bsdf}; // for the static casts of handles - using block_allocator_t = frontend_ir_t::obj_pool_type::mem_pool_type::block_allocator_type; - using node_t = frontend_ir_t::typed_pointer_type; - using expected_e = SStackEntry::ExpectedNodeType; - const node_t errorNodes[size_t(expected_e::Count)] = {errorMaterial}; // TODO: {errorFactor,errorBRDF,errorLayer} - core::unordered_map localCache; + core::unordered_map localCache; localCache.reserve(16); // while (!stack.empty()) @@ -935,49 +928,32 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW { case CElementBSDF::COATING: [[fallthrough]]; case CElementBSDF::ROUGHCOATING: [[fallthrough]]; + case CElementBSDF::BUMPMAP: [[fallthrough]]; + case CElementBSDF::NORMALMAP: [[fallthrough]]; case CElementBSDF::MASK: - { assert(meta_common.childCount==1); - stack.emplace_back() = {.bsdf=meta_common.bsdf[0],.expectedNodeType=expected_e::Layer}; - break; - } - case CElementBSDF::BUMPMAP: - { - assert(false); // unimplemented - break; - } - case CElementBSDF::NORMALMAP: - { - assert(false); // unimplemented break; - } case CElementBSDF::MIXTURE_BSDF: - { - assert(false); // unimplemented break; - } case CElementBSDF::BLEND_BSDF: - { - assert(false); // unimplemented + assert(meta_common.childCount==2); break; - } case CElementBSDF::TWO_SIDED: - { assert(meta_common.childCount<=2); - stack.emplace_back() = {.bsdf=meta_common.bsdf[0],.expectedNodeType=expected_e::Layer}; - assert(false); // unimplemented break; - } default: assert(false); // we shouldn't get this case here break; } + stack.emplace_back() = {.bsdf=meta_common.bsdf[0]}; + for (decltype(meta_common.childCount) i=1; iisMeta()) { switch(_bsdf->type) @@ -992,7 +968,7 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW // BTDF thats transmission with absorption // TODO // attach leaf as layer - coating->coated = block_allocator_t::_static_cast(localCache[_bsdf->mask.bsdf[0]]); + coating->coated = localCache[_bsdf->mask.bsdf[0]]._const_cast(); newNodeH = coatingH; break; } @@ -1001,8 +977,8 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW const auto maskH = frontPool.emplace(); auto* const mask = frontPool.deref(maskH); // - const auto nestedH = block_allocator_t::_static_cast(localCache[_bsdf->mask.bsdf[0]]); - auto* const nested = frontPool.deref(nestedH); + const auto nestedH = localCache[_bsdf->mask.bsdf[0]]; + const auto* const nested = frontPool.deref(nestedH); assert(nested && nested->brdfTop); const auto opacityH = createFactorNode(_bsdf->mask.opacity,ECommonDebug::Opacity); mask->brdfTop = mul(nested->brdfTop,opacityH); @@ -1037,8 +1013,39 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } case CElementBSDF::TWO_SIDED: { - // create a copy of the layer, with different settings (don't want to disturb the original) - // material compiler is designed so that we don't need to reciprocate the BRDFs as we go through layers as long as observer is still on the same side + const auto origFrontH = localCache[_bsdf->mask.bsdf[0]]; + const auto* const origFront = frontPool.deref(origFrontH); +#if 0 + // attach the original on both sides + if (_bsdf->twosided.childCount==1) + { + // get to the bottom layer + auto lastLayerH = origTopH; + while (lastLayerH) + { + const auto* const layer = frontPool.deref(lastLayerH); + lastLayerH = layer->coated; + } + // do we even have a non-twosided material? + // + + // create a copy of the layer, with different settings (don't want to disturb the original) + const auto twoSidedH = frontPool.emplace(); + auto* const twoSided = frontPool.deref(twoSidedH); + if (origTop->coated) + { + // + } + else + // + twoSided->brdfBottom = origTop->brdfTop; + } + else + { + // glue a copy of the second child in reverse from its last layer to the front while reciprocating + const auto origBottomH = localCache[_bsdf->mask.bsdf[1]]; + } +#endif assert(false); // unimplemented break; } @@ -1050,7 +1057,7 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW else newNodeH = createMistubaLeaf(_bsdf); if (!newNodeH) - newNodeH = errorNodes[size_t(entry.expectedNodeType)]; + newNodeH = errorMaterial; localCache[_bsdf] = newNodeH; stack.pop_back(); } @@ -1059,7 +1066,7 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW const auto found = localCache.find(bsdf); if (found!=localCache.end()) return errorMaterial; - const auto rootH = block_allocator_t::_static_cast(found->second); + const auto rootH = found->second._const_cast(); if (!rootH) return errorMaterial; auto* const root = frontPool.deref(rootH); From 0dc31aa9c70fcd4383f079ad364de30ad9dba2c6 Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Mon, 16 Mar 2026 18:57:49 +0100 Subject: [PATCH 10/55] add CFrontendIR node copying, stack reversal and individual BxDF expression reciprocation (swapping of interface order) Unfortunately I realized our THINDIELECTRIC correction is badly designed --- .../asset/material_compiler3/CFrontendIR.h | 200 +++++++++++++++--- .../asset/material_compiler3/CFrontendIR.cpp | 129 ++++++++++- 2 files changed, 289 insertions(+), 40 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CFrontendIR.h b/include/nbl/asset/material_compiler3/CFrontendIR.h index d43b0be0ae..6eba4682ee 100644 --- a/include/nbl/asset/material_compiler3/CFrontendIR.h +++ b/include/nbl/asset/material_compiler3/CFrontendIR.h @@ -271,6 +271,26 @@ class CFrontendIR final : public CNodePool protected: friend class CFrontendIR; + // copy + virtual _typed_pointer_type copy(CFrontendIR* ir) const = 0; +#define COPY_DEFAULT_IMPL inline _typed_pointer_type copy(CFrontendIR* ir) const override final \ + { \ + auto& pool = ir->getObjectPool(); \ + const auto copyH = pool.emplace > >(); \ + if (auto* const copy = pool.deref(copyH); copyH) \ + *copy = *this; \ + return copyH; \ + } + + // child managment + virtual inline _typed_pointer_type getChildHandle_impl(const uint8_t ix) const {assert(false); return {};} + inline void setChild(const uint8_t ix, _typed_pointer_type newChild) + { + assert(ix newChild) {assert(false);} + // default is no special checks beyond the above struct SInvalidCheckArgs { @@ -291,7 +311,10 @@ class CFrontendIR final : public CNodePool } return false; } - virtual _typed_pointer_type getChildHandle_impl(const uint8_t ix) const = 0; + + virtual bool inline reciprocatable() const {return false;} + // unless you override it, you're not supposed to call it + virtual void reciprocate(IExprNode* dst) const {assert(reciprocatable() && dst);} virtual inline core::string getLabelSuffix() const {return "";} virtual inline std::string_view getChildName_impl(const uint8_t ix) const {return "";} @@ -356,6 +379,10 @@ class CFrontendIR final : public CNodePool } std::construct_at(reinterpret_cast*>(this+1),std::move(params)); } + inline CSpectralVariable(const CSpectralVariable& other) + { + std::uninitialized_copy_n(other.pWonky(),other.getKnotCount(),pWonky()); + } // encapsulation due to padding abuse inline uint8_t& uvSlot() {return pWonky()->knots.uvSlot();} @@ -390,11 +417,11 @@ class CFrontendIR final : public CNodePool { return sizeof(CSpectralVariable)+sizeof(SCreationParams); } - /* - inline uint32_t getSize() const override + // for copying + static inline uint32_t calc_size(const CSpectralVariable& other) { - return sizeof(CSpectralVariable)+sizeof(SCreationParams<1>)+(getKnotCount()-1)*sizeof(SParameter); - }*/ + return sizeof(CSpectralVariable)+sizeof(SCreationParams<1>)+(other.getKnotCount()-1)*sizeof(SParameter); + } inline operator bool() const {return !invalid(SInvalidCheckArgs{.pool=nullptr,.logger=nullptr});} @@ -403,8 +430,13 @@ class CFrontendIR final : public CNodePool { std::destroy_n(pWonky()->knots.params,getKnotCount()); } + inline _typed_pointer_type copy(CFrontendIR* ir) const override final + { + auto& pool = ir->getObjectPool(); + const uint8_t count = getKnotCount(); + return pool.emplace(*this); + } - inline _typed_pointer_type getChildHandle_impl(const uint8_t ix) const override {return {};} inline bool invalid(const SInvalidCheckArgs& args) const override { const auto knotCount = getKnotCount(); @@ -442,13 +474,14 @@ class CFrontendIR final : public CNodePool private: SCreationParams<1>* pWonky() {return reinterpret_cast*>(this+1);} const SCreationParams<1>* pWonky() const {return reinterpret_cast*>(this+1);} - const uint8_t* paramsBeginPadding() const {return pWonky()->knots.params[0].padding; } + const uint8_t* paramsBeginPadding() const {return pWonky()->knots.params[0].padding;} }; // class IUnaryOp : public obj_pool_type::INonTrivial, public IExprNode { protected: inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final {return child;} + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final {child = newChild;} public: inline uint8_t getChildCount() const override final {return 1;} @@ -459,6 +492,8 @@ class CFrontendIR final : public CNodePool { protected: inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final {return ix ? rhs:lhs;} + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final {*(ix ? &rhs:&lhs) = newChild;} + inline std::string_view getChildName_impl(const uint8_t ix) const override final {return ix ? "rhs":"lhs";} public: @@ -476,6 +511,9 @@ class CFrontendIR final : public CNodePool // you can set the children later inline CMul() = default; + + protected: + COPY_DEFAULT_IMPL }; class CAdd final : public IBinOp { @@ -485,6 +523,9 @@ class CFrontendIR final : public CNodePool // you can set the children later inline CAdd() = default; + + protected: + COPY_DEFAULT_IMPL }; // does `1-expression` class CComplement final : public IUnaryOp @@ -494,6 +535,9 @@ class CFrontendIR final : public CNodePool // you can set the children later inline CComplement() = default; + + protected: + COPY_DEFAULT_IMPL }; // Emission nodes are only allowed in BRDF expressions, not BTDF. To allow different emission on both sides, expressed unambigously. // Basic Emitter - note that it is of unit radiance so its easier to importance sample @@ -517,8 +561,10 @@ class CFrontendIR final : public CNodePool // TODO: semantic flags/metadata (symmetries of the profile) protected: - inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override {return {};} + COPY_DEFAULT_IMPL + NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override; + NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override; }; //! Special nodes meant to be used as `CMul::rhs`, their behaviour depends on the IContributor in its MUL node relative subgraph. @@ -547,9 +593,16 @@ class CFrontendIR final : public CNodePool // Absorption and thickness of the interface combined into a single variable, eta and `LdotX` is taken from the leaf BTDF node. // With refractions from Dielectrics, we get just `1/LdotX`, for Delta Transmission we get `1/VdotN` since its the same typed_pointer_type perpTransmittance = {}; + protected: + COPY_DEFAULT_IMPL + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override {return perpTransmittance;} + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override + { + perpTransmittance = block_allocator_type::_static_cast(newChild); + } inline std::string_view getChildName_impl(const uint8_t ix) const override {return "Perpendicular\\nTransmittance";} NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override; @@ -572,8 +625,22 @@ class CFrontendIR final : public CNodePool uint8_t reciprocateEtas : 1 = false; protected: + COPY_DEFAULT_IMPL + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override {return ix ? orientedImagEta:orientedRealEta;} + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override + { + *(ix ? &orientedImagEta:&orientedRealEta) = block_allocator_type::_static_cast(newChild); + } + NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override; + + inline bool reciprocatable() const override {return true;} + inline void reciprocate(IExprNode* dst) const override + { + (*static_cast(dst) = *this).reciprocateEtas = ~reciprocateEtas; + } + inline std::string_view getChildName_impl(const uint8_t ix) const override {return ix ? "Imaginary":"Real";} NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override; }; @@ -595,7 +662,14 @@ class CFrontendIR final : public CNodePool class CThinInfiniteScatterCorrection final : public obj_pool_type::INonTrivial, public IExprNode { protected: - inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final {return ix ? (ix>1 ? reflectanceBottom:extinction):reflectanceTop;} + COPY_DEFAULT_IMPL + + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override {return ix ? (ix>1 ? reflectanceBottom:extinction):reflectanceTop;} + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override + { + *(ix ? (ix>1 ? &reflectanceBottom:&extinction):&reflectanceTop) = newChild; + } + NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override; inline std::string_view getChildName_impl(const uint8_t ix) const override {return ix ? (ix>1 ? "reflectanceBottom":"extinction"):"reflectanceTop";} @@ -662,9 +736,8 @@ class CFrontendIR final : public CNodePool // - Delta Reflection -> Any Cook Torrance BxDF with roughness=0 attached as BRDF // - Smooth Conductor -> above multiplied with Conductor-Fresnel // - Smooth Dielectric -> Any Cook Torrance BxDF with roughness=0 attached as BRDF on both sides of a Layer and BTDF multiplied with Dielectric-Fresnel (no imaginary component) - // - Thindielectric -> Any Cook Torrance BxDF multiplied with Dielectric-Fresnel as BRDF in both sides and a Delta Transmission BTDF with `CThinInfiniteScatterCorrection` on the fresnel + // - Thindielectric -> Any Cook Torrance BxDF multiplied with Dielectric-Fresnel .... TODO description .... // - Plastic -> Similar to layering the above over Diffuse BRDF, its of uttmost importance that the BTDF is Delta Transmission. - // If one wants to emulate non-linear diffuse TIR color shifts, abuse `CThinInfiniteScatterCorrection`. class CDeltaTransmission final : public IBxDF { public: @@ -673,7 +746,7 @@ class CFrontendIR final : public CNodePool inline CDeltaTransmission() = default; protected: - inline _typed_pointer_type getChildHandle_impl(const uint8_t ix) const override {return {};} + COPY_DEFAULT_IMPL }; //! Because of Schussler et. al 2017 every one of these nodes splits into 2 (if no L dependence) or 3 during canonicalization // Base diffuse node @@ -687,8 +760,10 @@ class CFrontendIR final : public CNodePool SBasicNDFParams ndParams = {}; protected: - inline _typed_pointer_type getChildHandle_impl(const uint8_t ix) const override {return {};} + COPY_DEFAULT_IMPL + NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override; + NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override; }; // Supports anisotropy for all models @@ -714,49 +789,114 @@ class CFrontendIR final : public CNodePool // BRDFs and BTDFs components into separate expressions, and also importance sample much better, for details see comments in CTrueIR. typed_pointer_type orientedRealEta = {}; // - NDF ndf = NDF::GGX; + NDF ndf : 7 = NDF::GGX; + uint8_t reciprocateEta : 1 = false; protected: + COPY_DEFAULT_IMPL + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override {return orientedRealEta;} + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override {orientedRealEta = block_allocator_type::_static_cast(newChild);} + NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override; + + inline bool reciprocatable() const override {return true;} + inline void reciprocate(IExprNode* dst) const override + { + (*static_cast(dst) = *this).reciprocateEta = ~reciprocateEta; + } inline core::string getLabelSuffix() const override {return ndf!=NDF::GGX ? "\\nNDF = Beckmann":"\\nNDF = GGX";} inline std::string_view getChildName_impl(const uint8_t ix) const override {return "Oriented η";} NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override; }; +#undef COPY_DEFAULT_IMPL #undef TYPE_NAME_STR - // Each material comes down to this, YOU MUST NOT MODIFY THE NODES AFTER ADDING THEIR PARENT TO THE ROOT NODES! - // TODO: shall we copy and hand out a new handle? - inline std::span> getMaterials() {return m_rootNodes;} - inline bool addMaterial(const typed_pointer_type rootNode, system::logger_opt_ptr logger) + // basic utilities + inline typed_pointer_type createMul(const typed_pointer_type lhs, const typed_pointer_type rhs) { - if (valid(rootNode,logger)) - { - m_rootNodes.push_back(rootNode); - return true; - } - return false; + if (!lhs || !rhs) // acceptable premaute optimization + return {}; + const auto mulH = getObjectPool().emplace(); + auto* const mul = getObjectPool().deref(mulH); + mul->lhs = lhs; + mul->rhs = rhs; + return mulH; + } + inline typed_pointer_type createAdd(const typed_pointer_type lhs, const typed_pointer_type rhs) + { + if (!lhs) + return rhs; + if (!rhs) + return lhs; + const auto addH = getObjectPool().emplace(); + auto* const add = getObjectPool().deref(addH); + add->lhs = lhs; + add->rhs = rhs; + return addH; + } + inline typed_pointer_type createComplement(const typed_pointer_type child) + { + if (!child) + return {}; + const auto complH = getObjectPool().emplace(); + getObjectPool().deref(complH)->child = child; + return complH; } - // To quickly make a matching backface material from a frontface or vice versa - NBL_API2 typed_pointer_type reciprocate(const typed_pointer_type other); + // To quickly make a fresnel NBL_API2 typed_pointer_type createNamedFresnel(const std::string_view name); inline typed_pointer_type createConstantMonochromeRealFresnel(const hlsl::float32_t orientedRealEta) { - const auto fresnelH = getObjectPool().emplace(); + auto& pool = getObjectPool(); + const auto fresnelH = pool.emplace(); CSpectralVariable::SCreationParams<1> params = {}; params.knots.params[0].scale = orientedRealEta; - if (auto* const fresnel=getObjectPool().deref(fresnelH); fresnel) - fresnel->orientedRealEta = getObjectPool().emplace(std::move(params)); + if (auto* const fresnel=pool.deref(fresnelH); fresnel) + fresnel->orientedRealEta = pool.emplace(std::move(params)); return fresnelH; } + // To quickly make a matching backface BxDF from a frontface or vice versa + NBL_API2 typed_pointer_type reciprocate(const typed_pointer_type orig); + + // a deep copy of the layer stack, wont copy the BxDFs + NBL_API2 typed_pointer_type copyLayers(const typed_pointer_type orig); + // Reverse the linked list of layers and reciprocate their Etas + NBL_API2 typed_pointer_type reverse(const typed_pointer_type orig); + + // first query, we check presence of btdf layers all the way through the layer stack + inline bool transmissive(const typed_pointer_type rootHandle) const + { + auto& pool = getObjectPool(); + for (auto layer=pool.deref(rootHandle); layer; layer=pool.deref(layer->coated)) + { + // it takes only one layer without transmission to break the chain + if (!layer->btdf) + return false; + } + return true; + } + // IMPORTANT: Two BxDFs are not allowed to be multiplied together. // NOTE: Right now all Spectral Variables are required to be Monochrome or 3 bucket fixed semantics, all the same wavelength. // Some things we can't check such as the compatibility of the BTDF with the BRDF (matching indices of refraction, etc.) - bool valid(const typed_pointer_type rootHandle, system::logger_opt_ptr logger) const; + NBL_API2 bool valid(const typed_pointer_type rootHandle, system::logger_opt_ptr logger) const; + + // Each material comes down to this, YOU MUST NOT MODIFY THE NODES AFTER ADDING THEIR PARENT TO THE ROOT NODES! + // TODO: shall we copy and hand out a new handle? + inline std::span> getMaterials() {return m_rootNodes;} + inline bool addMaterial(const typed_pointer_type rootNode, system::logger_opt_ptr logger) + { + if (valid(rootNode,logger)) + { + m_rootNodes.push_back(rootNode); + return true; + } + return false; + } // For Debug Visualization struct SDotPrinter final diff --git a/src/nbl/asset/material_compiler3/CFrontendIR.cpp b/src/nbl/asset/material_compiler3/CFrontendIR.cpp index c4b7fd49a8..357a647da4 100644 --- a/src/nbl/asset/material_compiler3/CFrontendIR.cpp +++ b/src/nbl/asset/material_compiler3/CFrontendIR.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2022-2025 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2022-2025 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h #include "nbl/asset/material_compiler3/CFrontendIR.h" @@ -111,18 +111,127 @@ bool CFrontendIR::CCookTorrance::invalid(const SInvalidCheckArgs& args) const } -auto CFrontendIR::reciprocate(const typed_pointer_type other) -> typed_pointer_type +auto CFrontendIR::reciprocate(const typed_pointer_type orig) -> typed_pointer_type { - if (const auto* in=getObjectPool().deref(block_allocator_type::_static_cast(other)); in) + auto& pool = getObjectPool(); + struct SEntry { - auto fresnelH = getObjectPool().emplace(); - auto* fresnel = getObjectPool().deref(fresnelH); - *fresnel = *in; - fresnel->reciprocateEtas = ~in->reciprocateEtas; - return fresnelH; + typed_pointer_type handle; + bool visited = false; + }; + core::vector stack; + stack.reserve(32); + stack.push_back({.handle=orig}); + // use a hashmap because of holes in child arrays + core::unordered_map,typed_pointer_type> substitutions; + while (!stack.empty()) + { + auto& entry = stack.back(); + const auto* const node = pool.deref(entry.handle); + if (!node) // this is an error + return {}; + const auto childCount = node->getChildCount(); + if (entry.visited) + { + entry.visited = true; + for (uint8_t c=0; cgetChildHandle(c); + if (auto child=pool.deref(childH); !child) + continue; // this is not an error + stack.push_back({.handle=childH}); + } + } + else + { + const bool needToReciprocate = node->reciprocatable(); + bool needToCopy = needToReciprocate; + // if one descendant has changed then we need to copy node + if (!needToCopy) + { + uint8_t c = 0; + for (; cgetChildHandle(c)); found!=substitutions.end()) + break; + } + needToCopy = c!=childCount; + } + if (needToCopy) + { + const auto copyH = node->copy(this); + // copy copies everything including children + auto* const copy = pool.deref(copyH); + if (!copy) + return {}; + if (needToReciprocate) + node->reciprocate(copy); + // only changed children need to be set + for (uint8_t c=0; cgetChildHandle(c); + if (!childH) + continue; + if (auto found=substitutions.find(childH); found!=substitutions.end()) + copy->setChild(c,found->second); + } + substitutions.insert({entry.handle,copyH}); + } + stack.pop_back(); + } + } + // there was nothing to reciprocate in the expression stack + if (substitutions.empty()) + return orig; + return substitutions[orig]; +} + +auto CFrontendIR::copyLayers(const typed_pointer_type orig) -> typed_pointer_type +{ + auto& pool = getObjectPool(); + auto copyH = pool.emplace(); + { + auto* outLayer = pool.deref(copyH); + for (const auto* layer=pool.deref(orig); true; layer=pool.deref(layer->coated)) + { + *outLayer = *layer; + if (!layer->coated) + { + // terminate the new stack + outLayer->coated = {}; + break; + } + // continue the new stack + outLayer->coated = pool.emplace(); + outLayer = pool.deref(outLayer->coated); + } + } + return copyH; +} + +auto CFrontendIR::reverse(const typed_pointer_type orig) -> typed_pointer_type +{ + auto& pool = getObjectPool(); + // we build the new linked list from the tail + auto copyH = pool.emplace(); + { + auto* outLayer = pool.deref(copyH); + typed_pointer_type underLayerH={}; + for (const auto* layer=pool.deref(orig); true; layer=pool.deref(layer->coated)) + { + outLayer->coated = underLayerH; + // we reciprocate everything because numerator and denominator switch (top and bottom of layer stack) + outLayer->brdfBottom = reciprocate(layer->brdfTop)._const_cast(); + outLayer->btdf = reciprocate(layer->btdf)._const_cast(); + outLayer->brdfTop = reciprocate(layer->brdfBottom)._const_cast(); + if (!layer->coated) + break; + underLayerH = copyH; + copyH = pool.emplace(); + outLayer = pool.deref(copyH); + } } - assert(false); // unimplemented - return {}; + return copyH; } auto CFrontendIR::createNamedFresnel(const std::string_view name) -> typed_pointer_type From 8aff0f7b3a9e568ce798dc4fb85d9ac11f0e513e Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Mon, 16 Mar 2026 23:24:22 +0100 Subject: [PATCH 11/55] Beer node needs a thickness unfortunately. --- .../asset/material_compiler3/CFrontendIR.h | 45 +++-- .../asset/material_compiler3/CFrontendIR.cpp | 5 + src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp | 174 ++++++++---------- .../CMitsubaMaterialCompilerFrontend.cpp | 111 +---------- 4 files changed, 119 insertions(+), 216 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CFrontendIR.h b/include/nbl/asset/material_compiler3/CFrontendIR.h index 6eba4682ee..5c7c02a914 100644 --- a/include/nbl/asset/material_compiler3/CFrontendIR.h +++ b/include/nbl/asset/material_compiler3/CFrontendIR.h @@ -584,24 +584,23 @@ class CFrontendIR final : public CNodePool { public: inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CBeer);} - inline uint8_t getChildCount() const override {return 1;} + inline uint8_t getChildCount() const override {return 2;} // you can set the members later inline CBeer() = default; - // Effective transparency = exp2(log2(perpTransmittance)/dot(refract(V,X,eta),X)) = exp2(log2(perpTransmittance)*inversesqrt(1.f+(LdotX-1)*rcpEta)) - // Absorption and thickness of the interface combined into a single variable, eta and `LdotX` is taken from the leaf BTDF node. - // With refractions from Dielectrics, we get just `1/LdotX`, for Delta Transmission we get `1/VdotN` since its the same + // Effective transparency = exp2(log2(perpTransmittance)*thickness/dot(refract(V,X,eta),X)) = exp2(log2(perpTransmittance)*thickness*inversesqrt(1.f+(LdotX-1)*rcpEta)) + // Eta and `LdotX` is taken from the leaf BTDF node. With refractions from Dielectrics, we get just `1/LdotX`, for Delta Transmission we get `1/VdotN` since its the same typed_pointer_type perpTransmittance = {}; - + typed_pointer_type thickness = {}; protected: COPY_DEFAULT_IMPL - inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override {return perpTransmittance;} + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override {return ix ? perpTransmittance:thickness;} inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override { - perpTransmittance = block_allocator_type::_static_cast(newChild); + *(ix ? &perpTransmittance:&thickness) = block_allocator_type::_static_cast(newChild); } inline std::string_view getChildName_impl(const uint8_t ix) const override {return "Perpendicular\\nTransmittance";} @@ -645,20 +644,40 @@ class CFrontendIR final : public CNodePool NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override; }; // Compute Inifinite Scatter and extinction between two parallel infinite planes. + // // It's a specialization of what would be a layer of two identical smooth BRDF and BTDF with arbitrary Fresnel function and beer's - // extinction on the BRDFs (not BTDFs), all applied on a per micro-facet basis (layering per microfacet, not whole surface). + // extinction, all applied on a per micro-facet basis (layering per microfacet, not whole surface) so the NDFs of two layers would be correlated. // - // We actually allow you to use different reflectance nodes R_u and R_b, the NDFs of both layers remain the same but Reflectance Functions to differ. + // We actually allow you to use different reflectance nodes R_u and R_b, the NDFs of both layers remain the same but Reflectance Functions differ. // Note that e.g. using different Etas for the Fresnel used for the top and bottom reflectance will result in a compound Fresnel!=1.0 // meaning that in such case you can no longer optimize the BTDF contributor into a DeltaTransmission but need a CookTorrance with // an Eta equal to the ratio of the first Eta over the second Eta (note that when they're equal the ratio is 1 which turns into Delta Trans). - // + // This will require you to make an AST that "seems wrong" that is where neither of the Etas of the CFresnel nodes match the Cook Torrance one. + // // Because we split BRDF and BTDF into separate expressions, what this node computes differs depending on where it gets used: + // Note the transformation in the equations at the end just makes the prevention of 0/0 or 0*INF same as for a non-extinctive equation, just check `R_u*R_b < Threshold` + // // BRDF: R_u + (1-R_u)^2 E^2 R_b Sum_{i=0}^{\Inf}{(R_b R_u E^2)^i} = R_u + (1-R_u)^2 E^2 R_b / (1 - R_u R_b E^2) = R_u + (1-R_u)^2 R_b / (E^-2 - R_u R_b) + // -------------------- + // Top BRDF as multiplied with CThinInfiniteScatterCorrection node with `reflectanceTop` + // BTDF matching the BRDF above + // Bottom BRDF matching Top (but corellated so you always hit the same microfacet going back) + // Null BRDF + // Delta Transmission Beer extinction + // Null BRDF + // Top Smooth BRDF with `reflectanceBottom` applied to a Delta Reflection + // ------------------ + // // BTDF: (1-R_u) E (1-R_b) Sum_{i=0}^{\Inf}{(R_b R_u E^2)^i} = (1-R_u) E^2 (1-R_b) / (1 - R_u R_b E^2) = (1-R_u) (1-R_b) / (E^-2 - R_u R_b) - // Note the transformation at the end just makes the prevention of 0/0 or 0*INF same as for a non-extinctive equation, just check `R_u*R_b < Threshold` + // -------------------- + // Bottom BRDF as multiplied with CThinInfiniteScatterCorrection node with `reflectanceTop` + // Null BRDF + // Delta Transmission Beer extinction + // Null BRDF + // Top BRDF as multiplied with CThinInfiniteScatterCorrection node but with `reflectanceBottom` (but corellated so you always hit the same microfacet leading to no refraction) + // ------------------ // - // Note: This node can be also used to model non-linear color shifts of Diffuse BRDF multiple scattering if one plugs in the albedo as the extinction. + // The obvious downside of using this node for transmission is that its impossible to get "milky" glass because a spread of refractions is needed class CThinInfiniteScatterCorrection final : public obj_pool_type::INonTrivial, public IExprNode { protected: @@ -736,7 +755,7 @@ class CFrontendIR final : public CNodePool // - Delta Reflection -> Any Cook Torrance BxDF with roughness=0 attached as BRDF // - Smooth Conductor -> above multiplied with Conductor-Fresnel // - Smooth Dielectric -> Any Cook Torrance BxDF with roughness=0 attached as BRDF on both sides of a Layer and BTDF multiplied with Dielectric-Fresnel (no imaginary component) - // - Thindielectric -> Any Cook Torrance BxDF multiplied with Dielectric-Fresnel .... TODO description .... + // - Thindielectric Correlated -> Any Cook Torrance BxDF multiplied with Dielectric-Fresnel, then Delta Transmission as BTDF with fresnels // - Plastic -> Similar to layering the above over Diffuse BRDF, its of uttmost importance that the BTDF is Delta Transmission. class CDeltaTransmission final : public IBxDF { diff --git a/src/nbl/asset/material_compiler3/CFrontendIR.cpp b/src/nbl/asset/material_compiler3/CFrontendIR.cpp index 357a647da4..ce02e46518 100644 --- a/src/nbl/asset/material_compiler3/CFrontendIR.cpp +++ b/src/nbl/asset/material_compiler3/CFrontendIR.cpp @@ -32,6 +32,11 @@ bool CFrontendIR::CBeer::invalid(const SInvalidCheckArgs& args) const args.logger.log("Perpendicular Transparency node of correct type must be attached, but is %u of type %s",ELL_ERROR,perpTransmittance,args.pool->getTypeName(perpTransmittance).data()); return true; } + if (const auto* const thick=args.pool->getObjectPool().deref(thickness); !thick || thick->getKnotCount()!=0) + { + args.logger.log("Monochromatic Thickness node must be attached, but is %u of type %s",ELL_ERROR,thickness,args.pool->getTypeName(thickness).data()); + return true; + } return false; } diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index 4bcddf659f..985847af29 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -632,32 +632,6 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW frontPool.deref(factorH)->debugInfo = commonDebugNames[uint16_t(debug)]._const_cast(); return factorH; }; - // TODO: Move this lambda to CFrontendIR for everyone to use - using expr_t = frontend_ir_t::typed_pointer_type; - auto mul = [&](const expr_t original, const expr_t factor)->frontend_ir_t::typed_pointer_type - { - // if there was no original, attenuating a black colour is useless - if (!original) - return {}; - const auto mulH = frontPool.emplace(); - auto* const mul = frontPool.deref(mulH); - mul->lhs = original; - mul->rhs = factor; - return mulH; - }; - auto add = [&](const expr_t lhs, const expr_t rhs)->expr_t - { - if (!lhs) - return rhs; - if (!rhs) - return lhs; - const auto addH = frontPool.emplace(); - auto* const add = frontPool.deref(addH); - add->lhs = lhs; - add->rhs = rhs; - return addH; - }; - // end TODO auto createCookTorrance = [&](const CElementBSDF::RoughSpecularBase* base, const frontend_ir_t::typed_pointer_type fresnelH, const CElementTexture::SpectrumOrTexture& specularReflectance)->auto { const auto mulH = frontPool.emplace(); @@ -697,7 +671,7 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW mul->lhs = ctH; mul->rhs = fresnelH; } - return mul(mulH,factorH); + return frontIR->createMul(mulH,factorH); }; auto createOrenNayar = [&](const CElementTexture::SpectrumOrTexture& albedo, const CElementTexture::FloatOrTexture& alphaU, const CElementTexture::FloatOrTexture& alphaV)->auto { @@ -714,7 +688,24 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW getParameters({roughness.data()+1,1},alphaV); } } - return mul(orenNayarH,factorH); + return frontIR->createMul(orenNayarH,factorH); + }; + + auto fillCoatingLayer = [&](frontend_ir_t::CLayer* layer, const T& element, const bool rough, const frontend_ir_t::typed_pointer_type extinctionH={})->void + { + const auto fresnelH = frontIR->createConstantMonochromeRealFresnel(element.intIOR/element.extIOR); + const auto dielectricH = createCookTorrance(rough ? &element:nullptr,fresnelH,element.specularReflectance); + layer->brdfTop = dielectricH; + const auto transH = frontPool.emplace(); + { + auto* const trans = frontPool.deref(transH); + trans->lhs = frontIR->createMul(deltaTransmission._const_cast(),extinctionH); + const auto factorH = createFactorNode(element.specularTransmittance,ECommonDebug::MitsubaExtraFactor); + trans->rhs = frontIR->createMul(fresnelH,factorH); + } + layer->btdf = transH; + // identical BRDF on the bottom, to have correct multiscatter + layer->brdfBottom = dielectricH; }; using bxdf_t = frontend_ir_t::typed_pointer_type; @@ -825,35 +816,29 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW mul->lhs = deltaTransmission._const_cast(); mul->rhs = thinInfiniteScatterH; } - // we're rethreading the fresnel here + // We need to attach the other glass interface as another layer because the Etas need to be reciprocated because the interactions are flipped + leaf->coated = frontIR->reverse(retval); + // we're rethreading the fresnel here, but needed to be careful to not apply thindielectric scatter correction and volumetric absorption / Mitsuba extra factor twice btdfH = correctedTransmissionH; } - else // beautiful thing we can reuse the same BxDF nodes for a transmission function without touching Etas or anything! + else + { + // beautiful thing we can reuse the same BxDF nodes for a transmission function without touching Etas or anything! btdfH = brdf->lhs; + leaf->brdfBottom = brdfH; + } } - leaf->btdf = mul(btdfH,factorH); - // By default, all non-transmissive scattering models in Mitsuba are one-sided = all transmissive are two sided - leaf->brdfBottom = brdfH; + leaf->btdf = frontIR->createMul(btdfH,factorH); } break; } case CElementBSDF::PLASTIC: [[fallthrough]]; case CElementBSDF::ROUGHPLASTIC: { - const auto fresnelH = frontIR->createConstantMonochromeRealFresnel(_bsdf->plastic.intIOR/_bsdf->plastic.extIOR); - const auto dielectricH = createCookTorrance(_bsdf->type==CElementBSDF::ROUGHPLASTIC ? &_bsdf->plastic:nullptr,fresnelH,_bsdf->plastic.specularReflectance); - leaf->brdfTop = dielectricH; - const auto transH = frontPool.emplace(); - { - auto* const trans = frontPool.deref(transH); - trans->lhs = deltaTransmission._const_cast(); - const auto factorH = createFactorNode(_bsdf->plastic.specularTransmittance,ECommonDebug::MitsubaExtraFactor); - trans->rhs = mul(fresnelH,factorH); - } - leaf->btdf = transH; + fillCoatingLayer(leaf,_bsdf->plastic,_bsdf->type==CElementBSDF::ROUGHPLASTIC); // shall we plug albedo back inside as an extinction factor !? const auto substrateH = frontPool.emplace(); { - // TODO: `_bsdf->plastic.nonlinear` , do we use beer? + // TODO: `_bsdf->plastic.nonlinear` to let the backend know the multiscatter should match provided albedo? Basically provided albedo is not the color at every bounce but after all bounces auto* const substrate = frontPool.deref(substrateH); substrate->brdfTop = createOrenNayar(_bsdf->plastic.diffuseReflectance,_bsdf->plastic.alphaU,_bsdf->plastic.alphaV); } @@ -884,7 +869,7 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } leaf->brdfTop = diffTransH; leaf->btdf = diffTransH; - // By default, all non-transmissive scattering models in Mitsuba are one-sided = all transmissive are two sided + // By default, all non-transmissive scattering models in Mitsuba are one-sided => all transmissive are two sided leaf->brdfBottom = diffTransH; break; } @@ -953,9 +938,10 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } else { - material_t newNodeH = {}; + material_t newMaterialH = {}; if (_bsdf->isMeta()) { + const auto childCount = _bsdf->meta_common.childCount; switch(_bsdf->type) { case CElementBSDF::COATING: [[fallthrough]]; @@ -963,13 +949,21 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW { const auto coatingH = frontPool.emplace(); auto* const coating = frontPool.deref(coatingH); - // create BRDF thats cook torrance - // TODO - // BTDF thats transmission with absorption - // TODO - // attach leaf as layer + // the top layer + const auto& sigmaA = _bsdf->coating.sigmaA; + const bool hasExtinction = sigmaA.texture||sigmaA.value.type==SPropertyElementData::FLOAT&&sigmaA.value.fvalue!=0.f; + const auto beerH = hasExtinction ? frontPool.emplace():frontend_ir_t::typed_pointer_type{}; + if (auto* const beer=frontPool.deref(beerH); beer) + { + spectral_var_t::SCreationParams<3> params = {}; + params.knots.uvTransform = getParameters(params.knots.params,sigmaA); + params.getSemantics() = spectral_var_t::Semantics::Fixed3_SRGB; + beer->perpTransmittance = frontPool.emplace(std::move(params)); + } + fillCoatingLayer(coating,_bsdf->coating,_bsdf->type==CElementBSDF::ROUGHCOATING,beerH); + // attach the nested as layer coating->coated = localCache[_bsdf->mask.bsdf[0]]._const_cast(); - newNodeH = coatingH; + newMaterialH = coatingH; break; } case CElementBSDF::MASK: @@ -981,14 +975,15 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW const auto* const nested = frontPool.deref(nestedH); assert(nested && nested->brdfTop); const auto opacityH = createFactorNode(_bsdf->mask.opacity,ECommonDebug::Opacity); - mask->brdfTop = mul(nested->brdfTop,opacityH); + mask->brdfTop = frontIR->createMul(nested->brdfTop,opacityH); { - const auto transparencyH = frontPool.emplace(); - frontPool.deref(transparencyH)->child = opacityH; - mask->btdf = add(mul(deltaTransmission._const_cast(),transparencyH),mul(nested->btdf,opacityH)); + mask->btdf = frontIR->createAdd( + frontIR->createMul(deltaTransmission._const_cast(),frontIR->createComplement(opacityH)), + frontIR->createMul(nested->btdf,opacityH) + ); } - mask->brdfBottom = mul(nested->brdfBottom,opacityH); - newNodeH = maskH; + mask->brdfBottom = frontIR->createMul(nested->brdfBottom,opacityH); + newMaterialH = maskH; break; } case CElementBSDF::BUMPMAP: @@ -1013,40 +1008,33 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } case CElementBSDF::TWO_SIDED: { - const auto origFrontH = localCache[_bsdf->mask.bsdf[0]]; - const auto* const origFront = frontPool.deref(origFrontH); -#if 0 - // attach the original on both sides - if (_bsdf->twosided.childCount==1) + const auto origFrontH = localCache[_bsdf->twosided.bsdf[0]]; + const auto chosenBackH = childCount!=1 ? localCache[_bsdf->twosided.bsdf[1]]:origFrontH; + // Mitsuba does a mad thing where it will pick the BSDF to use based on NdotV which would normally break the required reciprocity of a BxDF + // but then it saves the day by disallowing transmission on the combination two BxDFs it layers together. Lets do the same. + if (const bool firstIsTransmissive=frontIR->transmissive(origFrontH); firstIsTransmissive && (childCount==1 || frontIR->transmissive(chosenBackH))) { - // get to the bottom layer - auto lastLayerH = origTopH; - while (lastLayerH) - { - const auto* const layer = frontPool.deref(lastLayerH); - lastLayerH = layer->coated; - } - // do we even have a non-twosided material? - // - - // create a copy of the layer, with different settings (don't want to disturb the original) - const auto twoSidedH = frontPool.emplace(); - auto* const twoSided = frontPool.deref(twoSidedH); - if (origTop->coated) - { - // - } - else - // - twoSided->brdfBottom = origTop->brdfTop; + logger.log("Mitsuba cannot be used to glue two transmissive BxDF Layer Stacks together!",LoggerError,debugName.c_str()); + break; } - else + // this is the stack we attach to the back, but we need to reverse it + const auto backH = frontIR->reverse(chosenBackH); + if (!backH) { - // glue a copy of the second child in reverse from its last layer to the front while reciprocating - const auto origBottomH = localCache[_bsdf->mask.bsdf[1]]; + logger.log("Failed to reverse and reciprocate the BxDF Layer Stack!",LoggerError,debugName.c_str()); + break; } -#endif - assert(false); // unimplemented + // we need to make a copy because we'll be changing the last layer + const auto combinedH = frontIR->copyLayers(origFrontH); + { + auto* lastInFront = frontPool.deref(combinedH); + // scroll to the end + while (lastInFront->coated) + lastInFront = frontPool.deref(lastInFront->coated); + // attach the back stack + lastInFront->coated = backH; + } + newMaterialH = combinedH; break; } default: @@ -1055,10 +1043,10 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } } else - newNodeH = createMistubaLeaf(_bsdf); - if (!newNodeH) - newNodeH = errorMaterial; - localCache[_bsdf] = newNodeH; + newMaterialH = createMistubaLeaf(_bsdf); + if (!newMaterialH) + newMaterialH = errorMaterial; + localCache[_bsdf] = newMaterialH; stack.pop_back(); } } diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp index c5a0631ff2..6a3dba7c41 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp @@ -244,113 +244,4 @@ CMitsubaMaterialCompilerFrontend::tex_ass_type CMitsubaMaterialCompilerFrontend: } return ir_node; - } - -auto CMitsubaMaterialCompilerFrontend::compileToIRTree(asset::material_compiler::IR* ir, const CElementBSDF* _bsdf, const system::logger_opt_ptr& logger) -> front_and_back_t -{ - using namespace asset; - using namespace material_compiler; - - struct SNode - { - const CElementBSDF* bsdf; - IRNode* ir_node = nullptr; - uint32_t parent_ix = static_cast(-1); // it skips twosided - uint32_t child_num = 0u; // which child am I ? - bool twosided = false; // any parent above twosided - bool front = true; - - CElementBSDF::Type type() const { return bsdf->type; } - }; - auto node_parent = [](const SNode& node, core::vector& traversal) { return &traversal[node.parent_ix]; }; - - core::vector bfs; // skips twosided - { - core::queue q; - { - SNode root{ _bsdf }; - root.twosided = (root.type() == CElementBSDF::TWO_SIDED); - q.push(root); - } - - while (q.size()) - { - SNode parent = q.front(); - q.pop(); - //node.ir_node = createIRNode(node.bsdf); - - if (parent.bsdf->isMeta()) - { - const uint32_t child_count = (parent.bsdf->type == CElementBSDF::COATING) ? parent.bsdf->coating.childCount : parent.bsdf->meta_common.childCount; - for (uint32_t i = 0u; i < child_count; ++i) - { - SNode child_node; - child_node.bsdf = (parent.bsdf->type == CElementBSDF::COATING) ? parent.bsdf->coating.bsdf[i] : parent.bsdf->meta_common.bsdf[i]; - // all of this is to traverse as-if twosided doesn't exist - child_node.parent_ix = parent.type() == CElementBSDF::TWO_SIDED ? parent.parent_ix : bfs.size(); - child_node.twosided = (child_node.type() == CElementBSDF::TWO_SIDED) || parent.twosided; - child_node.child_num = (parent.type() == CElementBSDF::TWO_SIDED) ? parent.child_num : i; - // inherit frontsidedness from parent - child_node.front = parent.front; - // second child of twosided is not frontsided - if (parent.type() == CElementBSDF::TWO_SIDED && i == 1u) - child_node.front = false; - q.push(child_node); - } - } - if (parent.type() != CElementBSDF::TWO_SIDED) - bfs.push_back(parent); - } - } - - - //create frontface IR - IRNode* frontroot = nullptr; - for (auto& node : bfs) - { - if (!node.front) - continue; - - IRNode** dst = nullptr; - if (node.parent_ix >= bfs.size()) - dst = &frontroot; - else - dst = const_cast(&node_parent(node, bfs)->ir_node->children[node.child_num]); - - node.ir_node = *dst = createIRNode(ir, node.bsdf, logger); - } - IRNode* backroot = nullptr; - for (uint32_t i = 0u; i < bfs.size(); ++i) - { - SNode& node = bfs[i]; - - IRNode* ir_node = nullptr; - if (!node.twosided) - ir_node = createBackfaceNodeFromFrontface(node.ir_node); - else - { - if (node.front) - { - if ((i+1u) < bfs.size() && bfs[i+1u].twosided && !bfs[i+1u].front) - continue; // will take backface node in next iteration - //otherwise copy the one from front (same bsdf on both sides_ - ir_node = ir->copyNode(node.ir_node); - } - else - ir_node = createIRNode(ir, node.bsdf, logger); - } - node.ir_node = ir_node; - - IRNode** dst = nullptr; - if (node.parent_ix >= bfs.size()) - dst = &backroot; - else - dst = const_cast(&node_parent(node, bfs)->ir_node->children[node.child_num]); - - *dst = ir_node; - } - - return { frontroot, backroot }; -} - -} + } \ No newline at end of file From 1b0b6f3f1204d08fed4b909118ca272c1a40dfef Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Tue, 17 Mar 2026 00:17:03 +0100 Subject: [PATCH 12/55] quick hack around normalmaps and bumpmaps, sketch out the general idea, won't implement right now --- .../asset/material_compiler3/CFrontendIR.h | 2 +- src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp | 99 ++++++++++++------- 2 files changed, 64 insertions(+), 37 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CFrontendIR.h b/include/nbl/asset/material_compiler3/CFrontendIR.h index 5c7c02a914..d139977e0a 100644 --- a/include/nbl/asset/material_compiler3/CFrontendIR.h +++ b/include/nbl/asset/material_compiler3/CFrontendIR.h @@ -737,7 +737,7 @@ class CFrontendIR final : public CNodePool // if roughness inputs are not equal (same scale, same texture) then NDF can be anisotropic in places if (getRougness()[0]!=getRougness()[1]) return false; - // if a reference stretch is used, stretched triangles can turn the distribution isotropic + // if a reference stretch is used, stretched triangles can turn the distribution anisotropic return stretchInvariant(); } // whether the derivative map and roughness is constant regardless of UV-space texture stretching diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index 985847af29..b79841c63a 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -466,7 +466,7 @@ parameter_t SContext::getTexture(const CElementTexture* const rootTex, hlsl::flo params.AnisotropicFilter = core::max(hlsl::findMSB(bitmap.maxAnisotropy),1u); // TODO: embed the gamma in the material compiler Frontend // or adjust gamma on pixels (painful and long process) - assert(std::isnan(bitmap.gamma)); + //assert(std::isnan(bitmap.gamma)); auto& transform = *outUvTransform; transform[0][0] = bitmap.uscale; transform[0][2] = bitmap.uoffset; @@ -617,6 +617,7 @@ auto SContext::genProfile(const CElementEmissionProfile* profile) -> frontend_ir return retval; } +// TODO: include source debug information / location, e.g. XML path, line and column in the nodes auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileWriter) -> frontend_material_t { auto& frontPool = frontIR->getObjectPool(); @@ -632,6 +633,12 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW frontPool.deref(factorH)->debugInfo = commonDebugNames[uint16_t(debug)]._const_cast(); return factorH; }; + + struct SDerivativeMap + { + // TODO: derivative map SParameter[2] + }; + // TODO: take `SParameter[2]` for the derivative maps auto createCookTorrance = [&](const CElementBSDF::RoughSpecularBase* base, const frontend_ir_t::typed_pointer_type fresnelH, const CElementTexture::SpectrumOrTexture& specularReflectance)->auto { const auto mulH = frontPool.emplace(); @@ -660,6 +667,7 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW // TODO: check if UV transform is the same and warn if not getParameters({roughness.data()+1,1},base->alphaV); } + // TODO: derivative maps } else roughness[0].scale = roughness[1].scale = 0.f; @@ -687,6 +695,7 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW // TODO: check if UV transform is the same and warn if not getParameters({roughness.data()+1,1},alphaV); } + // TODO: derivative maps } return frontIR->createMul(orenNayarH,factorH); }; @@ -699,7 +708,10 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW const auto transH = frontPool.emplace(); { auto* const trans = frontPool.deref(transH); - trans->lhs = frontIR->createMul(deltaTransmission._const_cast(),extinctionH); + if (extinctionH) + trans->lhs = frontIR->createMul(deltaTransmission._const_cast(),extinctionH); + else + trans->lhs = deltaTransmission._const_cast(); const auto factorH = createFactorNode(element.specularTransmittance,ECommonDebug::MitsubaExtraFactor); trans->rhs = frontIR->createMul(fresnelH,factorH); } @@ -708,10 +720,23 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW layer->brdfBottom = dielectricH; }; - using bxdf_t = frontend_ir_t::typed_pointer_type; + struct SEntry + { + inline bool operator==(const SEntry& other) const {return bsdf==other.bsdf;} + + const CElementBSDF* bsdf; + // SDerivativeMap derivMap; + }; + struct HashEntry + { + inline size_t operator()(const SEntry& entry) const {return std::hash()(entry.bsdf);} + }; + core::unordered_map localCache; + localCache.reserve(16); // the layer returned will never have a bottom BRDF - auto createMistubaLeaf = [&](const CElementBSDF* _bsdf/*TODO: debug source information*/)->frontend_ir_t::typed_pointer_type + auto createMistubaLeaf = [&](const SEntry& entry)->frontend_ir_t::typed_pointer_type { + const CElementBSDF* _bsdf = entry.bsdf; auto retval = frontPool.emplace(); auto* leaf = frontPool.deref(retval); switch (_bsdf->type) @@ -796,7 +821,7 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW { // want a specularTransmittance instead of SpecularReflectance factor const auto factorH = createFactorNode(_bsdf->dielectric.specularTransmittance,ECommonDebug::MitsubaExtraFactor); - bxdf_t btdfH; + frontend_ir_t::typed_pointer_type btdfH; { const auto* const brdf = frontPool.deref(brdfH); // make the trans node refraction-less for thin dielectric and apply thin scattering correction to the transmissive fresnel @@ -886,21 +911,18 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW // Post-order Depth First Traversal (create children first, then create parent) struct SStackEntry { - const CElementBSDF* bsdf; - uint64_t visited : 1 = false; - uint64_t unused : 63 = 0; + SEntry immutable; + bool visited = false; }; core::vector stack; stack.reserve(128); - stack.emplace_back() = {.bsdf=bsdf}; - // for the static casts of handles - core::unordered_map localCache; - localCache.reserve(16); + stack.emplace_back() = {.immutable={.bsdf=bsdf}}; // + frontend_ir_t::typed_pointer_type rootH = {}; while (!stack.empty()) { auto& entry = stack.back(); - const auto* const _bsdf = entry.bsdf; + const auto* const _bsdf = entry.immutable.bsdf; assert(_bsdf); // we only do post-dfs for non-leafs if (_bsdf->isMeta() && !entry.visited) @@ -912,9 +934,14 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW switch(_bsdf->type) { case CElementBSDF::COATING: [[fallthrough]]; - case CElementBSDF::ROUGHCOATING: [[fallthrough]]; + case CElementBSDF::ROUGHCOATING: + assert(meta_common.childCount==1); + break; case CElementBSDF::BUMPMAP: [[fallthrough]]; - case CElementBSDF::NORMALMAP: [[fallthrough]]; + case CElementBSDF::NORMALMAP: + assert(meta_common.childCount==1); + // TODO : create the derivative map and cache it + break; case CElementBSDF::MASK: assert(meta_common.childCount==1); break; @@ -930,9 +957,9 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW assert(false); // we shouldn't get this case here break; } - stack.emplace_back() = {.bsdf=meta_common.bsdf[0]}; - for (decltype(meta_common.childCount) i=1; iisMeta()) { const auto childCount = _bsdf->meta_common.childCount; + auto getChildFromCache = [&](const CElementBSDF* child)->frontend_ir_t::typed_pointer_type + { + return localCache[{.bsdf=child/*, TODO: copy the current normalmap stuff from entry or self if self is bump map*/}]._const_cast(); + }; switch(_bsdf->type) { case CElementBSDF::COATING: [[fallthrough]]; @@ -962,7 +993,7 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } fillCoatingLayer(coating,_bsdf->coating,_bsdf->type==CElementBSDF::ROUGHCOATING,beerH); // attach the nested as layer - coating->coated = localCache[_bsdf->mask.bsdf[0]]._const_cast(); + coating->coated = getChildFromCache(_bsdf->mask.bsdf[0]); newMaterialH = coatingH; break; } @@ -971,7 +1002,7 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW const auto maskH = frontPool.emplace(); auto* const mask = frontPool.deref(maskH); // - const auto nestedH = localCache[_bsdf->mask.bsdf[0]]; + const auto nestedH = getChildFromCache(_bsdf->mask.bsdf[0]); const auto* const nested = frontPool.deref(nestedH); assert(nested && nested->brdfTop); const auto opacityH = createFactorNode(_bsdf->mask.opacity,ECommonDebug::Opacity); @@ -985,15 +1016,12 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW mask->brdfBottom = frontIR->createMul(nested->brdfBottom,opacityH); newMaterialH = maskH; break; - } - case CElementBSDF::BUMPMAP: - { - assert(false); // unimplemented - break; - } + } + case CElementBSDF::BUMPMAP: [[fallthrough]]; case CElementBSDF::NORMALMAP: { - assert(false); // unimplemented + // we basically ignore and skip because derivative map already applied + newMaterialH = getChildFromCache(_bsdf->mask.bsdf[0]); break; } case CElementBSDF::MIXTURE_BSDF: @@ -1008,8 +1036,8 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } case CElementBSDF::TWO_SIDED: { - const auto origFrontH = localCache[_bsdf->twosided.bsdf[0]]; - const auto chosenBackH = childCount!=1 ? localCache[_bsdf->twosided.bsdf[1]]:origFrontH; + const auto origFrontH = getChildFromCache(_bsdf->twosided.bsdf[0]); + const auto chosenBackH = childCount!=1 ? getChildFromCache(_bsdf->twosided.bsdf[1]):origFrontH; // Mitsuba does a mad thing where it will pick the BSDF to use based on NdotV which would normally break the required reciprocity of a BxDF // but then it saves the day by disallowing transmission on the combination two BxDFs it layers together. Lets do the same. if (const bool firstIsTransmissive=frontIR->transmissive(origFrontH); firstIsTransmissive && (childCount==1 || frontIR->transmissive(chosenBackH))) @@ -1043,20 +1071,19 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } } else - newMaterialH = createMistubaLeaf(_bsdf); + newMaterialH = createMistubaLeaf(entry.immutable); if (!newMaterialH) newMaterialH = errorMaterial; - localCache[_bsdf] = newMaterialH; + localCache[entry.immutable] = newMaterialH; stack.pop_back(); + if (stack.empty()) + rootH = newMaterialH._const_cast(); } } - - const auto found = localCache.find(bsdf); - if (found!=localCache.end()) - return errorMaterial; - const auto rootH = found->second._const_cast(); if (!rootH) return errorMaterial; + + // add debug info auto* const root = frontPool.deref(rootH); root->debugInfo = frontPool.emplace(debugName); From 0c1e045f1c433b60115da250fe585d79fabf8362 Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Tue, 17 Mar 2026 00:48:36 +0100 Subject: [PATCH 13/55] move UV transform to front of SParameterSet, keep variable length stuff last --- .../nbl/asset/material_compiler3/CFrontendIR.h | 4 ++-- src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CFrontendIR.h b/include/nbl/asset/material_compiler3/CFrontendIR.h index d139977e0a..6248dd7ab7 100644 --- a/include/nbl/asset/material_compiler3/CFrontendIR.h +++ b/include/nbl/asset/material_compiler3/CFrontendIR.h @@ -131,7 +131,7 @@ class CFrontendIR final : public CNodePool float scale = std::numeric_limits::infinity(); // rest are ignored if the view is null uint8_t viewChannel : 2 = 0; - uint8_t padding[3] = {0,0,0}; + uint8_t padding[3] = {0,0,0}; // TODO: padding stores metadata, shall we exclude from assignment and copy operators? core::smart_refctd_ptr view = {}; // shadow comparison functions are ignored // NOTE: could take only things that matter from the sampler and pack the viewChannel and reduce padding @@ -189,13 +189,13 @@ class CFrontendIR final : public CNodePool printDot(Count,sstr,selfID,std::forward(paramNameBegin),uvRequired); } - SParameter params[Count] = {}; // identity transform by default, ignored if no UVs // NOTE: a transform could be applied per-param, whats important that the UV slot remains the smae across all of them. hlsl::float32_t2x3 uvTransform = hlsl::float32_t2x3( 1,0,0, 0,1,0 ); + SParameter params[Count] = {}; // to make sure there will be no padding inbetween static_assert(alignof(SParameter)>=alignof(hlsl::float32_t2x3)); diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index b79841c63a..538c0d0493 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -337,7 +337,7 @@ SAssetBundle CMitsubaLoader::loadAsset(system::IFile* _file, const IAssetLoader: } ctx.transferMetadata(); - return asset::SAssetBundle(std::move(result.metadata),{std::move(ctx.scene)}); + return SAssetBundle(std::move(result.metadata),{std::move(ctx.scene)}); } } @@ -503,8 +503,10 @@ hlsl::float32_t2x3 SContext::getParameters(const std::span out, c const auto param = getTexture(src.texture,&retval); for (auto c=0; c out, con { auto retval = hlsl::math::linalg::diagonal(0.f); if (src.texture) - std::fill(out.begin(),out.end(),getTexture(src.texture,&retval)); + { + const auto param = getTexture(src.texture,&retval); + for (auto c=0; c Date: Tue, 17 Mar 2026 01:22:27 +0100 Subject: [PATCH 14/55] implement BLEND_BSDF --- src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index 538c0d0493..dd2e2df74a 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -1042,7 +1042,19 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } case CElementBSDF::BLEND_BSDF: { - assert(false); // unimplemented + const auto blendH = frontPool.emplace(); + auto* const blend = frontPool.deref(blendH); + const auto tH = createFactorNode(_bsdf->blendbsdf.weight,ECommonDebug::Weight); + const auto tComplementH = frontIR->createComplement(tH); + const auto loH = getChildFromCache(_bsdf->blendbsdf.bsdf[0]); + const auto hiH = getChildFromCache(_bsdf->blendbsdf.bsdf[1]); + const auto* const lo = frontPool.deref(loH); + const auto* const hi = frontPool.deref(hiH); + // I don't actually need to check if the child Expressions are non-empty, the CFrontendIR utilities nicely carry through NOOPs + blend->brdfTop = frontIR->createAdd(frontIR->createMul(lo->brdfTop,tComplementH),frontIR->createMul(hi->brdfTop,tH)); + blend->btdf = frontIR->createAdd(frontIR->createMul(lo->btdf,tComplementH),frontIR->createMul(hi->btdf,tH)); + blend->brdfBottom = frontIR->createAdd(frontIR->createMul(lo->brdfBottom,tComplementH),frontIR->createMul(hi->brdfBottom,tH)); + newMaterialH = blendH; break; } case CElementBSDF::TWO_SIDED: From 011e7f06ff9ed0ad447e9e6df9c2ee82e17746c1 Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Tue, 17 Mar 2026 15:22:44 +0100 Subject: [PATCH 15/55] I forgot that for blends all layers need to be blended! --- .../asset/material_compiler3/CFrontendIR.h | 8 +++ include/nbl/ext/MitsubaLoader/SContext.h | 1 + src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp | 62 ++++++++++++++++--- .../CMitsubaMaterialCompilerFrontend.cpp | 34 +--------- 4 files changed, 63 insertions(+), 42 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CFrontendIR.h b/include/nbl/asset/material_compiler3/CFrontendIR.h index 6248dd7ab7..7d4c2cbb14 100644 --- a/include/nbl/asset/material_compiler3/CFrontendIR.h +++ b/include/nbl/asset/material_compiler3/CFrontendIR.h @@ -856,6 +856,14 @@ class CFrontendIR final : public CNodePool add->rhs = rhs; return addH; } + inline typed_pointer_type createFMA(const typed_pointer_type a, const typed_pointer_type b, const typed_pointer_type c) + { + return createAdd(createMul(a,b),c); + } + inline typed_pointer_type createWeightedSum(const typed_pointer_type x0, const typed_pointer_type w0, const typed_pointer_type x1, const typed_pointer_type w1) + { + return createAdd(createMul(x0,w0),createMul(x1,w1)); + } inline typed_pointer_type createComplement(const typed_pointer_type child) { if (!child) diff --git a/include/nbl/ext/MitsubaLoader/SContext.h b/include/nbl/ext/MitsubaLoader/SContext.h index cce42b3ca9..17b8f48bb3 100644 --- a/include/nbl/ext/MitsubaLoader/SContext.h +++ b/include/nbl/ext/MitsubaLoader/SContext.h @@ -87,6 +87,7 @@ struct SContext final #endif core::smart_refctd_ptr frontIR; // common frontend nodes + frontend_ir_t::typed_pointer_type unityFactor; frontend_ir_t::typed_pointer_type errorBRDF; frontend_ir_t::typed_pointer_type errorMaterial, unsupportedPhong, unsupportedWard; frontend_ir_t::typed_pointer_type deltaTransmission; diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index dd2e2df74a..a0919b6fb9 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -474,7 +474,7 @@ parameter_t SContext::getTexture(const CElementTexture* const rootTex, hlsl::flo transform[1][2] = bitmap.voffset; } else - inner.params.logger.log("Failed to load bitmap texture for %p with id %s",LoggerError,tex,tex ? tex->id.c_str():""); + inner.params.logger.log("Failed to load bitmap texture for %p with id %s from path \"%s\"",LoggerError,tex,tex ? tex->id.c_str():"",tex->bitmap.filename); } else inner.params.logger.log("Failed to unroll texture scale for %p with id %s",LoggerError,rootTex,rootTex ? rootTex->id.c_str():""); @@ -730,6 +730,31 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW // identical BRDF on the bottom, to have correct multiscatter layer->brdfBottom = dielectricH; }; + + using expr_handle_t = frontend_ir_t::typed_pointer_type; + auto createWeightedSum = [&](frontend_ir_t::CLayer* out, material_t A, const expr_handle_t w0, material_t B, const expr_handle_t w1)->void + { + while (true) + { + auto* const a = frontPool.deref(A); + auto* const b = frontPool.deref(B); + // I don't actually need to check if the child Expressions are non-empty, the CFrontendIR utilities nicely carry through NOOPs + out->brdfTop = frontIR->createWeightedSum(a ? a->brdfTop:expr_handle_t{},w0,b ? b->brdfTop:expr_handle_t{},w1); + out->btdf = frontIR->createWeightedSum(a ? a->btdf :expr_handle_t{},w0,b ? b->btdf:expr_handle_t{},w1); + out->brdfBottom = frontIR->createWeightedSum(a ? a->brdfBottom:expr_handle_t{},w0,b ? b->brdfBottom:expr_handle_t{},w1); + if (a && a->coated || b && b->coated) + { + out = frontPool.deref(out->coated = frontPool.emplace()); + A = a ? a->coated:material_t{}; + B = b ? b->coated:material_t{}; + } + else + { + out->coated = {}; + break; + } + } + }; struct SEntry { @@ -832,7 +857,7 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW { // want a specularTransmittance instead of SpecularReflectance factor const auto factorH = createFactorNode(_bsdf->dielectric.specularTransmittance,ECommonDebug::MitsubaExtraFactor); - frontend_ir_t::typed_pointer_type btdfH; + expr_handle_t btdfH; { const auto* const brdf = frontPool.deref(brdfH); // make the trans node refraction-less for thin dielectric and apply thin scattering correction to the transmissive fresnel @@ -1037,23 +1062,35 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW } case CElementBSDF::MIXTURE_BSDF: { - assert(false); // unimplemented + const auto mixH = frontPool.emplace(); + auto* const mix = frontPool.deref(mixH); + const uint8_t realChildCount = hlsl::min(childCount,_bsdf->mixturebsdf.weightCount); + const auto firstH = realChildCount>=1 ? getChildFromCache(_bsdf->mixturebsdf.bsdf[0]):frontend_ir_t::typed_pointer_type{}; + spectral_var_t::SCreationParams<1> firstParams = {}; + firstParams.knots.params[0].scale = _bsdf->mixturebsdf.weights[0]; + const auto firstWeightH = frontPool.emplace(std::move(firstParams)); + if (realChildCount<2) + createWeightedSum(mix,firstH,firstWeightH,{},{}); + else + for (uint8_t c=1; cmixturebsdf.bsdf[c]); + spectral_var_t::SCreationParams<1> params = {}; + params.knots.params[0].scale = _bsdf->mixturebsdf.weights[c]; + // don't feel like spending time on this, since its never used, trust CTrueIR optimizer to remove this during canonicalization + createWeightedSum(mix,mixH,unityFactor._const_cast(),otherH,frontPool.emplace(std::move(params))); + } + newMaterialH = mixH; break; } case CElementBSDF::BLEND_BSDF: { const auto blendH = frontPool.emplace(); - auto* const blend = frontPool.deref(blendH); const auto tH = createFactorNode(_bsdf->blendbsdf.weight,ECommonDebug::Weight); const auto tComplementH = frontIR->createComplement(tH); const auto loH = getChildFromCache(_bsdf->blendbsdf.bsdf[0]); const auto hiH = getChildFromCache(_bsdf->blendbsdf.bsdf[1]); - const auto* const lo = frontPool.deref(loH); - const auto* const hi = frontPool.deref(hiH); - // I don't actually need to check if the child Expressions are non-empty, the CFrontendIR utilities nicely carry through NOOPs - blend->brdfTop = frontIR->createAdd(frontIR->createMul(lo->brdfTop,tComplementH),frontIR->createMul(hi->brdfTop,tH)); - blend->btdf = frontIR->createAdd(frontIR->createMul(lo->btdf,tComplementH),frontIR->createMul(hi->btdf,tH)); - blend->brdfBottom = frontIR->createAdd(frontIR->createMul(lo->brdfBottom,tComplementH),frontIR->createMul(hi->brdfBottom,tH)); + createWeightedSum(frontPool.deref(blendH),loH,tComplementH,hiH,tH); newMaterialH = blendH; break; } @@ -1239,6 +1276,11 @@ SContext::SContext( } // { + { + spectral_var_t::SCreationParams<1> params = {}; + params.knots.params[0].scale = 1.f; + unityFactor = frontPool.emplace(std::move(params)); + } deltaTransmission = frontPool.emplace(); const auto mulH = frontPool.emplace(); { diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp index 6a3dba7c41..7452e51862 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaMaterialCompilerFrontend.cpp @@ -149,31 +149,6 @@ CMitsubaMaterialCompilerFrontend::tex_ass_type CMitsubaMaterialCompilerFrontend: } #endif - auto CMitsubaMaterialCompilerFrontend::createIRNode(asset::material_compiler::IR* ir, const CElementBSDF* _bsdf, const system::logger_opt_ptr& logger) -> IRNode* - { - using namespace asset; - using namespace material_compiler; - - - - const auto type = _bsdf->type; - IRNode* ir_node = nullptr; - switch (type) - { - case CElementBSDF::TWO_SIDED: - //TWO_SIDED is not translated into IR node directly - break; - case CElementBSDF::MASK: - ir_node = ir->allocNode(); - ir_node->children.count = 1u; - getSpectrumOrTexture(_bsdf->mask.opacity,static_cast(ir_node)->opacity,EIVS_BLEND_WEIGHT); - break; - - - - - - case CElementBSDF::BUMPMAP: @@ -223,13 +198,8 @@ CMitsubaMaterialCompilerFrontend::tex_ass_type CMitsubaMaterialCompilerFrontend: } } break; - case CElementBSDF::BLEND_BSDF: - { - ir_node = ir->allocNode(); - ir_node->children.count = 2u; - getSpectrumOrTexture(_bsdf->blendbsdf.weight,static_cast(ir_node)->weight,EIVS_BLEND_WEIGHT); - } - break; + + case CElementBSDF::MIXTURE_BSDF: { ir_node = ir->allocNode(); From 2b340472b9b831f78f64e0cff827c9dd49e63433 Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Tue, 17 Mar 2026 22:47:12 +0100 Subject: [PATCH 16/55] I don't want to admit how long it took me to debug this one --- src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp | 112 +++++++++++------- .../CMitsubaMaterialCompilerFrontend.cpp | 53 +-------- 2 files changed, 67 insertions(+), 98 deletions(-) diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index a0919b6fb9..b32c24eb2e 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -419,59 +419,69 @@ parameter_t SContext::getTexture(const CElementTexture* const rootTex, hlsl::flo SAssetBundle viewBundle = interm_getImageViewInHierarchy(tex->bitmap.filename,/*ICPUScene::MATERIAL_IMAGES_HIERARCHY_LEVELS_BELOW*/1); if (auto contents=viewBundle.getContents(); !contents.empty()) { - retval.view = IAsset::castDown(*contents.begin()); + auto view = IAsset::castDown(*contents.begin()); + const auto format = view->getCreationParameters().format; if (bitmap.channel!=CElementTexture::Bitmap::CHANNEL::INVALID) retval.viewChannel = bitmap.channel-CElementTexture::Bitmap::CHANNEL::R; - // get sampler parameters - using tex_clamp_e = asset::ISampler::E_TEXTURE_CLAMP; - auto getWrapMode = [](CElementTexture::Bitmap::WRAP_MODE mode) + if (retval.viewChannel(bitmap.maxAnisotropy),1u); + // TODO: embed the gamma in the material compiler Frontend + // or adjust gamma on pixels (painful and long process) + //assert(std::isnan(bitmap.gamma)); + auto& transform = *outUvTransform; + transform[0][0] = bitmap.uscale; + transform[0][2] = bitmap.uoffset; + transform[1][1] = bitmap.vscale; + transform[1][2] = bitmap.voffset; } - params.AnisotropicFilter = core::max(hlsl::findMSB(bitmap.maxAnisotropy),1u); - // TODO: embed the gamma in the material compiler Frontend - // or adjust gamma on pixels (painful and long process) - //assert(std::isnan(bitmap.gamma)); - auto& transform = *outUvTransform; - transform[0][0] = bitmap.uscale; - transform[0][2] = bitmap.uoffset; - transform[1][1] = bitmap.vscale; - transform[1][2] = bitmap.voffset; + else + inner.params.logger.log( + "Failed to parse CElementTexture bitmap for %p with id %s from path \"%s\", channel swizzle %s requested out of range of format %s", + LoggerError,tex,tex ? tex->id.c_str():"",tex->bitmap.filename,system::to_string(bitmap.channel),system::to_string(format) + ); } else inner.params.logger.log("Failed to load bitmap texture for %p with id %s from path \"%s\"",LoggerError,tex,tex ? tex->id.c_str():"",tex->bitmap.filename); @@ -501,10 +511,19 @@ hlsl::float32_t2x3 SContext::getParameters(const std::span out, c if (src.texture) { const auto param = getTexture(src.texture,&retval); + const auto formatChannelCount = getFormatChannelCount(param.view ? param.view->getCreationParameters().format:E_FORMAT::EF_UNKNOWN); + if (src.texture->bitmap.channel==CElementTexture::Bitmap::CHANNEL::INVALID && formatChannelCount>1 && out.size()>formatChannelCount) + { + for (auto c=0; c::signaling_NaN(); + return hlsl::math::linalg::diagonal(0.f);; + } for (auto c=0; c1 && src.texture->bitmap.channel==CElementTexture::Bitmap::CHANNEL::INVALID) + out[c].viewChannel += c; out[c].view = param.view; out[c].sampler = param.sampler; } @@ -535,7 +554,8 @@ hlsl::float32_t2x3 SContext::getParameters(const std::span out, con for (auto c=0; ctexture.image,node->texture.sampler,node->texture.scale) = getTexture(_bsdf->bumpmap.texture,_bsdf->bumpmap.wasNormal ? EIVS_NORMAL_MAP:EIVS_BUMP_MAP); } - break; - case CElementBSDF::COATING: - case CElementBSDF::ROUGHCOATING: - { - ir_node = ir->allocNode(); - ir_node->children.count = 1u; - - const float eta = _bsdf->dielectric.intIOR/_bsdf->dielectric.extIOR; - - auto* node = static_cast(ir_node); - - const float thickness = _bsdf->coating.thickness; - getSpectrumOrTexture(_bsdf->coating.sigmaA, node->thicknessSigmaA); - if (node->thicknessSigmaA.isConstant()) - node->thicknessSigmaA.constant *= thickness; - else - node->thicknessSigmaA.texture.scale *= thickness; - - node->eta = IR::INode::color_t(eta); - node->shadowing = IR::CMicrofacetCoatingBSDFNode::EST_SMITH; - if (type == CElementBSDF::ROUGHCOATING) - { - node->ndf = ndfMap[_bsdf->coating.distribution]; - getFloatOrTexture(_bsdf->coating.alphaU, node->alpha_u); - if (node->ndf == IR::CMicrofacetSpecularBSDFNode::ENDF_ASHIKHMIN_SHIRLEY) - getFloatOrTexture(_bsdf->coating.alphaV, node->alpha_v); - else - node->alpha_v = node->alpha_u; - } - else - { - node->setSmooth(); - } - } - break; - - - case CElementBSDF::MIXTURE_BSDF: - { - ir_node = ir->allocNode(); - auto* node = static_cast(ir_node); - const size_t cnt = _bsdf->mixturebsdf.childCount; - ir_node->children.count = cnt; - const auto* weightIt = _bsdf->mixturebsdf.weights; - for (size_t i=0u; iweights[i] = *(weightIt++); - } - break; - } - - return ir_node; - } \ No newline at end of file + break; \ No newline at end of file From 2d3719f07e3db9e63f55f44bc6da0d32245e601c Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Wed, 18 Mar 2026 01:10:01 +0100 Subject: [PATCH 17/55] fix the last thing in the material frontend --- examples_tests | 2 +- .../nbl/asset/material_compiler3/CFrontendIR.h | 18 ++++++++++++++---- .../asset/material_compiler3/CFrontendIR.cpp | 2 +- src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp | 5 +++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/examples_tests b/examples_tests index 6b7e164ccb..9e5e1b4981 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 6b7e164ccb57697dfe4ce78097a8b70b10ba6f4d +Subproject commit 9e5e1b49816afa034ee3cce41e8429fa152ebaa9 diff --git a/include/nbl/asset/material_compiler3/CFrontendIR.h b/include/nbl/asset/material_compiler3/CFrontendIR.h index 7d4c2cbb14..4269af5d6f 100644 --- a/include/nbl/asset/material_compiler3/CFrontendIR.h +++ b/include/nbl/asset/material_compiler3/CFrontendIR.h @@ -650,7 +650,7 @@ class CFrontendIR final : public CNodePool // // We actually allow you to use different reflectance nodes R_u and R_b, the NDFs of both layers remain the same but Reflectance Functions differ. // Note that e.g. using different Etas for the Fresnel used for the top and bottom reflectance will result in a compound Fresnel!=1.0 - // meaning that in such case you can no longer optimize the BTDF contributor into a DeltaTransmission but need a CookTorrance with + // meaning that in such case you can no longer optimize the BTDF contributor into a DeltaTransmission but need a zero-roughness CookTorrance with // an Eta equal to the ratio of the first Eta over the second Eta (note that when they're equal the ratio is 1 which turns into Delta Trans). // This will require you to make an AST that "seems wrong" that is where neither of the Etas of the CFresnel nodes match the Cook Torrance one. // @@ -755,7 +755,16 @@ class CFrontendIR final : public CNodePool // - Delta Reflection -> Any Cook Torrance BxDF with roughness=0 attached as BRDF // - Smooth Conductor -> above multiplied with Conductor-Fresnel // - Smooth Dielectric -> Any Cook Torrance BxDF with roughness=0 attached as BRDF on both sides of a Layer and BTDF multiplied with Dielectric-Fresnel (no imaginary component) - // - Thindielectric Correlated -> Any Cook Torrance BxDF multiplied with Dielectric-Fresnel, then Delta Transmission as BTDF with fresnels + // - Thindielectric Correlated -> Cook Torrance BxDF multiplied with Dielectric-Fresnel as top BRDF and its reciprocal as the bottom, then Delta Transmission as BTDF with fresnels of similar Eta + // - Thindielectric Uncorrelated -> BRDF and BTDF same as above, no bottom BRDF, then another layer with delta transmission BTDF + // For Smooth dielectrics it makes sense because fresnel of the interface is the same (microfacet equals macro surface normal, no confusion) + // For Rough its a little more complicated, but using the same BTDF still makes sense. + // Why? Because you enter all microfacets at once with a ray packet, and because their backfaces are correlated you don't refract. + // If we then assume that they're quite big in relation to the thickness, most of the Total Internal Reflection stays within the same microfacet slab. + // So for a single microfacet we have the thindielectric infinite TIR equation with `R_u = (1-Fresnel(VdotH))` and `R_b = (1-Fresnel(-LdotH))`, + // which when convolved with the VNDF (integral of complete TIR equation over all H) can be approximated by substitution of `...dotH` with `...dotN`. + // It also wouldn't matter if we dictate each slab have uniform perpendicular or geometric normal thickness, as the VNDF keeps projected surface area proportional to microfacet angle. + // So the average VdotH or LdotH are equal to NdotV and NdotL respectively, which doesn't guarantee average `inversesqrt(1-VdotH*VdotH)` equals `inversesqrt(1-NdotV*NdotV)` but difference is small. // - Plastic -> Similar to layering the above over Diffuse BRDF, its of uttmost importance that the BTDF is Delta Transmission. class CDeltaTransmission final : public IBxDF { @@ -912,9 +921,10 @@ class CFrontendIR final : public CNodePool // Some things we can't check such as the compatibility of the BTDF with the BRDF (matching indices of refraction, etc.) NBL_API2 bool valid(const typed_pointer_type rootHandle, system::logger_opt_ptr logger) const; - // Each material comes down to this, YOU MUST NOT MODIFY THE NODES AFTER ADDING THEIR PARENT TO THE ROOT NODES! - // TODO: shall we copy and hand out a new handle? inline std::span> getMaterials() {return m_rootNodes;} + + // Each material comes down to this, YOU MUST NOT MODIFY THE NODES AFTER ADDING THEIR PARENT TO THE ROOT NODES! + // TODO: shall we copy and hand out a new handle? Allow RootNode from a foreign const pool inline bool addMaterial(const typed_pointer_type rootNode, system::logger_opt_ptr logger) { if (valid(rootNode,logger)) diff --git a/src/nbl/asset/material_compiler3/CFrontendIR.cpp b/src/nbl/asset/material_compiler3/CFrontendIR.cpp index ce02e46518..d77ac287da 100644 --- a/src/nbl/asset/material_compiler3/CFrontendIR.cpp +++ b/src/nbl/asset/material_compiler3/CFrontendIR.cpp @@ -32,7 +32,7 @@ bool CFrontendIR::CBeer::invalid(const SInvalidCheckArgs& args) const args.logger.log("Perpendicular Transparency node of correct type must be attached, but is %u of type %s",ELL_ERROR,perpTransmittance,args.pool->getTypeName(perpTransmittance).data()); return true; } - if (const auto* const thick=args.pool->getObjectPool().deref(thickness); !thick || thick->getKnotCount()!=0) + if (const auto* const thick=args.pool->getObjectPool().deref(thickness); !thick || thick->getKnotCount()!=1) { args.logger.log("Monochromatic Thickness node must be attached, but is %u of type %s",ELL_ERROR,thickness,args.pool->getTypeName(thickness).data()); return true; diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index b32c24eb2e..51c25dc325 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -897,9 +897,14 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW mul->lhs = deltaTransmission._const_cast(); mul->rhs = thinInfiniteScatterH; } +#ifdef UNCORRELLATED // We need to attach the other glass interface as another layer because the Etas need to be reciprocated because the interactions are flipped leaf->coated = frontIR->reverse(retval); + frontPool.deref(leaf->coated)->btdf = deltaTransmission._const_cast(); // we're rethreading the fresnel here, but needed to be careful to not apply thindielectric scatter correction and volumetric absorption / Mitsuba extra factor twice +#else + leaf->brdfBottom = frontIR->reciprocate(brdfH)._const_cast(); +#endif btdfH = correctedTransmissionH; } else From ad0cf8dfc48bb50dbaeb1ab291c8188b1aabf848 Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Wed, 18 Mar 2026 13:53:13 +0100 Subject: [PATCH 18/55] fix TLAS export when Polygon Geometry can't be made into a BLAS --- examples_tests | 2 +- include/nbl/asset/ICPUScene.h | 2 +- src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples_tests b/examples_tests index 9e5e1b4981..d16ccb5dcf 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 9e5e1b49816afa034ee3cce41e8429fa152ebaa9 +Subproject commit d16ccb5dcfbefc37f94d37c2802af25e4044e002 diff --git a/include/nbl/asset/ICPUScene.h b/include/nbl/asset/ICPUScene.h index 62d8d2e9a3..3a3ab469a3 100644 --- a/include/nbl/asset/ICPUScene.h +++ b/include/nbl/asset/ICPUScene.h @@ -276,7 +276,7 @@ class ICPUScene final : public IAsset, public IScene instances.emplace_back().instance = std::move(inst); } // TODO: adjust BLAS geometry flags according to materials set opaqueness and NO_DUPLICATE_ANY_HIT_INVOCATION_BIT - SResult retval = {.instances=core::make_refctd_dynamic_array(instanceCount),.allInstancesValid=allInstancesValid}; + SResult retval = {.instances=core::make_refctd_dynamic_array(instances.size()),.allInstancesValid=allInstancesValid}; std::move(instances.begin(),instances.end(),retval.instances->begin()); return retval; } diff --git a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp index 51c25dc325..bd90b15887 100644 --- a/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp +++ b/src/nbl/ext/MitsubaLoader/CMitsubaLoader.cpp @@ -164,7 +164,7 @@ bool CMitsubaLoader::isALoadableFileFormat(system::IFile* _file, const system::l // TODO: make configurable -constexpr bool PrintMaterialDot3 = true; +constexpr bool PrintMaterialDot3 = false; system::path DebugDir("D:\\work\\Nabla-master\\examples_tests\\15_MitsubaLoader\\bin"); // void SContext::writeDot3File(system::ISystem* system, const system::path& filepath, frontend_ir_t::SDotPrinter& printer) From 5d2568bc0edca8de7123a5d3af406ec8defc5a92 Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Wed, 18 Mar 2026 13:56:18 +0100 Subject: [PATCH 19/55] merge master --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index d16ccb5dcf..8f045a1c27 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit d16ccb5dcfbefc37f94d37c2802af25e4044e002 +Subproject commit 8f045a1c27a198f8542456378f865032765378b8 From c86f8e1dc55e9eafaff160effab319af94142f6e Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Thu, 19 Mar 2026 13:59:57 +0100 Subject: [PATCH 20/55] Fix Wave include paths and failure dumps --- include/nbl/asset/utils/IShaderCompiler.h | 1 + src/nbl/asset/utils/CWaveStringResolver.cpp | 200 +++++++++++++++++--- src/nbl/asset/utils/waveContext.h | 61 +++++- tools/nsc/main.cpp | 101 ++++++---- 4 files changed, 300 insertions(+), 63 deletions(-) diff --git a/include/nbl/asset/utils/IShaderCompiler.h b/include/nbl/asset/utils/IShaderCompiler.h index 0c24c2b1d0..552d786c80 100644 --- a/include/nbl/asset/utils/IShaderCompiler.h +++ b/include/nbl/asset/utils/IShaderCompiler.h @@ -136,6 +136,7 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted E_SPIRV_VERSION targetSpirvVersion = E_SPIRV_VERSION::ESV_1_6; bool depfile = false; system::path depfilePath = {}; + std::string* partialOutputOnFailure = nullptr; }; // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#debugging diff --git a/src/nbl/asset/utils/CWaveStringResolver.cpp b/src/nbl/asset/utils/CWaveStringResolver.cpp index aae872668f..cfd329fdd4 100644 --- a/src/nbl/asset/utils/CWaveStringResolver.cpp +++ b/src/nbl/asset/utils/CWaveStringResolver.cpp @@ -49,6 +49,22 @@ using namespace nbl::asset; namespace { +constexpr size_t kWavePartialOutputTailMaxChars = 4096ull; +constexpr size_t kWavePartialOutputTailMaxLines = 16ull; +constexpr size_t kWaveTokenPreviewMaxChars = 160ull; + +struct WaveRenderProgress +{ + core::string output; + std::optional previousPosition = std::nullopt; + bool previousWasExplicitWhitespace = false; + size_t emittedTokenCount = 0ull; + std::string lastTokenFile; + int lastTokenLine = 0; + int lastTokenColumn = 0; + std::string lastTokenValue; +}; + std::string getLineSnippet(std::string_view text, const int lineNo) { if (lineNo <= 0) @@ -85,8 +101,100 @@ std::string makeCaretLine(const int columnNo) return std::string(static_cast(columnNo - 1), ' ') + '^'; } +size_t countLogicalLines(const std::string_view text) +{ + if (text.empty()) + return 0ull; + + size_t lines = 0ull; + for (const auto ch : text) + { + if (ch == '\n') + ++lines; + } + + if (text.back() != '\n') + ++lines; + return lines; +} + +std::string truncateEscapedPreview(std::string value, const size_t maxChars) +{ + if (value.size() <= maxChars) + return value; + + if (maxChars <= 3ull) + return value.substr(0ull, maxChars); + + value.resize(maxChars - 3ull); + value += "..."; + return value; +} + +std::string indentMultiline(std::string_view text, std::string_view indent) +{ + if (text.empty()) + return {}; + + std::string out; + out.reserve(text.size() + indent.size() * 4ull); + + size_t lineStart = 0ull; + while (lineStart < text.size()) + { + out.append(indent.data(), indent.size()); + + const auto lineEnd = text.find('\n', lineStart); + if (lineEnd == std::string_view::npos) + { + out.append(text.data() + lineStart, text.size() - lineStart); + break; + } + + out.append(text.data() + lineStart, lineEnd - lineStart + 1ull); + lineStart = lineEnd + 1ull; + } + + return out; +} + +std::string makeOutputTail(std::string_view text) +{ + if (text.empty()) + return {}; + + size_t start = text.size(); + size_t chars = 0ull; + size_t newlines = 0ull; + while (start > 0ull) + { + --start; + ++chars; + if (text[start] == '\n') + { + ++newlines; + if (newlines > kWavePartialOutputTailMaxLines) + { + ++start; + break; + } + } + + if (chars >= kWavePartialOutputTailMaxChars) + break; + } + + std::string tail; + if (start > 0ull) + tail = "[truncated]\n"; + tail.append(text.data() + start, text.size() - start); + return tail; +} + std::string makeWaveFailureContext( const nbl::asset::IShaderCompiler::SPreprocessorOptions& preprocessOptions, + const nbl::wave::context& context, + const WaveRenderProgress& renderProgress, const std::string_view code, const char* const phase, const std::string_view activeMacroDefinition, @@ -97,14 +205,28 @@ std::string makeWaveFailureContext( std::ostringstream stream; stream << "Wave preprocessing context:"; if (!preprocessOptions.sourceIdentifier.empty()) - stream << "\n source: " << preprocessOptions.sourceIdentifier; + stream << "\n source: " << nbl::wave::detail::escape_control_chars(preprocessOptions.sourceIdentifier); stream << "\n phase: " << phase; stream << "\n extra_define_count: " << preprocessOptions.extraDefines.size(); + stream << "\n source_length_bytes: " << code.size(); stream << "\n source_has_trailing_newline: " << ((!code.empty() && code.back() == '\n') ? "yes" : "no"); + stream << "\n include_depth: " << context.get_iteration_depth(); + stream << "\n current_include_spelling: " << nbl::wave::detail::escape_control_chars(context.get_current_relative_filename()); + stream << "\n current_directory: " << nbl::wave::detail::escape_control_chars(context.get_current_directory().string()); +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + stream << "\n current_include_absolute_path: " << nbl::wave::detail::escape_control_chars(context.get_current_filename()); +#endif if (!activeMacroDefinition.empty()) - stream << "\n active_macro_definition: " << activeMacroDefinition; + stream << "\n active_macro_definition: " << nbl::wave::detail::escape_control_chars(activeMacroDefinition); if (fileName && fileName[0] != '\0') - stream << "\n location: " << fileName << ':' << lineNo << ':' << columnNo; + stream << "\n location: " << nbl::wave::detail::escape_control_chars(fileName) << ':' << lineNo << ':' << columnNo; + stream << "\n emitted_output_bytes: " << renderProgress.output.size(); + stream << "\n emitted_output_lines: " << countLogicalLines(renderProgress.output); + stream << "\n emitted_token_count: " << renderProgress.emittedTokenCount; + if (!renderProgress.lastTokenFile.empty()) + stream << "\n last_emitted_token_location: " << nbl::wave::detail::escape_control_chars(renderProgress.lastTokenFile) << ':' << renderProgress.lastTokenLine << ':' << renderProgress.lastTokenColumn; + if (!renderProgress.lastTokenValue.empty()) + stream << "\n last_emitted_token_value: " << truncateEscapedPreview(nbl::wave::detail::escape_control_chars(renderProgress.lastTokenValue), kWaveTokenPreviewMaxChars); const auto snippet = getLineSnippet(code, lineNo); if (!snippet.empty() && fileName && preprocessOptions.sourceIdentifier == fileName) @@ -115,6 +237,10 @@ std::string makeWaveFailureContext( stream << "\n " << caret; } + const auto outputTail = makeOutputTail(renderProgress.output); + if (!outputTail.empty()) + stream << "\n partial_output_tail:\n" << indentMultiline(outputTail, " "); + return stream.str(); } @@ -134,14 +260,11 @@ std::string tokenValueToString(const TokenT& token) return std::string(value.data(), value.size()); } -core::string renderPreprocessedOutput(nbl::wave::context& context) +void renderPreprocessedOutput(nbl::wave::context& context, WaveRenderProgress& renderProgress) { using namespace boost::wave; - core::string output; util::insert_whitespace_detection whitespace(true); - std::optional previousPosition = std::nullopt; - bool previousWasExplicitWhitespace = false; for (auto it = context.begin(); it != context.end(); ++it) { @@ -154,37 +277,41 @@ core::string renderPreprocessedOutput(nbl::wave::context& context) const auto& position = token.get_position(); const auto value = tokenValueToString(token); - if (previousPosition.has_value() && !explicitWhitespace) + if (renderProgress.previousPosition.has_value() && !explicitWhitespace) { const auto movedToNewLogicalLine = - position.get_file() != previousPosition->get_file() || - position.get_line() > previousPosition->get_line(); + position.get_file() != renderProgress.previousPosition->get_file() || + position.get_line() > renderProgress.previousPosition->get_line(); if (movedToNewLogicalLine) { - if (output.empty() || output.back() != '\n') + if (renderProgress.output.empty() || renderProgress.output.back() != '\n') { - output.push_back('\n'); + renderProgress.output.push_back('\n'); whitespace.shift_tokens(T_NEWLINE); } } - else if (!previousWasExplicitWhitespace && whitespace.must_insert(id, value)) + else if (!renderProgress.previousWasExplicitWhitespace && whitespace.must_insert(id, value)) { - if (output.empty() || (output.back() != ' ' && output.back() != '\n' && output.back() != '\r' && output.back() != '\t')) + if (renderProgress.output.empty() || (renderProgress.output.back() != ' ' && renderProgress.output.back() != '\n' && renderProgress.output.back() != '\r' && renderProgress.output.back() != '\t')) { - output.push_back(' '); + renderProgress.output.push_back(' '); whitespace.shift_tokens(T_SPACE); } } } - output += value; + renderProgress.output += value; whitespace.shift_tokens(id); - previousPosition = position; - previousWasExplicitWhitespace = explicitWhitespace; + renderProgress.previousPosition = position; + renderProgress.previousWasExplicitWhitespace = explicitWhitespace; + const auto& file = position.get_file(); + renderProgress.lastTokenFile.assign(file.c_str(), file.size()); + renderProgress.lastTokenLine = position.get_line(); + renderProgress.lastTokenColumn = position.get_column(); + renderProgress.lastTokenValue = value; + ++renderProgress.emittedTokenCount; } - - return output; } std::string preprocessImpl( @@ -199,9 +326,20 @@ std::string preprocessImpl( context.add_macro_definition("__SPIRV_MAJOR_VERSION__=" + std::to_string(IShaderCompiler::getSpirvMajor(preprocessOptions.targetSpirvVersion))); context.add_macro_definition("__SPIRV_MINOR_VERSION__=" + std::to_string(IShaderCompiler::getSpirvMinor(preprocessOptions.targetSpirvVersion))); - core::string resolvedString; + WaveRenderProgress renderProgress; + if (preprocessOptions.partialOutputOnFailure) + preprocessOptions.partialOutputOnFailure->clear(); const char* phase = "registering built-in macros"; std::string activeMacroDefinition; + const auto storePartialOutputOnFailure = [&]() + { + if (preprocessOptions.partialOutputOnFailure) + *preprocessOptions.partialOutputOnFailure = renderProgress.output; + }; + const auto makeFailureContext = [&](const char* const fileName, const int lineNo, const int columnNo) + { + return makeWaveFailureContext(preprocessOptions, context, renderProgress, code, phase, activeMacroDefinition, fileName, lineNo, columnNo); + }; try { phase = "registering extra macro definitions"; @@ -215,31 +353,37 @@ std::string preprocessImpl( activeMacroDefinition.clear(); phase = "expanding translation unit"; - resolvedString = renderPreprocessedOutput(context); + renderPreprocessedOutput(context, renderProgress); } catch (boost::wave::preprocess_exception& e) { - const auto failureContext = makeWaveFailureContext(preprocessOptions, code, phase, activeMacroDefinition, e.file_name(), e.line_no(), e.column_no()); - preprocessOptions.logger.log("%s exception caught. %s [%s:%d:%d]\n%s", system::ILogger::ELL_ERROR, e.what(), e.description(), e.file_name(), e.line_no(), e.column_no(), failureContext.c_str()); + storePartialOutputOnFailure(); + const auto escapedDescription = nbl::wave::detail::escape_control_chars(e.description()); + const auto escapedFileName = nbl::wave::detail::escape_control_chars(e.file_name()); + const auto failureContext = makeFailureContext(e.file_name(), e.line_no(), e.column_no()); + preprocessOptions.logger.log("%s exception caught. %s [%s:%d:%d]\n%s", system::ILogger::ELL_ERROR, e.what(), escapedDescription.c_str(), escapedFileName.c_str(), e.line_no(), e.column_no(), failureContext.c_str()); preprocessOptions.logger.log("Boost diagnostic information:\n%s", system::ILogger::ELL_ERROR, boost::diagnostic_information(e).c_str()); return {}; } catch (const boost::exception& e) { - const auto failureContext = makeWaveFailureContext(preprocessOptions, code, phase, activeMacroDefinition, preprocessOptions.sourceIdentifier.data(), 0, 0); + storePartialOutputOnFailure(); + const auto failureContext = makeFailureContext(preprocessOptions.sourceIdentifier.data(), 0, 0); preprocessOptions.logger.log("Boost exception caught during Wave preprocessing.\n%s", system::ILogger::ELL_ERROR, failureContext.c_str()); preprocessOptions.logger.log("Boost diagnostic information:\n%s", system::ILogger::ELL_ERROR, boost::diagnostic_information(e).c_str()); return {}; } catch (const std::exception& e) { - const auto failureContext = makeWaveFailureContext(preprocessOptions, code, phase, activeMacroDefinition, preprocessOptions.sourceIdentifier.data(), 0, 0); + storePartialOutputOnFailure(); + const auto failureContext = makeFailureContext(preprocessOptions.sourceIdentifier.data(), 0, 0); preprocessOptions.logger.log("std::exception caught during Wave preprocessing. %s\n%s", system::ILogger::ELL_ERROR, e.what(), failureContext.c_str()); return {}; } catch (...) { - const auto failureContext = makeWaveFailureContext(preprocessOptions, code, phase, activeMacroDefinition, preprocessOptions.sourceIdentifier.data(), 0, 0); + storePartialOutputOnFailure(); + const auto failureContext = makeFailureContext(preprocessOptions.sourceIdentifier.data(), 0, 0); preprocessOptions.logger.log("Unknown exception caught during Wave preprocessing.\n%s", system::ILogger::ELL_ERROR, failureContext.c_str()); preprocessOptions.logger.log("Current exception diagnostic information:\n%s", system::ILogger::ELL_ERROR, boost::current_exception_diagnostic_information().c_str()); return {}; @@ -247,7 +391,7 @@ std::string preprocessImpl( post(context); - return resolvedString; + return std::move(renderProgress.output); } } diff --git a/src/nbl/asset/utils/waveContext.h b/src/nbl/asset/utils/waveContext.h index 660c86ad08..14dca37159 100644 --- a/src/nbl/asset/utils/waveContext.h +++ b/src/nbl/asset/utils/waveContext.h @@ -18,6 +18,58 @@ using namespace boost; using namespace boost::wave; using namespace boost::wave::util; +namespace detail +{ +inline std::string escape_control_chars(std::string_view text) +{ + static constexpr char hex[] = "0123456789ABCDEF"; + + std::string out; + out.reserve(text.size()); + + for (const auto ch : text) + { + switch (ch) + { + case '\n': + out += "\\n"; + break; + case '\r': + out += "\\r"; + break; + case '\t': + out += "\\t"; + break; + case '\0': + out += "\\0"; + break; + default: + { + const auto uch = static_cast(ch); + if (uch < 0x20u || uch == 0x7Fu) + { + out += "\\x"; + out.push_back(hex[uch >> 4u]); + out.push_back(hex[uch & 0x0Fu]); + } + else + out.push_back(ch); + break; + } + } + } + + return out; +} + +inline std::string escape_control_chars(const char* text) +{ + if (!text) + return {}; + return escape_control_chars(std::string_view(text)); +} +} + // for including builtins struct load_to_string final { @@ -531,7 +583,6 @@ template<> inline bool boost::wave::impl::pp_iterator_functor inline bool boost::wave::impl::pp_iterator_functor("-P"); + const bool dumpPreprocessedOnFailure = program.get("--dump-preprocessed-on-failure"); const bool hasFc = program.is_used("-Fc"); const bool hasFo = program.is_used("-Fo"); @@ -253,6 +255,11 @@ class ShaderCompiler final : public IApplicationFramework std::cerr << "Missing arguments. Expecting `-Fc {filename}` or `-Fo {filename}`.\n"; return false; } + if (dumpPreprocessedOnFailure && !preprocessOnly) + { + std::cerr << "Invalid arguments. --dump-preprocessed-on-failure requires -P.\n"; + return false; + } const std::string outputFilepath = hasFc ? program.get("-Fc") : program.get("-Fo"); if (outputFilepath.empty()) @@ -329,6 +336,8 @@ class ShaderCompiler final : public IApplicationFramework const char* const outType = preprocessOnly ? "Preprocessed" : "Compiled"; m_logger->log("%s %s", ILogger::ELL_INFO, action, fileToCompile.c_str()); m_logger->log("%s shader code will be saved to %s", ILogger::ELL_INFO, outType, outputFilepath.c_str()); + if (dumpPreprocessedOnFailure) + m_logger->log("Partial preprocessed output will be written to %s if preprocessing fails.", ILogger::ELL_INFO, outputFilepath.c_str()); auto [shader, shaderStage] = open_shader_file(fileToCompile); if (!shader || shader->getContentType() != IShader::E_CONTENT_TYPE::ECT_HLSL) @@ -338,12 +347,22 @@ class ShaderCompiler final : public IApplicationFramework } const auto start = std::chrono::high_resolution_clock::now(); - const auto job = runShaderJob(shader.get(), shaderStage, fileToCompile, dep, preprocessOnly); + const auto job = runShaderJob(shader.get(), shaderStage, fileToCompile, dep, preprocessOnly, dumpPreprocessedOnFailure); const auto end = std::chrono::high_resolution_clock::now(); const char* const op = preprocessOnly ? "preprocessing" : "compilation"; if (!job.ok) { + if (job.writeOutputOnFailure) + { + if (!writeOutputFile(outputFilepath, job.view, "partial preprocess dump")) + return false; + + if (job.view.empty()) + m_logger->log("Shader preprocessing failed before emitting any output. Empty dump written to %s.", ILogger::ELL_WARNING, outputFilepath.c_str()); + else + m_logger->log("Shader preprocessing failed after emitting %zu bytes. Partial dump written to %s.", ILogger::ELL_WARNING, job.view.size(), outputFilepath.c_str()); + } m_logger->log("Shader %s failed.", ILogger::ELL_ERROR, op); return false; } @@ -352,37 +371,8 @@ class ShaderCompiler final : public IApplicationFramework m_logger->log("Shader %s successful.", ILogger::ELL_INFO, op); m_logger->log("Took %s ms.", ILogger::ELL_PERFORMANCE, took.c_str()); - const auto outParent = std::filesystem::path(outputFilepath).parent_path(); - if (!outParent.empty() && !std::filesystem::exists(outParent)) - { - if (!std::filesystem::create_directories(outParent)) - { - m_logger->log("Failed to create parent directory for output %s.", ILogger::ELL_ERROR, outputFilepath.c_str()); - return false; - } - } - - std::fstream out(outputFilepath, std::ios::out | std::ios::binary); - if (!out.is_open()) - { - m_logger->log("Failed to open output file: %s", ILogger::ELL_ERROR, outputFilepath.c_str()); - return false; - } - - out.write(job.view.data(), job.view.size()); - if (out.fail()) - { - m_logger->log("Failed to write to output file: %s", ILogger::ELL_ERROR, outputFilepath.c_str()); - out.close(); + if (!writeOutputFile(outputFilepath, job.view, "output")) return false; - } - - out.close(); - if (out.fail()) - { - m_logger->log("Failed to close output file: %s", ILogger::ELL_ERROR, outputFilepath.c_str()); - return false; - } if (dep.enabled) m_logger->log("Dependency file written to %s", ILogger::ELL_INFO, dep.path.c_str()); @@ -403,6 +393,7 @@ class ShaderCompiler final : public IApplicationFramework struct RunResult { bool ok = false; + bool writeOutputOnFailure = false; std::string text; smart_refctd_ptr compiled; std::string_view view; @@ -484,7 +475,45 @@ class ShaderCompiler final : public IApplicationFramework std::printf("Saved \"%s\"\n", oPath.string().c_str()); } - RunResult runShaderJob(const IShader* shader, hlsl::ShaderStage shaderStage, std::string_view sourceIdentifier, const DepfileConfig& dep, const bool preprocessOnly) + bool writeOutputFile(const std::string& outputFilepath, const std::string_view content, const char* const description) + { + const auto outParent = std::filesystem::path(outputFilepath).parent_path(); + if (!outParent.empty() && !std::filesystem::exists(outParent)) + { + if (!std::filesystem::create_directories(outParent)) + { + m_logger->log("Failed to create parent directory for %s %s.", ILogger::ELL_ERROR, description, outputFilepath.c_str()); + return false; + } + } + + std::fstream out(outputFilepath, std::ios::out | std::ios::binary | std::ios::trunc); + if (!out.is_open()) + { + m_logger->log("Failed to open %s file: %s", ILogger::ELL_ERROR, description, outputFilepath.c_str()); + return false; + } + + if (!content.empty()) + out.write(content.data(), content.size()); + if (out.fail()) + { + m_logger->log("Failed to write %s file: %s", ILogger::ELL_ERROR, description, outputFilepath.c_str()); + out.close(); + return false; + } + + out.close(); + if (out.fail()) + { + m_logger->log("Failed to close %s file: %s", ILogger::ELL_ERROR, description, outputFilepath.c_str()); + return false; + } + + return true; + } + + RunResult runShaderJob(const IShader* shader, hlsl::ShaderStage shaderStage, std::string_view sourceIdentifier, const DepfileConfig& dep, const bool preprocessOnly, const bool dumpPreprocessedOnFailure) { RunResult r; auto hlslcompiler = make_smart_refctd_ptr(smart_refctd_ptr(m_system)); @@ -547,9 +576,17 @@ class ShaderCompiler final : public IApplicationFramework const char* codePtr = (const char*)shader->getContent()->getPointer(); std::string_view code(codePtr, std::strlen(codePtr)); + std::string partialOutputOnFailure; + if (dumpPreprocessedOnFailure) + opt.partialOutputOnFailure = &partialOutputOnFailure; r.text = hlslcompiler->preprocessShader(std::string(code), shaderStage, opt, nullptr); r.ok = !r.text.empty(); + if (!r.ok && dumpPreprocessedOnFailure) + { + r.text = std::move(partialOutputOnFailure); + r.writeOutputOnFailure = true; + } r.view = r.text; return r; } From 4871e245e3440fb8807b4fa06d528c4d1934f21d Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Thu, 19 Mar 2026 14:57:30 +0100 Subject: [PATCH 21/55] Clean up preprocess failure plumbing --- include/nbl/asset/utils/IShaderCompiler.h | 4 +- src/nbl/asset/utils/CWaveStringResolver.cpp | 41 +++---- tools/nsc/main.cpp | 125 ++++++++++++-------- 3 files changed, 97 insertions(+), 73 deletions(-) diff --git a/include/nbl/asset/utils/IShaderCompiler.h b/include/nbl/asset/utils/IShaderCompiler.h index 552d786c80..f3cfe07132 100644 --- a/include/nbl/asset/utils/IShaderCompiler.h +++ b/include/nbl/asset/utils/IShaderCompiler.h @@ -16,6 +16,8 @@ #include "nbl/builtin/hlsl/enums.hlsl" +#include + namespace nbl::asset { @@ -136,7 +138,7 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted E_SPIRV_VERSION targetSpirvVersion = E_SPIRV_VERSION::ESV_1_6; bool depfile = false; system::path depfilePath = {}; - std::string* partialOutputOnFailure = nullptr; + std::function onPartialOutputOnFailure = {}; }; // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#debugging diff --git a/src/nbl/asset/utils/CWaveStringResolver.cpp b/src/nbl/asset/utils/CWaveStringResolver.cpp index cfd329fdd4..1238000793 100644 --- a/src/nbl/asset/utils/CWaveStringResolver.cpp +++ b/src/nbl/asset/utils/CWaveStringResolver.cpp @@ -40,6 +40,7 @@ #include "nabla.h" #include #include +#include #include using namespace nbl; @@ -49,9 +50,9 @@ using namespace nbl::asset; namespace { -constexpr size_t kWavePartialOutputTailMaxChars = 4096ull; -constexpr size_t kWavePartialOutputTailMaxLines = 16ull; -constexpr size_t kWaveTokenPreviewMaxChars = 160ull; +constexpr size_t kWaveFailureLogOutputTailMaxChars = 4096ull; +constexpr size_t kWaveFailureLogOutputTailMaxLines = 16ull; +constexpr size_t kWaveFailureLogTokenPreviewMaxChars = 160ull; struct WaveRenderProgress { @@ -106,13 +107,7 @@ size_t countLogicalLines(const std::string_view text) if (text.empty()) return 0ull; - size_t lines = 0ull; - for (const auto ch : text) - { - if (ch == '\n') - ++lines; - } - + size_t lines = static_cast(std::count(text.begin(), text.end(), '\n')); if (text.back() != '\n') ++lines; return lines; @@ -158,7 +153,7 @@ std::string indentMultiline(std::string_view text, std::string_view indent) return out; } -std::string makeOutputTail(std::string_view text) +std::string makeFailureLogOutputTail(std::string_view text) { if (text.empty()) return {}; @@ -173,14 +168,14 @@ std::string makeOutputTail(std::string_view text) if (text[start] == '\n') { ++newlines; - if (newlines > kWavePartialOutputTailMaxLines) + if (newlines > kWaveFailureLogOutputTailMaxLines) { ++start; break; } } - if (chars >= kWavePartialOutputTailMaxChars) + if (chars >= kWaveFailureLogOutputTailMaxChars) break; } @@ -226,7 +221,7 @@ std::string makeWaveFailureContext( if (!renderProgress.lastTokenFile.empty()) stream << "\n last_emitted_token_location: " << nbl::wave::detail::escape_control_chars(renderProgress.lastTokenFile) << ':' << renderProgress.lastTokenLine << ':' << renderProgress.lastTokenColumn; if (!renderProgress.lastTokenValue.empty()) - stream << "\n last_emitted_token_value: " << truncateEscapedPreview(nbl::wave::detail::escape_control_chars(renderProgress.lastTokenValue), kWaveTokenPreviewMaxChars); + stream << "\n last_emitted_token_value: " << truncateEscapedPreview(nbl::wave::detail::escape_control_chars(renderProgress.lastTokenValue), kWaveFailureLogTokenPreviewMaxChars); const auto snippet = getLineSnippet(code, lineNo); if (!snippet.empty() && fileName && preprocessOptions.sourceIdentifier == fileName) @@ -237,7 +232,7 @@ std::string makeWaveFailureContext( stream << "\n " << caret; } - const auto outputTail = makeOutputTail(renderProgress.output); + const auto outputTail = makeFailureLogOutputTail(renderProgress.output); if (!outputTail.empty()) stream << "\n partial_output_tail:\n" << indentMultiline(outputTail, " "); @@ -327,14 +322,12 @@ std::string preprocessImpl( context.add_macro_definition("__SPIRV_MINOR_VERSION__=" + std::to_string(IShaderCompiler::getSpirvMinor(preprocessOptions.targetSpirvVersion))); WaveRenderProgress renderProgress; - if (preprocessOptions.partialOutputOnFailure) - preprocessOptions.partialOutputOnFailure->clear(); const char* phase = "registering built-in macros"; std::string activeMacroDefinition; - const auto storePartialOutputOnFailure = [&]() + const auto reportPartialOutputOnFailure = [&]() { - if (preprocessOptions.partialOutputOnFailure) - *preprocessOptions.partialOutputOnFailure = renderProgress.output; + if (preprocessOptions.onPartialOutputOnFailure) + preprocessOptions.onPartialOutputOnFailure(renderProgress.output); }; const auto makeFailureContext = [&](const char* const fileName, const int lineNo, const int columnNo) { @@ -357,7 +350,7 @@ std::string preprocessImpl( } catch (boost::wave::preprocess_exception& e) { - storePartialOutputOnFailure(); + reportPartialOutputOnFailure(); const auto escapedDescription = nbl::wave::detail::escape_control_chars(e.description()); const auto escapedFileName = nbl::wave::detail::escape_control_chars(e.file_name()); const auto failureContext = makeFailureContext(e.file_name(), e.line_no(), e.column_no()); @@ -367,7 +360,7 @@ std::string preprocessImpl( } catch (const boost::exception& e) { - storePartialOutputOnFailure(); + reportPartialOutputOnFailure(); const auto failureContext = makeFailureContext(preprocessOptions.sourceIdentifier.data(), 0, 0); preprocessOptions.logger.log("Boost exception caught during Wave preprocessing.\n%s", system::ILogger::ELL_ERROR, failureContext.c_str()); preprocessOptions.logger.log("Boost diagnostic information:\n%s", system::ILogger::ELL_ERROR, boost::diagnostic_information(e).c_str()); @@ -375,14 +368,14 @@ std::string preprocessImpl( } catch (const std::exception& e) { - storePartialOutputOnFailure(); + reportPartialOutputOnFailure(); const auto failureContext = makeFailureContext(preprocessOptions.sourceIdentifier.data(), 0, 0); preprocessOptions.logger.log("std::exception caught during Wave preprocessing. %s\n%s", system::ILogger::ELL_ERROR, e.what(), failureContext.c_str()); return {}; } catch (...) { - storePartialOutputOnFailure(); + reportPartialOutputOnFailure(); const auto failureContext = makeFailureContext(preprocessOptions.sourceIdentifier.data(), 0, 0); preprocessOptions.logger.log("Unknown exception caught during Wave preprocessing.\n%s", system::ILogger::ELL_ERROR, failureContext.c_str()); preprocessOptions.logger.log("Current exception diagnostic information:\n%s", system::ILogger::ELL_ERROR, boost::current_exception_diagnostic_information().c_str()); diff --git a/tools/nsc/main.cpp b/tools/nsc/main.cpp index 26bb4f5b40..94180ba71c 100644 --- a/tools/nsc/main.cpp +++ b/tools/nsc/main.cpp @@ -8,8 +8,6 @@ #include #include #include -#include -#include #include #include #include @@ -81,9 +79,9 @@ class ShaderLogger final : public IThreadsafeLogger if (!m_system || m_logPath.empty()) return; - const auto parent = std::filesystem::path(m_logPath).parent_path(); - if (!parent.empty() && !std::filesystem::exists(parent)) - std::filesystem::create_directories(parent); + const auto parent = m_logPath.parent_path(); + if (!parent.empty() && !m_system->isDirectory(parent)) + m_system->createDirectory(parent); for (auto attempt = 0u; attempt < kDeleteRetries; ++attempt) { @@ -152,6 +150,61 @@ class ShaderLogger final : public IThreadsafeLogger bool m_noLog = false; }; +static bool writeFileWithSystem(ISystem* system, const path& outputPath, const std::string_view content) +{ + if (!system || outputPath.empty()) + return false; + + const auto parent = outputPath.parent_path(); + if (!parent.empty() && !system->isDirectory(parent)) + { + if (!system->createDirectory(parent)) + return false; + } + + auto tempPath = outputPath; + tempPath += ".tmp"; + system->deleteFile(tempPath); + + smart_refctd_ptr outputFile; + { + ISystem::future_t> future; + system->createFile(future, tempPath, IFileBase::ECF_WRITE); + if (!future.wait()) + return false; + + auto lock = future.acquire(); + if (!lock) + return false; + lock.move_into(outputFile); + } + if (!outputFile) + return false; + + if (!content.empty()) + { + IFile::success_t success; + outputFile->write(success, content.data(), 0ull, content.size()); + if (!success) + { + outputFile = nullptr; + system->deleteFile(tempPath); + return false; + } + } + outputFile = nullptr; + + system->deleteFile(outputPath); + const auto moveError = system->moveFileOrDirectory(tempPath, outputPath); + if (moveError) + { + system->deleteFile(tempPath); + return false; + } + + return true; +} + class ShaderCompiler final : public IApplicationFramework { using base_t = IApplicationFramework; @@ -192,10 +245,13 @@ class ShaderCompiler final : public IApplicationFramework return false; } + m_system = system ? std::move(system) : IApplicationFramework::createSystem(); + if (!m_system) + return false; + if (program.get("--dump-build-info")) { - dumpBuildInfo(program); - std::exit(0); + return dumpBuildInfo(program); } if (!isAPILoaded()) @@ -204,10 +260,6 @@ class ShaderCompiler final : public IApplicationFramework return false; } - m_system = system ? std::move(system) : IApplicationFramework::createSystem(); - if (!m_system) - return false; - if (program.get("--self-test-unmount-builtins")) { const auto mountedBuiltinArchiveCount = m_system->getMountedBuiltinArchiveCount(); @@ -284,7 +336,7 @@ class ShaderCompiler final : public IApplicationFramework return false; } - const auto logPath = logPathOverride.empty() ? std::filesystem::path(outputFilepath).concat(".log") : std::filesystem::path(logPathOverride); + const auto logPath = logPathOverride.empty() ? path(outputFilepath).concat(".log") : path(logPathOverride); const auto fileMask = bitflag(ILogger::ELL_ALL); const auto consoleMask = bitflag(ILogger::ELL_WARNING) | ILogger::ELL_ERROR; @@ -427,7 +479,7 @@ class ShaderCompiler final : public IApplicationFramework return out; } - static void dumpBuildInfo(const argparse::ArgumentParser& program) + bool dumpBuildInfo(const argparse::ArgumentParser& program) { ::json j; auto& modules = j["modules"]; @@ -456,7 +508,7 @@ class ShaderCompiler final : public IApplicationFramework const auto pretty = j.dump(4); std::cout << pretty << std::endl; - std::filesystem::path oPath = "build-info.json"; + path oPath = "build-info.json"; if (program.is_used("--file")) { const auto filePath = program.get("--file"); @@ -464,49 +516,21 @@ class ShaderCompiler final : public IApplicationFramework oPath = filePath; } - std::ofstream outFile(oPath); - if (!outFile.is_open()) + if (!writeFileWithSystem(m_system.get(), oPath, pretty)) { - std::printf("Failed to open \"%s\" for writing\n", oPath.string().c_str()); - std::exit(-1); + std::printf("Failed to write \"%s\"\n", oPath.string().c_str()); + return false; } - outFile << pretty; std::printf("Saved \"%s\"\n", oPath.string().c_str()); + return true; } bool writeOutputFile(const std::string& outputFilepath, const std::string_view content, const char* const description) { - const auto outParent = std::filesystem::path(outputFilepath).parent_path(); - if (!outParent.empty() && !std::filesystem::exists(outParent)) - { - if (!std::filesystem::create_directories(outParent)) - { - m_logger->log("Failed to create parent directory for %s %s.", ILogger::ELL_ERROR, description, outputFilepath.c_str()); - return false; - } - } - - std::fstream out(outputFilepath, std::ios::out | std::ios::binary | std::ios::trunc); - if (!out.is_open()) - { - m_logger->log("Failed to open %s file: %s", ILogger::ELL_ERROR, description, outputFilepath.c_str()); - return false; - } - - if (!content.empty()) - out.write(content.data(), content.size()); - if (out.fail()) + if (!writeFileWithSystem(m_system.get(), outputFilepath, content)) { m_logger->log("Failed to write %s file: %s", ILogger::ELL_ERROR, description, outputFilepath.c_str()); - out.close(); - return false; - } - - out.close(); - if (out.fail()) - { - m_logger->log("Failed to close %s file: %s", ILogger::ELL_ERROR, description, outputFilepath.c_str()); return false; } @@ -578,7 +602,12 @@ class ShaderCompiler final : public IApplicationFramework std::string_view code(codePtr, std::strlen(codePtr)); std::string partialOutputOnFailure; if (dumpPreprocessedOnFailure) - opt.partialOutputOnFailure = &partialOutputOnFailure; + { + opt.onPartialOutputOnFailure = [&](const std::string_view partialOutput) + { + partialOutputOnFailure.assign(partialOutput); + }; + } r.text = hlslcompiler->preprocessShader(std::string(code), shaderStage, opt, nullptr); r.ok = !r.text.empty(); From 1f73d6a70724bd3cb18e20d7c88b0e79821f65be Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Thu, 19 Mar 2026 19:59:02 +0100 Subject: [PATCH 22/55] Backport Wave pragma fix and remove workaround Point Boost to the exact Wave one-line backport for emitted pragma newlines, remove the temporary local pragma workaround, and keep the remaining include-path fixes in Nabla. This leaves the Wave pragma issue fixed at the dependency level while preserving the Nabla-side fixes for Windows backslash includes and single-leading-slash virtual includes. Thanks to @Themperror for the additional pragma and include repros. Those made it straightforward to verify the dependency-level fix and drop the local workaround cleanly. --- 3rdparty/boost/superproject | 2 +- src/nbl/asset/utils/CWaveStringResolver.cpp | 9 +++--- src/nbl/asset/utils/IShaderCompiler.cpp | 31 +++++++++++++++++---- src/nbl/asset/utils/waveContext.h | 1 + 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/3rdparty/boost/superproject b/3rdparty/boost/superproject index 3b9e116eee..79a5d9a5be 160000 --- a/3rdparty/boost/superproject +++ b/3rdparty/boost/superproject @@ -1 +1 @@ -Subproject commit 3b9e116eeee85ab8fd0d8e5a97364fff5f02eb86 +Subproject commit 79a5d9a5be2d7bb75f8dbebf9a1412f5f700ac1e diff --git a/src/nbl/asset/utils/CWaveStringResolver.cpp b/src/nbl/asset/utils/CWaveStringResolver.cpp index 1238000793..8da0e828ec 100644 --- a/src/nbl/asset/utils/CWaveStringResolver.cpp +++ b/src/nbl/asset/utils/CWaveStringResolver.cpp @@ -316,10 +316,6 @@ std::string preprocessImpl( std::function post) { nbl::wave::context context(code.begin(), code.end(), preprocessOptions.sourceIdentifier.data(), { preprocessOptions }); - context.set_caching(withCaching); - context.add_macro_definition("__HLSL_VERSION"); - context.add_macro_definition("__SPIRV_MAJOR_VERSION__=" + std::to_string(IShaderCompiler::getSpirvMajor(preprocessOptions.targetSpirvVersion))); - context.add_macro_definition("__SPIRV_MINOR_VERSION__=" + std::to_string(IShaderCompiler::getSpirvMinor(preprocessOptions.targetSpirvVersion))); WaveRenderProgress renderProgress; const char* phase = "registering built-in macros"; @@ -335,6 +331,11 @@ std::string preprocessImpl( }; try { + context.set_caching(withCaching); + context.add_macro_definition("__HLSL_VERSION"); + context.add_macro_definition("__SPIRV_MAJOR_VERSION__=" + std::to_string(IShaderCompiler::getSpirvMajor(preprocessOptions.targetSpirvVersion))); + context.add_macro_definition("__SPIRV_MINOR_VERSION__=" + std::to_string(IShaderCompiler::getSpirvMinor(preprocessOptions.targetSpirvVersion))); + phase = "registering extra macro definitions"; for (const auto& define : preprocessOptions.extraDefines) { diff --git a/src/nbl/asset/utils/IShaderCompiler.cpp b/src/nbl/asset/utils/IShaderCompiler.cpp index 3f5a3bab17..372e877e21 100644 --- a/src/nbl/asset/utils/IShaderCompiler.cpp +++ b/src/nbl/asset/utils/IShaderCompiler.cpp @@ -627,6 +627,25 @@ auto IShaderCompiler::CFileSystemIncludeLoader::getInclude(const system::path& s return { f->getFileName(),std::move(contents) }; } +namespace +{ +std::string normalizeIncludeLookupName(const std::string& includeName) +{ + if (includeName.size() <= 1ull) + return includeName; + + const auto first = includeName.front(); + const auto second = includeName[1ull]; + const bool hasSingleLeadingSeparator = + (first == '/' || first == '\\') && + second != '/' && second != '\\'; + if (!hasSingleLeadingSeparator) + return includeName; + + return includeName.substr(1ull); +} +} + IShaderCompiler::CIncludeFinder::CIncludeFinder(core::smart_refctd_ptr&& system) : m_defaultFileSystemLoader(core::make_smart_refctd_ptr(std::move(system))) { @@ -639,12 +658,13 @@ IShaderCompiler::CIncludeFinder::CIncludeFinder(core::smart_refctd_ptr IIncludeLoader::found_t { + const auto lookupName = normalizeIncludeLookupName(includeName); IShaderCompiler::IIncludeLoader::found_t retVal; - if (auto contents = tryIncludeGenerators(includeName)) + if (auto contents = tryIncludeGenerators(lookupName)) retVal = std::move(contents); - else if (auto contents = trySearchPaths(includeName)) + else if (auto contents = trySearchPaths(lookupName)) retVal = std::move(contents); - else retVal = m_defaultFileSystemLoader->getInclude(requestingSourceDir.string(), includeName); + else retVal = m_defaultFileSystemLoader->getInclude(requestingSourceDir.string(), lookupName); core::blake3_hasher hasher; @@ -658,10 +678,11 @@ auto IShaderCompiler::CIncludeFinder::getIncludeStandard(const system::path& req // @param includeName: the string within "" of the include preprocessing directive auto IShaderCompiler::CIncludeFinder::getIncludeRelative(const system::path& requestingSourceDir, const std::string& includeName) const -> IIncludeLoader::found_t { + const auto lookupName = normalizeIncludeLookupName(includeName); IShaderCompiler::IIncludeLoader::found_t retVal; - if (auto contents = m_defaultFileSystemLoader->getInclude(requestingSourceDir.string(), includeName)) + if (auto contents = m_defaultFileSystemLoader->getInclude(requestingSourceDir.string(), lookupName)) retVal = std::move(contents); - else retVal = std::move(trySearchPaths(includeName)); + else retVal = std::move(trySearchPaths(lookupName)); core::blake3_hasher hasher; hasher.update(reinterpret_cast(retVal.contents.data()), retVal.contents.size() * (sizeof(char) / sizeof(uint8_t))); diff --git a/src/nbl/asset/utils/waveContext.h b/src/nbl/asset/utils/waveContext.h index 14dca37159..36f9d4ea99 100644 --- a/src/nbl/asset/utils/waveContext.h +++ b/src/nbl/asset/utils/waveContext.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "nbl/asset/utils/IShaderCompiler.h" From 66da590b3f06b586f69bdb522bad2f2eebf11b6f Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Thu, 19 Mar 2026 22:12:12 +0100 Subject: [PATCH 23/55] Use upstream gh-pages action for badges --- .github/workflows/build-nabla.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-nabla.yml b/.github/workflows/build-nabla.yml index 4e91c99587..c08ee7d73b 100644 --- a/.github/workflows/build-nabla.yml +++ b/.github/workflows/build-nabla.yml @@ -569,7 +569,7 @@ jobs: $imageBadge | Set-Content -Path "$imagePath/image-badge.json" -Encoding utf8 - name: Deploy Badges - uses: Devsh-Graphics-Programming/actions-gh-pages@v4.0.0-devsh.1 + uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_branch: badges From f830a47de2aae7c330c6e7c116c37cc20510e5f0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 22:14:28 +0000 Subject: [PATCH 24/55] Promote NSC channel 66da590b3f06b586f69bdb522bad2f2eebf11b6f --- tools/nsc/manifests/nsc-windows-x64-release.tag | 2 +- .../exe/tools/nsc/bin/build-info.json.dvc | 4 ++-- .../nsc-windows-x64-release/exe/tools/nsc/bin/nsc.exe.dvc | 4 ++-- .../runtime/nbl/3rdparty/dxc/dxcompiler.dll.dvc | 2 +- .../nsc-windows-x64-release/runtime/nbl/Nabla.dll.dvc | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/nsc/manifests/nsc-windows-x64-release.tag b/tools/nsc/manifests/nsc-windows-x64-release.tag index 1b036fd2ef..cd8d651439 100644 --- a/tools/nsc/manifests/nsc-windows-x64-release.tag +++ b/tools/nsc/manifests/nsc-windows-x64-release.tag @@ -1 +1 @@ -nsc-windows-x64-release-a091ca9ba0e04a7141fb8c107186ba8c478caa5a +nsc-windows-x64-release-66da590b3f06b586f69bdb522bad2f2eebf11b6f diff --git a/tools/nsc/manifests/nsc-windows-x64-release/exe/tools/nsc/bin/build-info.json.dvc b/tools/nsc/manifests/nsc-windows-x64-release/exe/tools/nsc/bin/build-info.json.dvc index 8f36d50b7d..2e26e723f6 100644 --- a/tools/nsc/manifests/nsc-windows-x64-release/exe/tools/nsc/bin/build-info.json.dvc +++ b/tools/nsc/manifests/nsc-windows-x64-release/exe/tools/nsc/bin/build-info.json.dvc @@ -1,5 +1,5 @@ outs: -- md5: cff274e861ff4a11e3778df40c872f71 - size: 1391 +- md5: 3422c063e9f0078b4efae5aa374e12c6 + size: 1286 hash: md5 path: build-info.json diff --git a/tools/nsc/manifests/nsc-windows-x64-release/exe/tools/nsc/bin/nsc.exe.dvc b/tools/nsc/manifests/nsc-windows-x64-release/exe/tools/nsc/bin/nsc.exe.dvc index 790b695c02..976c2e55fb 100644 --- a/tools/nsc/manifests/nsc-windows-x64-release/exe/tools/nsc/bin/nsc.exe.dvc +++ b/tools/nsc/manifests/nsc-windows-x64-release/exe/tools/nsc/bin/nsc.exe.dvc @@ -1,5 +1,5 @@ outs: -- md5: a858a4755629b784e25cbef47e75c975 - size: 263680 +- md5: e8859f963019b7c7dd0fd815e625e4ee + size: 256512 hash: md5 path: nsc.exe diff --git a/tools/nsc/manifests/nsc-windows-x64-release/runtime/nbl/3rdparty/dxc/dxcompiler.dll.dvc b/tools/nsc/manifests/nsc-windows-x64-release/runtime/nbl/3rdparty/dxc/dxcompiler.dll.dvc index 157154696a..3b22d29bd8 100644 --- a/tools/nsc/manifests/nsc-windows-x64-release/runtime/nbl/3rdparty/dxc/dxcompiler.dll.dvc +++ b/tools/nsc/manifests/nsc-windows-x64-release/runtime/nbl/3rdparty/dxc/dxcompiler.dll.dvc @@ -1,5 +1,5 @@ outs: -- md5: 491b609f8d02cadc5e9ac6df78dc5f14 +- md5: bcdd137482f6fd4a3b55da0884978d58 size: 21367296 hash: md5 path: dxcompiler.dll diff --git a/tools/nsc/manifests/nsc-windows-x64-release/runtime/nbl/Nabla.dll.dvc b/tools/nsc/manifests/nsc-windows-x64-release/runtime/nbl/Nabla.dll.dvc index 1024e8693b..5ad31c5b16 100644 --- a/tools/nsc/manifests/nsc-windows-x64-release/runtime/nbl/Nabla.dll.dvc +++ b/tools/nsc/manifests/nsc-windows-x64-release/runtime/nbl/Nabla.dll.dvc @@ -1,5 +1,5 @@ outs: -- md5: 40ea03c1c0e553b24f0d635741c77a83 - size: 29013504 +- md5: 5707af6c5ca1d82db41e877d075af6b2 + size: 29018624 hash: md5 path: Nabla.dll From 067d107e36ae99c3f2c281b8e078a6bbe36a4364 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 20 Mar 2026 16:55:36 +0700 Subject: [PATCH 25/55] change concepts for bxdf to *_and_weight methods, make changes for common bxdfs --- .../builtin/hlsl/bxdf/base/lambertian.hlsl | 15 ++- .../builtin/hlsl/bxdf/base/oren_nayar.hlsl | 18 +-- include/nbl/builtin/hlsl/bxdf/common.hlsl | 123 ++++++++++++++++-- .../bxdf/reflection/delta_distribution.hlsl | 14 +- .../bxdf/transmission/delta_distribution.hlsl | 14 +- .../bxdf/transmission/smooth_dielectric.hlsl | 30 ++--- 6 files changed, 161 insertions(+), 53 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index 3f7b85875a..92c5e2867d 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -25,13 +25,14 @@ struct SLambertianBase NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return hlsl::promote(_sample.getNdotL(_clamp) * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF)); + const spectral_type quo = hlsl::promote(_sample.getNdotL(_clamp) * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF)); + return quotient_pdf_type::create(quo, pdf(_sample, interaction)); } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return eval(_sample, interaction.isotropic); + return eval_and_weight(_sample, interaction.isotropic); } template > @@ -74,7 +75,7 @@ struct SLambertianBase return pdf(_sample, interaction.isotropic); } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { sampling::quotient_and_pdf qp; NBL_IF_CONSTEXPR (IsBSDF) @@ -83,9 +84,9 @@ struct SLambertianBase qp = sampling::ProjectedHemisphere::template quotientAndPdf(_sample.getNdotL(_clamp)); return quotient_pdf_type::create(qp.quotient()[0], qp.pdf()); } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_pdf(_sample, interaction.isotropic); + return quotient_and_weight(_sample, interaction.isotropic); } }; diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index ab06e8d43a..ae414cfcec 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -58,15 +58,15 @@ struct SOrenNayarBase return hlsl::promote(NdotL * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF) * __rec_pi_factored_out_wo_clamps(query.getVdotL(), NdotL, interaction.getNdotV(_clamp))); } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); - return __eval(query, _sample, interaction); + return quotient_pdf_type::create(__eval(query, _sample, interaction), pdf(_sample, interaction)); } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return eval(_sample, interaction.isotropic); + return eval_and_weight(_sample, interaction.isotropic); } template > @@ -110,21 +110,21 @@ struct SOrenNayarBase } template - quotient_pdf_type __quotient_and_pdf(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type __quotient_and_weight(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { scalar_type _pdf = pdf(_sample, interaction); scalar_type q = __rec_pi_factored_out_wo_clamps(query.getVdotL(), _sample.getNdotL(_clamp), interaction.getNdotV(_clamp)); return quotient_pdf_type::create(q, _pdf); } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); - return __quotient_and_pdf(query, _sample, interaction); + return __quotient_and_weight(query, _sample, interaction); } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_pdf(_sample, interaction.isotropic); + return quotient_and_weight(_sample, interaction.isotropic); } scalar_type A2; diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index c114222c7c..47ba881d9e 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -918,9 +918,9 @@ NBL_CONCEPT_BEGIN(3) #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) @@ -930,6 +930,32 @@ NBL_CONCEPT_END( #undef bxdf #include +// TODO: make these tractable_pdf_bxdf concepts (for existing bxdfs) -- or maybe not, tractable pdf may become a property + +// #define NBL_CONCEPT_NAME bxdf_common +// #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +// #define NBL_CONCEPT_TPLT_PRM_NAMES (T) +// #define NBL_CONCEPT_PARAM_0 (bxdf, T) +// #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) +// #define NBL_CONCEPT_PARAM_2 (aniso, typename T::anisotropic_interaction_type) +// NBL_CONCEPT_BEGIN(3) +// #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +// #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +// #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +// NBL_CONCEPT_END( +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) +// ); +// #undef aniso +// #undef _sample +// #undef bxdf +// #include + #define NBL_CONCEPT_NAME iso_bxdf_common #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -943,9 +969,9 @@ NBL_CONCEPT_BEGIN(3) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) ); @@ -953,6 +979,30 @@ NBL_CONCEPT_END( #undef _sample #undef bxdf #include + +// #define NBL_CONCEPT_NAME iso_bxdf_common +// #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +// #define NBL_CONCEPT_TPLT_PRM_NAMES (T) +// #define NBL_CONCEPT_PARAM_0 (bxdf, T) +// #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) +// #define NBL_CONCEPT_PARAM_2 (iso, typename T::isotropic_interaction_type) +// NBL_CONCEPT_BEGIN(3) +// #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +// #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +// #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +// NBL_CONCEPT_END( +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) +// ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) +// ); +// #undef iso +// #undef _sample +// #undef bxdf +// #include } #define NBL_CONCEPT_NAME BRDF @@ -1054,9 +1104,9 @@ NBL_CONCEPT_BEGIN(4) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) @@ -1068,6 +1118,35 @@ NBL_CONCEPT_END( #undef bxdf #include +// #define NBL_CONCEPT_NAME microfacet_bxdf_common +// #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +// #define NBL_CONCEPT_TPLT_PRM_NAMES (T) +// #define NBL_CONCEPT_PARAM_0 (bxdf, T) +// #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) +// #define NBL_CONCEPT_PARAM_2 (aniso, typename T::anisotropic_interaction_type) +// #define NBL_CONCEPT_PARAM_3 (anisocache, typename T::anisocache_type) +// NBL_CONCEPT_BEGIN(4) +// #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +// #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +// #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +// #define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +// NBL_CONCEPT_END( +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) +// ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) +// ); +// #undef anisocache +// #undef aniso +// #undef _sample +// #undef bxdf +// #include + #define NBL_CONCEPT_NAME iso_microfacet_bxdf_common #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -1084,9 +1163,9 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(microfacet_bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) ); @@ -1095,6 +1174,34 @@ NBL_CONCEPT_END( #undef _sample #undef bxdf #include + +// #define NBL_CONCEPT_NAME iso_microfacet_bxdf_common +// #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +// #define NBL_CONCEPT_TPLT_PRM_NAMES (T) +// #define NBL_CONCEPT_PARAM_0 (bxdf, T) +// #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) +// #define NBL_CONCEPT_PARAM_2 (iso, typename T::isotropic_interaction_type) +// #define NBL_CONCEPT_PARAM_3 (isocache, typename T::isocache_type) +// NBL_CONCEPT_BEGIN(4) +// #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +// #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +// #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +// #define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +// NBL_CONCEPT_END( +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(microfacet_bxdf_common, T)) +// ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) +// ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) +// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) +// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) +// ); +// #undef isocache +// #undef iso +// #undef _sample +// #undef bxdf +// #include } #define NBL_CONCEPT_NAME MicrofacetBRDF diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index 9bf9e16aa9..fd8291e3c2 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -25,13 +25,13 @@ struct SDeltaDistribution NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return hlsl::promote(0); + return quotient_pdf_type::create(0.0, 0.0); } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return hlsl::promote(0); + return quotient_pdf_type::create(0.0, 0.0); } sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) NBL_CONST_MEMBER_FUNC @@ -61,14 +61,14 @@ struct SDeltaDistribution return 0; } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { const scalar_type _pdf = bit_cast(numeric_limits::infinity); return quotient_pdf_type::create(1.0, _pdf); } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_pdf(_sample, interaction.isotropic); + return quotient_and_weight(_sample, interaction.isotropic); } }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index 7ec41aeed5..1796971b10 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -25,13 +25,13 @@ struct SDeltaDistribution NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return hlsl::promote(0); + return quotient_pdf_type::create(0.0, 0.0); } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return hlsl::promote(0); + return quotient_pdf_type::create(0.0, 0.0); } sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) NBL_CONST_MEMBER_FUNC @@ -58,14 +58,14 @@ struct SDeltaDistribution return 0; } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { const scalar_type _pdf = bit_cast(numeric_limits::infinity); return quotient_pdf_type::create(1.0, _pdf); } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_pdf(_sample, interaction.isotropic); + return quotient_and_weight(_sample, interaction.isotropic); } }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index bee202dc8d..c5f38dd13e 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -26,13 +26,13 @@ struct SSmoothDielectric NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return hlsl::promote(0); + return quotient_pdf_type::create(0.0, 0.0); } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return hlsl::promote(0); + return quotient_pdf_type::create(0.0, 0.0); } sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) NBL_CONST_MEMBER_FUNC @@ -67,13 +67,13 @@ struct SSmoothDielectric } // smooth BxDFs are isotropic by definition - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(1.0, bit_cast(numeric_limits::infinity)); } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_pdf(_sample, interaction.isotropic); + return quotient_and_weight(_sample, interaction.isotropic); } fresnel::OrientedEtas orientedEta; @@ -105,19 +105,19 @@ struct SThinSmoothDielectric return retval; } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return hlsl::promote(0); + return quotient_pdf_type::create(0.0, 0.0); } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return hlsl::promote(0); + return quotient_pdf_type::create(0.0, 0.0); } // usually `luminosityContributionHint` would be the Rec.709 luma coefficients (the Y row of the RGB to CIE XYZ matrix) // its basically a set of weights that determine // assert(1.0==luminosityContributionHint.r+luminosityContributionHint.g+luminosityContributionHint.b); - // `remainderMetadata` is a variable which the generator function returns byproducts of sample generation that would otherwise have to be redundantly calculated `quotient_and_pdf` + // `remainderMetadata` is a variable which the generator function returns byproducts of sample generation that would otherwise have to be redundantly calculated `quotient_and_weight` sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(spectral_type) remainderMetadata) NBL_CONST_MEMBER_FUNC { // we will only ever intersect from the outside @@ -160,7 +160,7 @@ struct SThinSmoothDielectric } // smooth BxDFs are isotropic by definition - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(interaction.getNdotV(), _sample.getNdotL()); const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel(interaction.getNdotV(_clamp))); @@ -171,9 +171,9 @@ struct SThinSmoothDielectric const scalar_type _pdf = bit_cast(numeric_limits::infinity); return quotient_pdf_type::create(sampleValue / sampleProb, _pdf); } - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_pdf(_sample, interaction.isotropic); + return quotient_and_weight(_sample, interaction.isotropic); } fresnel::Dielectric fresnel; From fc6deb80f457e6e50ea79da0ec35f1f8787be72b Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 20 Mar 2026 17:03:34 +0700 Subject: [PATCH 26/55] pdf method renamed to denominator, don't expect to be used --- include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl | 9 +++++---- include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl | 11 ++++++----- include/nbl/builtin/hlsl/bxdf/common.hlsl | 4 ++-- .../hlsl/bxdf/reflection/delta_distribution.hlsl | 4 ++-- .../hlsl/bxdf/transmission/delta_distribution.hlsl | 4 ++-- .../hlsl/bxdf/transmission/smooth_dielectric.hlsl | 10 +++++----- 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index 92c5e2867d..01bd494fc5 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -28,7 +28,7 @@ struct SLambertianBase quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { const spectral_type quo = hlsl::promote(_sample.getNdotL(_clamp) * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF)); - return quotient_pdf_type::create(quo, pdf(_sample, interaction)); + return quotient_pdf_type::create(quo, denominator(_sample, interaction)); } quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { @@ -63,16 +63,17 @@ struct SLambertianBase return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + // pdf function, because this BxDF has a tractable pdf, indicates that eval weight is also pdf + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { NBL_IF_CONSTEXPR (IsBSDF) return sampling::ProjectedSphere::pdf(_sample.getNdotL(_clamp)); else return sampling::ProjectedHemisphere::pdf(_sample.getNdotL(_clamp)); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return pdf(_sample, interaction.isotropic); + return denominator(_sample, interaction.isotropic); } quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index ae414cfcec..eb75217569 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -62,7 +62,7 @@ struct SOrenNayarBase { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); - return quotient_pdf_type::create(__eval(query, _sample, interaction), pdf(_sample, interaction)); + return quotient_pdf_type::create(__eval(query, _sample, interaction), denominator(_sample, interaction)); } quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { @@ -97,22 +97,23 @@ struct SOrenNayarBase return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + // pdf function, because this BxDF has a tractable pdf + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { if (IsBSDF) return sampling::ProjectedSphere::pdf(_sample.getNdotL(_clamp)); else return sampling::ProjectedHemisphere::pdf(_sample.getNdotL(_clamp)); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return pdf(_sample, interaction.isotropic); + return denominator(_sample, interaction.isotropic); } template quotient_pdf_type __quotient_and_weight(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - scalar_type _pdf = pdf(_sample, interaction); + scalar_type _pdf = denominator(_sample, interaction); scalar_type q = __rec_pi_factored_out_wo_clamps(query.getVdotL(), _sample.getNdotL(_clamp), interaction.getNdotV(_clamp)); return quotient_pdf_type::create(q, _pdf); } diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 47ba881d9e..25911adee1 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -919,7 +919,7 @@ NBL_CONCEPT_BEGIN(3) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.denominator(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) @@ -970,7 +970,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.denominator(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index fd8291e3c2..34da8fdfc9 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -52,11 +52,11 @@ struct SDeltaDistribution return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index 1796971b10..03fc46acc4 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -49,11 +49,11 @@ struct SDeltaDistribution return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index c5f38dd13e..3a01ee8dd5 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -26,6 +26,7 @@ struct SSmoothDielectric NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; + // eval and weight return 0 because smooth dielectric/conductor BxDFs are dirac delta distributions, model perfectly specular objects that scatter light to only one outgoing direction quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); @@ -56,12 +57,11 @@ struct SSmoothDielectric return generate(anisotropic_interaction_type::create(interaction), u); } - // eval and pdf return 0 because smooth dielectric/conductor BxDFs are dirac delta distributions, model perfectly specular objects that scatter light to only one outgoing direction - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } @@ -150,11 +150,11 @@ struct SThinSmoothDielectric return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } From 0afd2fa0631cd1d89fd760f29ae4b287777f74db Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Sat, 21 Mar 2026 17:48:54 +0100 Subject: [PATCH 27/55] fix MIS in the path tracer, dont make every ray that didn't miss go into the miss shader --- examples_tests | 2 +- include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 8 ++++---- include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples_tests b/examples_tests index 8f045a1c27..02fac2d163 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 8f045a1c27a198f8542456378f865032765378b8 +Subproject commit 02fac2d1633dd0405210e40a463e1180426133b9 diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 43e4cb124e..d018c9bf71 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -54,7 +54,7 @@ struct Unidirectional return hlsl::dot(spectralTypeToLumaCoeffs, col); } - bool closestHitProgram(uint16_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(closest_hit_type) intersectData) + bool closestHitProgram(const bool lastBounce, uint16_t depth, uint32_t _sample, NBL_REF_ARG(ray_type) ray, NBL_CONST_REF_ARG(closest_hit_type) intersectData) { anisotropic_interaction_type interaction = intersectData.getInteraction(); @@ -76,7 +76,7 @@ struct Unidirectional ray.addPayloadContribution(emissive); } - if (!matLightID.canContinuePath()) + if (lastBounce || !matLightID.canContinuePath()) return false; bxdfnode_type bxdf = materialSystem.getBxDFNode(matID, interaction); @@ -192,9 +192,9 @@ struct Unidirectional continuePath = intersection.foundHit(); if (continuePath) - continuePath = closestHitProgram(d, sampleIndex, ray, intersection); + continuePath = closestHitProgram(d==maxDepth, d, sampleIndex, ray, intersection); } - if (!continuePath) + if (continuePath) missProgram(ray); const uint32_t sampleCount = sampleIndex + 1; diff --git a/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl index 028d3e3653..aeb3cc854a 100644 --- a/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl @@ -64,7 +64,7 @@ struct SphericalTriangle return solidAngle(dummy0,dummy1); } - scalar_type projectedSolidAngle(const vector3_type receiverNormal, NBL_REF_ARG(vector3_type) cos_sides, NBL_REF_ARG(vector3_type) csc_sides, NBL_REF_ARG(vector3_type) cos_vertices) + scalar_type projectedSolidAngle(const vector3_type receiverNormal, NBL_REF_ARG(vector3_type) cos_vertices) { if (pyramidAngles()) return 0.f; From f2e2265da888165163ba6aba03458ab9ad700ead Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 23 Mar 2026 11:13:38 +0700 Subject: [PATCH 28/55] camelcase method names, no need for denominator method (pdf is pdf and weight is separate) --- .../builtin/hlsl/bxdf/base/lambertian.hlsl | 21 +++++++------- .../builtin/hlsl/bxdf/base/oren_nayar.hlsl | 27 +++++++++--------- include/nbl/builtin/hlsl/bxdf/common.hlsl | 10 +++---- .../bxdf/reflection/delta_distribution.hlsl | 14 +++++----- .../bxdf/transmission/delta_distribution.hlsl | 14 +++++----- .../bxdf/transmission/smooth_dielectric.hlsl | 28 +++++++++---------- 6 files changed, 55 insertions(+), 59 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index 01bd494fc5..b99bde6292 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -25,14 +25,14 @@ struct SLambertianBase NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { const spectral_type quo = hlsl::promote(_sample.getNdotL(_clamp) * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF)); - return quotient_pdf_type::create(quo, denominator(_sample, interaction)); + return quotient_pdf_type::create(quo, pdf(_sample, interaction)); } - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return eval_and_weight(_sample, interaction.isotropic); + return evalAndWeight(_sample, interaction.isotropic); } template > @@ -63,20 +63,19 @@ struct SLambertianBase return generate(anisotropic_interaction_type::create(interaction), u); } - // pdf function, because this BxDF has a tractable pdf, indicates that eval weight is also pdf - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { NBL_IF_CONSTEXPR (IsBSDF) return sampling::ProjectedSphere::pdf(_sample.getNdotL(_clamp)); else return sampling::ProjectedHemisphere::pdf(_sample.getNdotL(_clamp)); } - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return denominator(_sample, interaction.isotropic); + return pdf(_sample, interaction.isotropic); } - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { sampling::quotient_and_pdf qp; NBL_IF_CONSTEXPR (IsBSDF) @@ -85,9 +84,9 @@ struct SLambertianBase qp = sampling::ProjectedHemisphere::template quotientAndPdf(_sample.getNdotL(_clamp)); return quotient_pdf_type::create(qp.quotient()[0], qp.pdf()); } - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_weight(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic); } }; diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index eb75217569..585dd7c818 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -58,15 +58,15 @@ struct SOrenNayarBase return hlsl::promote(NdotL * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF) * __rec_pi_factored_out_wo_clamps(query.getVdotL(), NdotL, interaction.getNdotV(_clamp))); } - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); - return quotient_pdf_type::create(__eval(query, _sample, interaction), denominator(_sample, interaction)); + return quotient_pdf_type::create(__eval(query, _sample, interaction), pdf(_sample, interaction)); } - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return eval_and_weight(_sample, interaction.isotropic); + return evalAndWeight(_sample, interaction.isotropic); } template > @@ -97,35 +97,34 @@ struct SOrenNayarBase return generate(anisotropic_interaction_type::create(interaction), u); } - // pdf function, because this BxDF has a tractable pdf - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { if (IsBSDF) return sampling::ProjectedSphere::pdf(_sample.getNdotL(_clamp)); else return sampling::ProjectedHemisphere::pdf(_sample.getNdotL(_clamp)); } - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return denominator(_sample, interaction.isotropic); + return pdf(_sample, interaction.isotropic); } template - quotient_pdf_type __quotient_and_weight(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type __quotientAndWeight(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - scalar_type _pdf = denominator(_sample, interaction); + scalar_type _pdf = pdf(_sample, interaction); scalar_type q = __rec_pi_factored_out_wo_clamps(query.getVdotL(), _sample.getNdotL(_clamp), interaction.getNdotV(_clamp)); return quotient_pdf_type::create(q, _pdf); } - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); - return __quotient_and_weight(query, _sample, interaction); + return __quotientAndWeight(query, _sample, interaction); } - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_weight(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic); } scalar_type A2; diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 25911adee1..a094e5c6b1 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -918,9 +918,8 @@ NBL_CONCEPT_BEGIN(3) #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.denominator(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) @@ -969,9 +968,8 @@ NBL_CONCEPT_BEGIN(3) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.denominator(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) ); diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index 34da8fdfc9..f492ac0fdd 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -25,11 +25,11 @@ struct SDeltaDistribution NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } @@ -52,23 +52,23 @@ struct SDeltaDistribution return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { const scalar_type _pdf = bit_cast(numeric_limits::infinity); return quotient_pdf_type::create(1.0, _pdf); } - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_weight(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic); } }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index 03fc46acc4..dffde471dd 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -25,11 +25,11 @@ struct SDeltaDistribution NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } @@ -49,23 +49,23 @@ struct SDeltaDistribution return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { const scalar_type _pdf = bit_cast(numeric_limits::infinity); return quotient_pdf_type::create(1.0, _pdf); } - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_weight(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic); } }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index 3a01ee8dd5..5fc2587d87 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -27,11 +27,11 @@ struct SSmoothDielectric NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; // eval and weight return 0 because smooth dielectric/conductor BxDFs are dirac delta distributions, model perfectly specular objects that scatter light to only one outgoing direction - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } @@ -57,23 +57,23 @@ struct SSmoothDielectric return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } // smooth BxDFs are isotropic by definition - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(1.0, bit_cast(numeric_limits::infinity)); } - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_weight(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic); } fresnel::OrientedEtas orientedEta; @@ -105,11 +105,11 @@ struct SThinSmoothDielectric return retval; } - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type eval_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } @@ -150,17 +150,17 @@ struct SThinSmoothDielectric return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type denominator(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } // smooth BxDFs are isotropic by definition - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(interaction.getNdotV(), _sample.getNdotL()); const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel(interaction.getNdotV(_clamp))); @@ -171,9 +171,9 @@ struct SThinSmoothDielectric const scalar_type _pdf = bit_cast(numeric_limits::infinity); return quotient_pdf_type::create(sampleValue / sampleProb, _pdf); } - quotient_pdf_type quotient_and_weight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return quotient_and_weight(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic); } fresnel::Dielectric fresnel; From 070885996d413b1e6998998364f758d29c8f878c Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 23 Mar 2026 11:39:40 +0700 Subject: [PATCH 29/55] refactor cook torrance to fit concept --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) 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 30639d6b7c..78345032bd 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -156,11 +156,15 @@ struct SCookTorrance template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC { fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); if (!__checkValid(_f, _sample, interaction, cache)) - return hlsl::promote(0.0); + return quotient_pdf_type::create(scalar_type(0.0), scalar_type(0.0)); + + bool isInfinity; + scalar_type _pdf = __pdf(_sample, interaction, cache, isInfinity); + _pdf = hlsl::mix(_pdf, scalar_type(0.0), isInfinity); using quant_query_type = typename ndf_type::quant_query_type; quant_query_type qq = impl::quant_query_helper::template __call(ndf, _f, interaction, cache); @@ -168,7 +172,6 @@ struct SCookTorrance using g2g1_query_type = typename ndf_type::g2g1_query_type; g2g1_query_type gq = ndf.template createG2G1Query(_sample, interaction); - bool isInfinity; quant_type D = ndf.template D(qq, _sample, interaction, cache, isInfinity); scalar_type DG = D.projectedLightMeasure; if (!isInfinity) @@ -179,19 +182,22 @@ struct SCookTorrance // immediately return only after all calls setting DG // allows compiler to throw away calls to ndf.D if using __overwriteDG, before that we only avoid computation for G2(correlated) if (isInfinity) - return hlsl::promote(0.0); + return quotient_pdf_type::create(scalar_type(0.0), scalar_type(0.0)); scalar_type clampedVdotH = cache.getVdotH(); NBL_IF_CONSTEXPR(IsBSDF) clampedVdotH = hlsl::abs(clampedVdotH); + spectral_type quo; NBL_IF_CONSTEXPR(IsBSDF) { const spectral_type reflectance = impl::__implicit_promote::__call(_f(clampedVdotH)); - return hlsl::mix(reflectance, hlsl::promote(1.0) - reflectance, cache.isTransmission()) * DG; + quo = hlsl::mix(reflectance, hlsl::promote(1.0) - reflectance, cache.isTransmission()) * DG; } else - return impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; + quo = impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; + + return quotient_pdf_type::create(quo, _pdf); } sample_type __generate_common(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type localH, @@ -380,7 +386,7 @@ struct SCookTorrance template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) - quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC { if (!_sample.isValid()) return quotient_pdf_type::create(scalar_type(0.0), scalar_type(0.0)); // set pdf=0 when quo=0 because we don't want to give high weight to sampling strategy that yields 0 contribution From b6091798af0beb9ee80369fb1cba2cc88749e363 Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Mon, 23 Mar 2026 07:49:57 +0100 Subject: [PATCH 30/55] znowu w zyciu mi nie wyszlo --- include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index d018c9bf71..3fff1bc929 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -185,16 +185,17 @@ struct Unidirectional // bounces // note do 1-based indexing because we expect first dimension was consumed to generate the ray bool continuePath = true; + bool notMissed = true; for (uint16_t d = 1; (d <= maxDepth) && continuePath; d++) { ray.setT(numeric_limits::max); closest_hit_type intersection = intersector_type::traceClosestHit(scene, ray); - continuePath = intersection.foundHit(); - if (continuePath) + notMissed = intersection.foundHit(); + if (notMissed) continuePath = closestHitProgram(d==maxDepth, d, sampleIndex, ray, intersection); } - if (continuePath) + if (!notMissed) missProgram(ray); const uint32_t sampleCount = sampleIndex + 1; From 63cd4df209b89e884d69b3d4cada17363cb8e712 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 23 Mar 2026 14:16:44 +0700 Subject: [PATCH 31/55] changes to method names in pt concept, use *_weight instead of pdf and camelcase --- .../builtin/hlsl/path_tracing/concepts.hlsl | 13 +++++------ .../hlsl/path_tracing/unidirectional.hlsl | 23 +++++++++---------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 25ca98772c..f028de756d 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -182,10 +182,9 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::bxdfnode_type)) ((NBL_CONCEPT_REQ_TYPE)(T::create_params_t)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(BxdfNode, typename T::bxdfnode_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.eval(matid, _sample, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::measure_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.evalAndWeight(matid, _sample, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.generate(matid, aniso_inter, u, cache_)), ::nbl::hlsl::is_same_v, typename T::sample_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.pdf(matid, _sample, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotient_and_pdf(matid, _sample, aniso_inter, cache_)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotientAndWeight(matid, _sample, aniso_inter, cache_)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getBxDFNode(matid, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::bxdfnode_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.hasEmission(matid)), ::nbl::hlsl::is_same_v, bool)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.setMonochromeEta(matid, cie_y)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) @@ -261,10 +260,10 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::tolerance_method_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(NextEventEstimatorSampleQuotientReturn, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Ray, typename T::ray_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferred_pdf(scene, id, ray)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generate_and_quotient_and_pdf(scene, matSys, v/*origin*/, interaction, v/*xi*/, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.get_env_light_id()), ::nbl::hlsl::is_same_v, typename T::light_id_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.get_environment_radiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferredPdf(scene, id, ray)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generateAndQuotientAndWeight(scene, matSys, v/*origin*/, interaction, v/*xi*/, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.getEnvLightId()), ::nbl::hlsl::is_same_v, typename T::light_id_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.getEnvRadiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ); #undef scene #undef depth diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 98a81738cb..68eaf6d089 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -69,7 +69,7 @@ struct Unidirectional if (ray.shouldDoMIS() && matLightID.isLight()) { emissive *= ray.getPayloadThroughput(); - const scalar_type pdf = nee.deferred_pdf(scene, lightID, ray); + const scalar_type pdf = nee.deferredPdf(scene, lightID, ray); assert(!hlsl::isinf(pdf)); emissive *= ray.foundEmissiveMIS(pdf * pdf); } @@ -94,7 +94,7 @@ struct Unidirectional assert(neeProbability >= 0.0 && neeProbability <= 1.0); if (!partitionRandVariable(eps0.z, rcpChoiceProb)) { - typename nee_type::sample_quotient_return_type ret = nee.template generate_and_quotient_and_pdf( + typename nee_type::sample_quotient_return_type ret = nee.template generateAndQuotientAndWeight( scene, materialSystem, intersectP, interaction, eps0, depth ); @@ -108,12 +108,11 @@ struct Unidirectional // This stops a discrepancy in MIS weights and NEE mistakenly trying to add non-delta lobe contributions with a MIS weight > 0 and creating energy from thin air. if (neeContrib.pdf() > scalar_type(0.0)) { - // TODO: we'll need an `eval_and_mis_weight` and `quotient_and_mis_weight` - const scalar_type bsdf_pdf = materialSystem.pdf(matID, nee_sample, interaction); - neeContrib._quotient *= materialSystem.eval(matID, nee_sample, interaction) * rcpChoiceProb; + quotient_pdf_type bsdfContrib = materialSystem.evalAndWeight(matID, nee_sample, interaction); + neeContrib._quotient *= bsdfContrib.quotient() * rcpChoiceProb; if (neeContrib.pdf() < bit_cast(numeric_limits::infinity)) { - const scalar_type otherGenOverLightAndChoice = bsdf_pdf * rcpChoiceProb / neeContrib.pdf(); + const scalar_type otherGenOverLightAndChoice = bsdfContrib.pdf() * rcpChoiceProb / neeContrib.pdf(); neeContrib._quotient /= 1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice; // balance heuristic } @@ -141,9 +140,9 @@ struct Unidirectional return false; // the value of the bsdf divided by the probability of the sample being generated - quotient_pdf_type bsdf_quotient_pdf = materialSystem.quotient_and_pdf(matID, bsdf_sample, interaction, _cache); - throughput *= bsdf_quotient_pdf.quotient(); - bxdfPdf = bsdf_quotient_pdf.pdf(); + quotient_pdf_type bsdf_quotient_weight = materialSystem.quotientAndWeight(matID, bsdf_sample, interaction, _cache); + throughput *= bsdf_quotient_weight.quotient(); + bxdfPdf = bsdf_quotient_weight.pdf(); bxdfSample = bsdf_sample.getL().getDirection(); } @@ -170,9 +169,9 @@ struct Unidirectional void missProgram(NBL_REF_ARG(ray_type) ray) { - measure_type finalContribution = nee.get_environment_radiance(ray); - typename nee_type::light_id_type env_light_id = nee.get_env_light_id(); - const scalar_type pdf = nee.deferred_pdf(scene, env_light_id, ray); + measure_type finalContribution = nee.getEnvRadiance(ray); + typename nee_type::light_id_type env_light_id = nee.getEnvLightId(); + const scalar_type pdf = nee.deferredPdf(scene, env_light_id, ray); finalContribution *= ray.getPayloadThroughput(); if (pdf > scalar_type(0.0)) finalContribution *= ray.foundEmissiveMIS(pdf * pdf); From 59e090ecf48a601a853ceb68a2f0b24405154559 Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Mon, 23 Mar 2026 08:27:42 +0100 Subject: [PATCH 32/55] record my discoveries --- .../builtin/hlsl/shapes/spherical_triangle.hlsl | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl index aeb3cc854a..25f1e33554 100644 --- a/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl @@ -48,7 +48,8 @@ struct SphericalTriangle if (pyramidAngles()) return 0.f; - // 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 + // 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, hlsl::promote(-1.0), hlsl::promote(1.0)); // using Spherical Law of Cosines (TODO: do we need to clamp anymore? since the pyramid angles method introduction?) sin_vertices = hlsl::sqrt(hlsl::promote(1.0) - cos_vertices * cos_vertices); @@ -75,10 +76,22 @@ struct SphericalTriangle awayFromEdgePlane[0] = hlsl::cross(vertices[1], vertices[2]) * csc_sides[0]; awayFromEdgePlane[1] = hlsl::cross(vertices[2], vertices[0]) * csc_sides[1]; awayFromEdgePlane[2] = hlsl::cross(vertices[0], vertices[1]) * csc_sides[2]; + // The ABS makes it so that the computation is correct for an `abs(cos(theta))` factor which is the projected solid angle used for a BSDF + // Proof: Kelvin-Stokes theorem, if you split the set into two along the horizon with constant CCW winding, the `cross` along the shared edge goes in different directions and cancels out, + // while `acos` of the clipped great arcs corresponding to polygon edges add up to the original sides again const vector3_type externalProducts = hlsl::abs(hlsl::mul(/* transposed already */awayFromEdgePlane, receiverNormal)); + // Far TODO: `cross(A,B)*acos(dot(A,B))/sin(1-dot^2)` can be done with `cross*acos_csc_approx(dot(A,B))` + // We could skip the `csc_sides` factor, and computing `pyramidAngles` and replace them with this approximation weighting before the dot product with the receiver notmal + // The curve fit "revealed in a dream" to me is `exp2(F(log2(x+1)))` where `F(u)` is a polynomial, so far I've calculated `F = (1-u)0.635+(1-u^2)0.0118` which gives <5% error until 165 degrees + // I have a feeling that a polynomial of ((Au+B)u+C)u+D could be sufficient if it has following properties: + // `F(0) = 0` and + // `F(u) <= log2(\frac{\cos^{-1}\left(2^{x}-1\right)}{\sqrt{1-\left(2^{x}-1\right)^{2}}})` because you want to consistently under-estimate the Projected Solid Angle to avoid creating energy + // See https://www.desmos.com/calculator/sdptomhbju + // Furthermore we could clip the polynomial calc to `Cu+D or `(Bu+C)u+D` for small arguments const vector3_type pyramidAngles = hlsl::acos(cos_sides); - return hlsl::dot(pyramidAngles, externalProducts) / (2.f * numbers::pi); + // So that riangle covering almost whole hemisphere sums to PI + return hlsl::dot(pyramidAngles, externalProducts) * scalar_type(0.5); } vector3_type vertices[3]; From a877934d0f3928b543cb0e6f28c0612f2cbc29df Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 23 Mar 2026 14:46:11 +0700 Subject: [PATCH 33/55] renamed pdf to forwardPdf --- .../nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl | 10 +++++----- include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl | 10 +++++----- include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl | 10 +++++----- .../hlsl/bxdf/reflection/delta_distribution.hlsl | 4 ++-- .../hlsl/bxdf/transmission/delta_distribution.hlsl | 4 ++-- .../hlsl/bxdf/transmission/smooth_dielectric.hlsl | 8 ++++---- 6 files changed, 23 insertions(+), 23 deletions(-) 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 78345032bd..b478716cd0 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -163,7 +163,7 @@ struct SCookTorrance return quotient_pdf_type::create(scalar_type(0.0), scalar_type(0.0)); bool isInfinity; - scalar_type _pdf = __pdf(_sample, interaction, cache, isInfinity); + scalar_type _pdf = __forwardPdf(_sample, interaction, cache, isInfinity); _pdf = hlsl::mix(_pdf, scalar_type(0.0), isInfinity); using quant_query_type = typename ndf_type::quant_query_type; @@ -347,7 +347,7 @@ struct SCookTorrance } template - scalar_type __pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache, NBL_REF_ARG(bool) isInfinity) NBL_CONST_MEMBER_FUNC + scalar_type __forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache, NBL_REF_ARG(bool) isInfinity) NBL_CONST_MEMBER_FUNC { using quant_query_type = typename ndf_type::quant_query_type; using dg1_query_type = typename ndf_type::dg1_query_type; @@ -372,14 +372,14 @@ struct SCookTorrance template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC { fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); if (!__checkValid(_f, _sample, interaction, cache)) return scalar_type(0.0); bool isInfinity; - scalar_type _pdf = __pdf(_sample, interaction, cache, isInfinity); + scalar_type _pdf = __forwardPdf(_sample, interaction, cache, isInfinity); return hlsl::mix(_pdf, scalar_type(0.0), isInfinity); } @@ -392,7 +392,7 @@ struct SCookTorrance return quotient_pdf_type::create(scalar_type(0.0), scalar_type(0.0)); // set pdf=0 when quo=0 because we don't want to give high weight to sampling strategy that yields 0 contribution bool isInfinity; - scalar_type _pdf = __pdf(_sample, interaction, cache, isInfinity); + scalar_type _pdf = __forwardPdf(_sample, interaction, cache, isInfinity); fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); const bool valid = __checkValid(_f, _sample, interaction, cache); diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index b99bde6292..4d5dc9ce26 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -28,7 +28,7 @@ struct SLambertianBase quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { const spectral_type quo = hlsl::promote(_sample.getNdotL(_clamp) * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF)); - return quotient_pdf_type::create(quo, pdf(_sample, interaction)); + return quotient_pdf_type::create(quo, forwardPdf(_sample, interaction)); } quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { @@ -63,16 +63,16 @@ struct SLambertianBase return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { NBL_IF_CONSTEXPR (IsBSDF) return sampling::ProjectedSphere::pdf(_sample.getNdotL(_clamp)); else return sampling::ProjectedHemisphere::pdf(_sample.getNdotL(_clamp)); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return pdf(_sample, interaction.isotropic); + return forwardPdf(_sample, interaction.isotropic); } quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC @@ -82,7 +82,7 @@ struct SLambertianBase qp = sampling::ProjectedSphere::template quotientAndPdf(_sample.getNdotL(_clamp)); else qp = sampling::ProjectedHemisphere::template quotientAndPdf(_sample.getNdotL(_clamp)); - return quotient_pdf_type::create(qp.quotient()[0], qp.pdf()); + return quotient_pdf_type::create(qp.quotient(), qp.pdf()); } quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index 585dd7c818..d8cb919754 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -62,7 +62,7 @@ struct SOrenNayarBase { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); - return quotient_pdf_type::create(__eval(query, _sample, interaction), pdf(_sample, interaction)); + return quotient_pdf_type::create(__eval(query, _sample, interaction), forwardPdf(_sample, interaction)); } quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { @@ -97,22 +97,22 @@ struct SOrenNayarBase return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { if (IsBSDF) return sampling::ProjectedSphere::pdf(_sample.getNdotL(_clamp)); else return sampling::ProjectedHemisphere::pdf(_sample.getNdotL(_clamp)); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - return pdf(_sample, interaction.isotropic); + return forwardPdf(_sample, interaction.isotropic); } template quotient_pdf_type __quotientAndWeight(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - scalar_type _pdf = pdf(_sample, interaction); + scalar_type _pdf = forwardPdf(_sample, interaction); scalar_type q = __rec_pi_factored_out_wo_clamps(query.getVdotL(), _sample.getNdotL(_clamp), interaction.getNdotV(_clamp)); return quotient_pdf_type::create(q, _pdf); } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index f492ac0fdd..c6abb8646a 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -52,11 +52,11 @@ struct SDeltaDistribution return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index dffde471dd..f99d3f246a 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -49,11 +49,11 @@ struct SDeltaDistribution return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index 5fc2587d87..c9ff42cec0 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -57,11 +57,11 @@ struct SSmoothDielectric return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } @@ -150,11 +150,11 @@ struct SThinSmoothDielectric return generate(anisotropic_interaction_type::create(interaction), u); } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } - scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return 0; } From 64ce3baeb6cd49b9ab80553b927849f0f0c4c620 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 23 Mar 2026 14:53:34 +0700 Subject: [PATCH 34/55] add tractable pdf trait to bxdfs --- include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl | 1 + include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl | 1 + include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl | 1 + .../nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl | 1 + include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl | 1 + include/nbl/builtin/hlsl/bxdf/transmission/oren_nayar.hlsl | 1 + .../nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl | 2 ++ 7 files changed, 8 insertions(+) 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 b478716cd0..4d0672902b 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -442,6 +442,7 @@ struct traits > NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = !__type::IsBSDF; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = !__type::IsBSDF; + NBL_CONSTEXPR_STATIC_INLINE bool TractablePdf = true; }; } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl index 0e0e6bebb0..da0411eb4d 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/lambertian.hlsl @@ -28,6 +28,7 @@ struct traits > NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; + NBL_CONSTEXPR_STATIC_INLINE bool TractablePdf = true; }; } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl index df0e6ebc19..4ea6000419 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/oren_nayar.hlsl @@ -28,6 +28,7 @@ struct traits > NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; + NBL_CONSTEXPR_STATIC_INLINE bool TractablePdf = true; }; } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index f99d3f246a..9a30bf440a 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -78,6 +78,7 @@ struct traits > NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; + NBL_CONSTEXPR_STATIC_INLINE bool TractablePdf = true; }; } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl index 6ea67a65fd..0751ead92f 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/lambertian.hlsl @@ -28,6 +28,7 @@ struct traits > NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; + NBL_CONSTEXPR_STATIC_INLINE bool TractablePdf = true; }; } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/oren_nayar.hlsl index b39d48d2bf..1b224b34e1 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/oren_nayar.hlsl @@ -28,6 +28,7 @@ struct traits > NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; + NBL_CONSTEXPR_STATIC_INLINE bool TractablePdf = true; }; } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index c9ff42cec0..ee72fa0a9d 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -188,6 +188,7 @@ struct traits > NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; + NBL_CONSTEXPR_STATIC_INLINE bool TractablePdf = true; }; template @@ -197,6 +198,7 @@ struct traits > NBL_CONSTEXPR_STATIC_INLINE bool IsMicrofacet = false; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotV = true; NBL_CONSTEXPR_STATIC_INLINE bool clampNdotL = true; + NBL_CONSTEXPR_STATIC_INLINE bool TractablePdf = true; }; } From 148b9bdb90b2bfe5661f4b004c3fede61c0675ee Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 23 Mar 2026 16:54:55 +0700 Subject: [PATCH 35/55] bxdf input random to generate has variable dimensions (brdf>=2; bsdf>=3) for RIS sampling --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 7 +- .../builtin/hlsl/bxdf/base/lambertian.hlsl | 14 +- .../builtin/hlsl/bxdf/base/oren_nayar.hlsl | 14 +- include/nbl/builtin/hlsl/bxdf/common.hlsl | 135 +++--------------- .../bxdf/reflection/delta_distribution.hlsl | 6 +- .../bxdf/transmission/delta_distribution.hlsl | 6 +- .../bxdf/transmission/smooth_dielectric.hlsl | 25 ++-- 7 files changed, 53 insertions(+), 154 deletions(-) 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 4d0672902b..6b68879166 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -83,6 +83,7 @@ struct SCookTorrance NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = ndf_type::IsAnisotropic; NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = ndf_type::SupportedPaths != ndf::MTT_REFLECT; + using random_type = conditional_t; NBL_HLSL_BXDF_ANISOTROPIC_COND_DECLS(IsAnisotropic); // utility functions @@ -251,7 +252,7 @@ struct SCookTorrance return sample_type::create(L, T, B, NdotL); } template NBL_FUNC_REQUIRES(C::value && !IsBSDF) - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) cache) NBL_CONST_MEMBER_FUNC { const scalar_type NdotV = interaction.getNdotV(); if (NdotV < numeric_limits::min) @@ -281,7 +282,7 @@ struct SCookTorrance return s; } template NBL_FUNC_REQUIRES(C::value && IsBSDF) - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(anisocache_type) cache) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) cache) NBL_CONST_MEMBER_FUNC { const vector3_type localV = interaction.getTangentSpaceV(); const scalar_type NdotV = localV.z; @@ -338,7 +339,7 @@ struct SCookTorrance return s; } template NBL_FUNC_REQUIRES(C::value && !IsAnisotropic) - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const conditional_t u, NBL_REF_ARG(isocache_type) cache) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(isocache_type) cache) NBL_CONST_MEMBER_FUNC { anisocache_type aniso_cache; sample_type s = generate(anisotropic_interaction_type::create(interaction), u, aniso_cache); diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index 4d5dc9ce26..1d2588b008 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -23,6 +23,8 @@ struct SLambertianBase using this_t = SLambertianBase; BXDF_CONFIG_TYPE_ALIASES(Config); + using random_type = conditional_t; + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC @@ -36,7 +38,7 @@ struct SLambertianBase } template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) NBL_CONST_MEMBER_FUNC + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedHemisphere::cache_type cache; ray_dir_info_type L; @@ -44,7 +46,7 @@ struct SLambertianBase return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u) NBL_CONST_MEMBER_FUNC + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedSphere::cache_type cache; vector3_type _u = u; @@ -52,13 +54,7 @@ struct SLambertianBase L.setDirection(sampling::ProjectedSphere::generate(_u, cache)); return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } - template > - enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u) NBL_CONST_MEMBER_FUNC - { - return generate(anisotropic_interaction_type::create(interaction), u); - } - template > - enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector3_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { return generate(anisotropic_interaction_type::create(interaction), u); } diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index d8cb919754..4b356a1ecd 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -23,6 +23,8 @@ struct SOrenNayarBase using this_t = SOrenNayarBase; BXDF_CONFIG_TYPE_ALIASES(Config); + using random_type = conditional_t; + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; struct SCreationParams @@ -70,7 +72,7 @@ struct SOrenNayarBase } template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) NBL_CONST_MEMBER_FUNC + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedHemisphere::cache_type cache; ray_dir_info_type L; @@ -78,7 +80,7 @@ struct SOrenNayarBase return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u) NBL_CONST_MEMBER_FUNC + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedSphere::cache_type cache; vector3_type _u = u; @@ -86,13 +88,7 @@ struct SOrenNayarBase L.setDirection(sampling::ProjectedSphere::generate(_u, cache)); return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } - template > - enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u) NBL_CONST_MEMBER_FUNC - { - return generate(anisotropic_interaction_type::create(interaction), u); - } - template > - enable_if_t generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector3_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { return generate(anisotropic_interaction_type::create(interaction), u); } diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index a094e5c6b1..e9e103e203 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -902,6 +902,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::random_type)) ); #undef bxdf #include @@ -929,32 +930,6 @@ NBL_CONCEPT_END( #undef bxdf #include -// TODO: make these tractable_pdf_bxdf concepts (for existing bxdfs) -- or maybe not, tractable pdf may become a property - -// #define NBL_CONCEPT_NAME bxdf_common -// #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -// #define NBL_CONCEPT_TPLT_PRM_NAMES (T) -// #define NBL_CONCEPT_PARAM_0 (bxdf, T) -// #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) -// #define NBL_CONCEPT_PARAM_2 (aniso, typename T::anisotropic_interaction_type) -// NBL_CONCEPT_BEGIN(3) -// #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -// #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -// #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -// NBL_CONCEPT_END( -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) -// ); -// #undef aniso -// #undef _sample -// #undef bxdf -// #include - #define NBL_CONCEPT_NAME iso_bxdf_common #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -978,29 +953,10 @@ NBL_CONCEPT_END( #undef bxdf #include -// #define NBL_CONCEPT_NAME iso_bxdf_common -// #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -// #define NBL_CONCEPT_TPLT_PRM_NAMES (T) -// #define NBL_CONCEPT_PARAM_0 (bxdf, T) -// #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) -// #define NBL_CONCEPT_PARAM_2 (iso, typename T::isotropic_interaction_type) -// NBL_CONCEPT_BEGIN(3) -// #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -// #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -// #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -// NBL_CONCEPT_END( -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) -// ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) -// ); -// #undef iso -// #undef _sample -// #undef bxdf -// #include +template +NBL_BOOL_CONCEPT VecDim2OrMore = vector_traits::Dimension >= 2; +template +NBL_BOOL_CONCEPT VecDim3OrMore = vector_traits::Dimension >= 3; } #define NBL_CONCEPT_NAME BRDF @@ -1008,13 +964,14 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) #define NBL_CONCEPT_PARAM_1 (aniso, typename T::anisotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, vector) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) NBL_CONCEPT_BEGIN(3) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim2OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); #undef u @@ -1027,13 +984,14 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) #define NBL_CONCEPT_PARAM_1 (aniso, typename T::anisotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, vector) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) NBL_CONCEPT_BEGIN(3) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim3OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); #undef u @@ -1046,13 +1004,14 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) #define NBL_CONCEPT_PARAM_1 (iso, typename T::isotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, vector) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) NBL_CONCEPT_BEGIN(3) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::iso_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim2OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); #undef u @@ -1065,13 +1024,14 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) #define NBL_CONCEPT_PARAM_1 (iso, typename T::isotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, vector) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) NBL_CONCEPT_BEGIN(3) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::iso_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim3OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); #undef u @@ -1116,35 +1076,6 @@ NBL_CONCEPT_END( #undef bxdf #include -// #define NBL_CONCEPT_NAME microfacet_bxdf_common -// #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -// #define NBL_CONCEPT_TPLT_PRM_NAMES (T) -// #define NBL_CONCEPT_PARAM_0 (bxdf, T) -// #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) -// #define NBL_CONCEPT_PARAM_2 (aniso, typename T::anisotropic_interaction_type) -// #define NBL_CONCEPT_PARAM_3 (anisocache, typename T::anisocache_type) -// NBL_CONCEPT_BEGIN(4) -// #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -// #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -// #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -// #define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 -// NBL_CONCEPT_END( -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) -// ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) -// ); -// #undef anisocache -// #undef aniso -// #undef _sample -// #undef bxdf -// #include - #define NBL_CONCEPT_NAME iso_microfacet_bxdf_common #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -1172,34 +1103,6 @@ NBL_CONCEPT_END( #undef _sample #undef bxdf #include - -// #define NBL_CONCEPT_NAME iso_microfacet_bxdf_common -// #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -// #define NBL_CONCEPT_TPLT_PRM_NAMES (T) -// #define NBL_CONCEPT_PARAM_0 (bxdf, T) -// #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) -// #define NBL_CONCEPT_PARAM_2 (iso, typename T::isotropic_interaction_type) -// #define NBL_CONCEPT_PARAM_3 (isocache, typename T::isocache_type) -// NBL_CONCEPT_BEGIN(4) -// #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -// #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -// #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -// #define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 -// NBL_CONCEPT_END( -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(microfacet_bxdf_common, T)) -// ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) -// ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) -// ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) -// ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) -// ); -// #undef isocache -// #undef iso -// #undef _sample -// #undef bxdf -// #include } #define NBL_CONCEPT_NAME MicrofacetBRDF @@ -1207,7 +1110,7 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) #define NBL_CONCEPT_PARAM_1 (aniso, typename T::anisotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, vector) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) #define NBL_CONCEPT_PARAM_3 (anisocache, typename T::anisocache_type) NBL_CONCEPT_BEGIN(4) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 @@ -1216,6 +1119,7 @@ NBL_CONCEPT_BEGIN(4) #define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::microfacet_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim2OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u,anisocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); #undef anisocache @@ -1229,7 +1133,7 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) #define NBL_CONCEPT_PARAM_1 (aniso, typename T::anisotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, vector) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) #define NBL_CONCEPT_PARAM_3 (anisocache, typename T::anisocache_type) NBL_CONCEPT_BEGIN(4) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 @@ -1238,6 +1142,7 @@ NBL_CONCEPT_BEGIN(4) #define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::microfacet_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim3OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u,anisocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); #undef anisocache @@ -1251,7 +1156,7 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) #define NBL_CONCEPT_PARAM_1 (iso, typename T::isotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, vector) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) #define NBL_CONCEPT_PARAM_3 (isocache, typename T::isocache_type) NBL_CONCEPT_BEGIN(4) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 @@ -1260,6 +1165,7 @@ NBL_CONCEPT_BEGIN(4) #define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::iso_microfacet_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim2OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u,isocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); #undef isocache @@ -1273,7 +1179,7 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) #define NBL_CONCEPT_PARAM_1 (iso, typename T::isotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, vector) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) #define NBL_CONCEPT_PARAM_3 (isocache, typename T::isocache_type) NBL_CONCEPT_BEGIN(4) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 @@ -1282,6 +1188,7 @@ NBL_CONCEPT_BEGIN(4) #define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::iso_microfacet_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim3OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u,isocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); #undef isocache diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index c6abb8646a..84c25fb4fb 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -23,6 +23,8 @@ struct SDeltaDistribution using this_t = SDeltaDistribution; BXDF_CONFIG_TYPE_ALIASES(Config); + using random_type = vector2_type; + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC @@ -34,7 +36,7 @@ struct SDeltaDistribution return quotient_pdf_type::create(0.0, 0.0); } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { vector3_type V = interaction.getV().getDirection(); bxdf::Reflect r = bxdf::Reflect::create(V, interaction.getN()); @@ -47,7 +49,7 @@ struct SDeltaDistribution return s; } - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { return generate(anisotropic_interaction_type::create(interaction), u); } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index 9a30bf440a..835b41a044 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -23,6 +23,8 @@ struct SDeltaDistribution using this_t = SDeltaDistribution; BXDF_CONFIG_TYPE_ALIASES(Config); + using random_type = vector2_type; + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC @@ -34,7 +36,7 @@ struct SDeltaDistribution return quotient_pdf_type::create(0.0, 0.0); } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { ray_dir_info_type L = interaction.getV().transmit(); sample_type s = sample_type::create(L, interaction.getN()); @@ -44,7 +46,7 @@ struct SDeltaDistribution s.NdotL2 = interaction.getNdotV2(); return s; } - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { return generate(anisotropic_interaction_type::create(interaction), u); } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index ee72fa0a9d..d3af6840ec 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -24,6 +24,8 @@ struct SSmoothDielectric using this_t = SSmoothDielectric; BXDF_CONFIG_TYPE_ALIASES(Config); + using random_type = vector3_type; + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; // eval and weight return 0 because smooth dielectric/conductor BxDFs are dirac delta distributions, model perfectly specular objects that scatter light to only one outgoing direction @@ -36,7 +38,7 @@ struct SSmoothDielectric return quotient_pdf_type::create(0.0, 0.0); } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(random_type) u) NBL_CONST_MEMBER_FUNC { const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta.value*orientedEta.value, interaction.getNdotV(_clamp))[0]; @@ -52,7 +54,7 @@ struct SSmoothDielectric ray_dir_info_type L = V.reflectRefract(rr, transmitted, orientedEta.rcp[0]); return sample_type::create(L, interaction.getT(), interaction.getB(), interaction.getN()); } - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(random_type) u) NBL_CONST_MEMBER_FUNC { return generate(anisotropic_interaction_type::create(interaction), u); } @@ -83,16 +85,9 @@ template; - NBL_BXDF_CONFIG_ALIAS(scalar_type, Config); - NBL_BXDF_CONFIG_ALIAS(vector2_type, Config); - NBL_BXDF_CONFIG_ALIAS(vector3_type, Config); - NBL_BXDF_CONFIG_ALIAS(monochrome_type, Config); - NBL_BXDF_CONFIG_ALIAS(ray_dir_info_type, Config); - NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config); - NBL_BXDF_CONFIG_ALIAS(sample_type, Config); - NBL_BXDF_CONFIG_ALIAS(spectral_type, Config); - NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config); + BXDF_CONFIG_TYPE_ALIASES(Config); + + using random_type = vector3_type; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; @@ -118,7 +113,7 @@ struct SThinSmoothDielectric // its basically a set of weights that determine // assert(1.0==luminosityContributionHint.r+luminosityContributionHint.g+luminosityContributionHint.b); // `remainderMetadata` is a variable which the generator function returns byproducts of sample generation that would otherwise have to be redundantly calculated `quotient_and_weight` - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(spectral_type) remainderMetadata) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(spectral_type) remainderMetadata) NBL_CONST_MEMBER_FUNC { // we will only ever intersect from the outside const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel(interaction.getNdotV(_clamp))); @@ -140,12 +135,12 @@ struct SThinSmoothDielectric return sample_type::create(L, interaction.getT(), interaction.getB(), N); } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { vector3_type dummy; return generate(interaction, u, dummy); } - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector3_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC { return generate(anisotropic_interaction_type::create(interaction), u); } From 946c9c735913fe68125137772c0da52afb8b6106 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 24 Mar 2026 11:18:11 +0700 Subject: [PATCH 36/55] pass ray to nee generate instead, store depth in ray --- .../nbl/builtin/hlsl/path_tracing/concepts.hlsl | 11 ++++------- .../hlsl/path_tracing/unidirectional.hlsl | 17 +++++++++-------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index f028de756d..c4be1d83ad 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -234,17 +234,15 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_3 (v, typename T::vector3_type) #define NBL_CONCEPT_PARAM_4 (matSys, impl::DummyMaterialSystem) #define NBL_CONCEPT_PARAM_5 (interaction, typename T::interaction_type) -#define NBL_CONCEPT_PARAM_6 (depth, uint16_t) -#define NBL_CONCEPT_PARAM_7 (scene, typename T::scene_type) -NBL_CONCEPT_BEGIN(8) +#define NBL_CONCEPT_PARAM_6 (scene, typename T::scene_type) +NBL_CONCEPT_BEGIN(7) #define nee NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define ray NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define id NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define v NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 #define matSys NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 #define interaction NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5 -#define depth NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 -#define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7 +#define scene NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) @@ -261,12 +259,11 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(NextEventEstimatorSampleQuotientReturn, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Ray, typename T::ray_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferredPdf(scene, id, ray)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generateAndQuotientAndWeight(scene, matSys, v/*origin*/, interaction, v/*xi*/, depth)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generateAndQuotientAndWeight(scene, matSys, v/*origin*/, interaction, v/*xi*/, ray)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.getEnvLightId()), ::nbl::hlsl::is_same_v, typename T::light_id_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.getEnvRadiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) ); #undef scene -#undef depth #undef interaction #undef matSys #undef v diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index ff12ea7f16..1aecf129bb 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -69,9 +69,9 @@ struct Unidirectional if (ray.shouldDoMIS() && matLightID.isLight()) { emissive *= ray.getPayloadThroughput(); - const scalar_type pdf = nee.deferredPdf(scene, lightID, ray); - assert(!hlsl::isinf(pdf)); - emissive *= ray.foundEmissiveMIS(pdf * pdf); + const scalar_type weight = nee.deferredPdf(scene, lightID, ray); + assert(!hlsl::isinf(weight)); + emissive *= ray.foundEmissiveMIS(weight * weight); } ray.addPayloadContribution(emissive); } @@ -95,8 +95,8 @@ struct Unidirectional if (!partitionRandVariable(eps0.z, rcpChoiceProb)) { typename nee_type::sample_quotient_return_type ret = nee.template generateAndQuotientAndWeight( - scene, materialSystem, intersectP, interaction, - eps0, depth + scene, materialSystem, intersectP, + interaction, eps0, ray ); scalar_type t = ret.getT(); sample_type nee_sample = ret.getSample(); @@ -171,10 +171,10 @@ struct Unidirectional { measure_type finalContribution = nee.getEnvRadiance(ray); typename nee_type::light_id_type env_light_id = nee.getEnvLightId(); - const scalar_type pdf = nee.deferredPdf(scene, env_light_id, ray); + const scalar_type weight = nee.deferredPdf(scene, env_light_id, ray); finalContribution *= ray.getPayloadThroughput(); - if (pdf > scalar_type(0.0)) - finalContribution *= ray.foundEmissiveMIS(pdf * pdf); + if (weight > scalar_type(0.0)) + finalContribution *= ray.foundEmissiveMIS(weight * weight); ray.addPayloadContribution(finalContribution); } @@ -188,6 +188,7 @@ struct Unidirectional for (uint16_t d = 1; (d <= maxDepth) && continuePath; d++) { ray.setT(numeric_limits::max); + ray.setDepth(d); closest_hit_type intersection = intersector_type::traceClosestHit(scene, ray); notMissed = intersection.foundHit(); From 4f4421d4bb6039e55f09e2701639c821f630aed1 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 24 Mar 2026 11:36:02 +0700 Subject: [PATCH 37/55] nee deferredPdf is deferredWeight --- include/nbl/builtin/hlsl/path_tracing/concepts.hlsl | 2 +- include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index c4be1d83ad..438f0391de 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -258,7 +258,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::tolerance_method_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(NextEventEstimatorSampleQuotientReturn, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(Ray, typename T::ray_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferredPdf(scene, id, ray)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.deferredWeight(scene, id, ray)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.template generateAndQuotientAndWeight(scene, matSys, v/*origin*/, interaction, v/*xi*/, ray)), ::nbl::hlsl::is_same_v, typename T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.getEnvLightId()), ::nbl::hlsl::is_same_v, typename T::light_id_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((nee.getEnvRadiance(ray)), ::nbl::hlsl::is_same_v, typename T::spectral_type)) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 1aecf129bb..1752e9c768 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -69,7 +69,7 @@ struct Unidirectional if (ray.shouldDoMIS() && matLightID.isLight()) { emissive *= ray.getPayloadThroughput(); - const scalar_type weight = nee.deferredPdf(scene, lightID, ray); + const scalar_type weight = nee.deferredWeight(scene, lightID, ray); assert(!hlsl::isinf(weight)); emissive *= ray.foundEmissiveMIS(weight * weight); } @@ -171,7 +171,7 @@ struct Unidirectional { measure_type finalContribution = nee.getEnvRadiance(ray); typename nee_type::light_id_type env_light_id = nee.getEnvLightId(); - const scalar_type weight = nee.deferredPdf(scene, env_light_id, ray); + const scalar_type weight = nee.deferredWeight(scene, env_light_id, ray); finalContribution *= ray.getPayloadThroughput(); if (weight > scalar_type(0.0)) finalContribution *= ray.foundEmissiveMIS(weight * weight); From a70f420c4f84865ce4eaff72dc427e87ed7eca4e Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 24 Mar 2026 16:03:08 +0700 Subject: [PATCH 38/55] all bxdfs have a cache type from generate to quotient_weight, microfacet checks cache with concept --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 1 - .../builtin/hlsl/bxdf/base/lambertian.hlsl | 23 ++- .../builtin/hlsl/bxdf/base/oren_nayar.hlsl | 23 ++- include/nbl/builtin/hlsl/bxdf/common.hlsl | 175 +++--------------- .../bxdf/reflection/delta_distribution.hlsl | 19 +- .../bxdf/transmission/delta_distribution.hlsl | 17 +- .../bxdf/transmission/smooth_dielectric.hlsl | 38 ++-- 7 files changed, 95 insertions(+), 201 deletions(-) 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 6b68879166..f243d717ac 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -165,7 +165,6 @@ struct SCookTorrance bool isInfinity; scalar_type _pdf = __forwardPdf(_sample, interaction, cache, isInfinity); - _pdf = hlsl::mix(_pdf, scalar_type(0.0), isInfinity); using quant_query_type = typename ndf_type::quant_query_type; quant_query_type qq = impl::quant_query_helper::template __call(ndf, _f, interaction, cache); diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index 1d2588b008..3b3dd21e59 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -24,21 +24,24 @@ struct SLambertianBase BXDF_CONFIG_TYPE_ALIASES(Config); using random_type = conditional_t; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { const spectral_type quo = hlsl::promote(_sample.getNdotL(_clamp) * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF)); return quotient_pdf_type::create(quo, forwardPdf(_sample, interaction)); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return evalAndWeight(_sample, interaction.isotropic); + return evalAndWeight(_sample, interaction.isotropic, _cache); } template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedHemisphere::cache_type cache; ray_dir_info_type L; @@ -46,7 +49,7 @@ struct SLambertianBase return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedSphere::cache_type cache; vector3_type _u = u; @@ -54,9 +57,9 @@ struct SLambertianBase L.setDirection(sampling::ProjectedSphere::generate(_u, cache)); return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return generate(anisotropic_interaction_type::create(interaction), u); + return generate(anisotropic_interaction_type::create(interaction), u, _cache); } scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC @@ -71,7 +74,7 @@ struct SLambertianBase return forwardPdf(_sample, interaction.isotropic); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { sampling::quotient_and_pdf qp; NBL_IF_CONSTEXPR (IsBSDF) @@ -80,9 +83,9 @@ struct SLambertianBase qp = sampling::ProjectedHemisphere::template quotientAndPdf(_sample.getNdotL(_clamp)); return quotient_pdf_type::create(qp.quotient(), qp.pdf()); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotientAndWeight(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic, _cache); } }; diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index 4b356a1ecd..137ab211cf 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -24,6 +24,9 @@ struct SOrenNayarBase BXDF_CONFIG_TYPE_ALIASES(Config); using random_type = conditional_t; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; @@ -60,19 +63,19 @@ struct SOrenNayarBase return hlsl::promote(NdotL * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF) * __rec_pi_factored_out_wo_clamps(query.getVdotL(), NdotL, interaction.getNdotV(_clamp))); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); return quotient_pdf_type::create(__eval(query, _sample, interaction), forwardPdf(_sample, interaction)); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return evalAndWeight(_sample, interaction.isotropic); + return evalAndWeight(_sample, interaction.isotropic, _cache); } template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedHemisphere::cache_type cache; ray_dir_info_type L; @@ -80,7 +83,7 @@ struct SOrenNayarBase return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } template > - enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedSphere::cache_type cache; vector3_type _u = u; @@ -88,9 +91,9 @@ struct SOrenNayarBase L.setDirection(sampling::ProjectedSphere::generate(_u, cache)); return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace()); } - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return generate(anisotropic_interaction_type::create(interaction), u); + return generate(anisotropic_interaction_type::create(interaction), u, _cache); } scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC @@ -112,15 +115,15 @@ struct SOrenNayarBase scalar_type q = __rec_pi_factored_out_wo_clamps(query.getVdotL(), _sample.getNdotL(_clamp), interaction.getNdotV(_clamp)); return quotient_pdf_type::create(q, _pdf); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); return __quotientAndWeight(query, _sample, interaction); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotientAndWeight(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic, _cache); } scalar_type A2; diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index e9e103e203..494dcdd0c2 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -900,6 +900,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE)(T::random_type)) @@ -907,147 +908,12 @@ NBL_CONCEPT_END( #undef bxdf #include -#define NBL_CONCEPT_NAME bxdf_common -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -#define NBL_CONCEPT_TPLT_PRM_NAMES (T) -#define NBL_CONCEPT_PARAM_0 (bxdf, T) -#define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) -#define NBL_CONCEPT_PARAM_2 (aniso, typename T::anisotropic_interaction_type) -NBL_CONCEPT_BEGIN(3) -#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) -); -#undef aniso -#undef _sample -#undef bxdf -#include - -#define NBL_CONCEPT_NAME iso_bxdf_common -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -#define NBL_CONCEPT_TPLT_PRM_NAMES (T) -#define NBL_CONCEPT_PARAM_0 (bxdf, T) -#define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) -#define NBL_CONCEPT_PARAM_2 (iso, typename T::isotropic_interaction_type) -NBL_CONCEPT_BEGIN(3) -#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) - ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) -); -#undef iso -#undef _sample -#undef bxdf -#include - template NBL_BOOL_CONCEPT VecDim2OrMore = vector_traits::Dimension >= 2; template NBL_BOOL_CONCEPT VecDim3OrMore = vector_traits::Dimension >= 3; -} - -#define NBL_CONCEPT_NAME BRDF -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -#define NBL_CONCEPT_TPLT_PRM_NAMES (T) -#define NBL_CONCEPT_PARAM_0 (bxdf, T) -#define NBL_CONCEPT_PARAM_1 (aniso, typename T::anisotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) -NBL_CONCEPT_BEGIN(3) -#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::bxdf_common, T)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim2OrMore, typename T::random_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) -); -#undef u -#undef aniso -#undef bxdf -#include -#define NBL_CONCEPT_NAME BSDF -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -#define NBL_CONCEPT_TPLT_PRM_NAMES (T) -#define NBL_CONCEPT_PARAM_0 (bxdf, T) -#define NBL_CONCEPT_PARAM_1 (aniso, typename T::anisotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) -NBL_CONCEPT_BEGIN(3) -#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::bxdf_common, T)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim3OrMore, typename T::random_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) -); -#undef u -#undef aniso -#undef bxdf -#include - -#define NBL_CONCEPT_NAME IsotropicBRDF -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -#define NBL_CONCEPT_TPLT_PRM_NAMES (T) -#define NBL_CONCEPT_PARAM_0 (bxdf, T) -#define NBL_CONCEPT_PARAM_1 (iso, typename T::isotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) -NBL_CONCEPT_BEGIN(3) -#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::iso_bxdf_common, T)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim2OrMore, typename T::random_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) -); -#undef u -#undef iso -#undef bxdf -#include - -#define NBL_CONCEPT_NAME IsotropicBSDF -#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) -#define NBL_CONCEPT_TPLT_PRM_NAMES (T) -#define NBL_CONCEPT_PARAM_0 (bxdf, T) -#define NBL_CONCEPT_PARAM_1 (iso, typename T::isotropic_interaction_type) -#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) -NBL_CONCEPT_BEGIN(3) -#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 -#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 -#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::iso_bxdf_common, T)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim3OrMore, typename T::random_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type)) -); -#undef u -#undef iso -#undef bxdf -#include - -template -NBL_BOOL_CONCEPT BxDF = BRDF || BSDF; -template -NBL_BOOL_CONCEPT IsotropicBxDF = IsotropicBRDF || IsotropicBSDF; - - -namespace impl -{ -#define NBL_CONCEPT_NAME microfacet_bxdf_common +#define NBL_CONCEPT_NAME bxdf_common #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) @@ -1061,14 +927,12 @@ NBL_CONCEPT_BEGIN(4) #define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) - ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) ); #undef anisocache #undef aniso @@ -1076,7 +940,7 @@ NBL_CONCEPT_END( #undef bxdf #include -#define NBL_CONCEPT_NAME iso_microfacet_bxdf_common +#define NBL_CONCEPT_NAME iso_bxdf_common #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) @@ -1089,14 +953,13 @@ NBL_CONCEPT_BEGIN(4) #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(microfacet_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) ); #undef isocache #undef iso @@ -1105,7 +968,7 @@ NBL_CONCEPT_END( #include } -#define NBL_CONCEPT_NAME MicrofacetBRDF +#define NBL_CONCEPT_NAME BRDF #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) @@ -1118,7 +981,7 @@ NBL_CONCEPT_BEGIN(4) #define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::microfacet_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim2OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u,anisocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); @@ -1128,7 +991,7 @@ NBL_CONCEPT_END( #undef bxdf #include -#define NBL_CONCEPT_NAME MicrofacetBSDF +#define NBL_CONCEPT_NAME BSDF #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) @@ -1141,7 +1004,7 @@ NBL_CONCEPT_BEGIN(4) #define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::microfacet_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim3OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u,anisocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); @@ -1151,7 +1014,7 @@ NBL_CONCEPT_END( #undef bxdf #include -#define NBL_CONCEPT_NAME IsotropicMicrofacetBRDF +#define NBL_CONCEPT_NAME IsotropicBRDF #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) @@ -1164,7 +1027,7 @@ NBL_CONCEPT_BEGIN(4) #define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::iso_microfacet_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::iso_bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim2OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u,isocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); @@ -1174,7 +1037,7 @@ NBL_CONCEPT_END( #undef bxdf #include -#define NBL_CONCEPT_NAME IsotropicMicrofacetBSDF +#define NBL_CONCEPT_NAME IsotropicBSDF #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) #define NBL_CONCEPT_PARAM_0 (bxdf, T) @@ -1187,7 +1050,7 @@ NBL_CONCEPT_BEGIN(4) #define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::iso_microfacet_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::iso_bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim3OrMore, typename T::random_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u,isocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ); @@ -1197,8 +1060,22 @@ NBL_CONCEPT_END( #undef bxdf #include +template +NBL_BOOL_CONCEPT BxDF = BRDF || BSDF; +template +NBL_BOOL_CONCEPT IsotropicBxDF = IsotropicBRDF || IsotropicBSDF; + +template +NBL_BOOL_CONCEPT MicrofacetBRDF = BRDF && AnisotropicMicrofacetCache; +template +NBL_BOOL_CONCEPT MicrofacetBSDF = BSDF && AnisotropicMicrofacetCache; template NBL_BOOL_CONCEPT MicrofacetBxDF = MicrofacetBRDF || MicrofacetBSDF; + +template +NBL_BOOL_CONCEPT IsotropicMicrofacetBRDF = IsotropicBRDF && AnisotropicMicrofacetCache && CreatableIsotropicMicrofacetCache; +template +NBL_BOOL_CONCEPT IsotropicMicrofacetBSDF = IsotropicBSDF && AnisotropicMicrofacetCache && CreatableIsotropicMicrofacetCache; template NBL_BOOL_CONCEPT IsotropicMicrofacetBxDF = IsotropicMicrofacetBRDF || IsotropicMicrofacetBSDF; } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index 84c25fb4fb..c926096e59 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -24,19 +24,22 @@ struct SDeltaDistribution BXDF_CONFIG_TYPE_ALIASES(Config); using random_type = vector2_type; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { vector3_type V = interaction.getV().getDirection(); bxdf::Reflect r = bxdf::Reflect::create(V, interaction.getN()); @@ -49,9 +52,9 @@ struct SDeltaDistribution return s; } - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return generate(anisotropic_interaction_type::create(interaction), u); + return generate(anisotropic_interaction_type::create(interaction), u, _cache); } scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC @@ -63,14 +66,14 @@ struct SDeltaDistribution return 0; } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { const scalar_type _pdf = bit_cast(numeric_limits::infinity); return quotient_pdf_type::create(1.0, _pdf); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotientAndWeight(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic, _cache); } }; diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index 835b41a044..5666231e2d 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -24,19 +24,22 @@ struct SDeltaDistribution BXDF_CONFIG_TYPE_ALIASES(Config); using random_type = vector2_type; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { ray_dir_info_type L = interaction.getV().transmit(); sample_type s = sample_type::create(L, interaction.getN()); @@ -46,9 +49,9 @@ struct SDeltaDistribution s.NdotL2 = interaction.getNdotV2(); return s; } - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return generate(anisotropic_interaction_type::create(interaction), u); + return generate(anisotropic_interaction_type::create(interaction), u, _cache); } scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC @@ -60,12 +63,12 @@ struct SDeltaDistribution return 0; } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { const scalar_type _pdf = bit_cast(numeric_limits::infinity); return quotient_pdf_type::create(1.0, _pdf); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotientAndWeight(_sample, interaction.isotropic); } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index d3af6840ec..ad85be0c92 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -25,20 +25,23 @@ struct SSmoothDielectric BXDF_CONFIG_TYPE_ALIASES(Config); using random_type = vector3_type; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; // eval and weight return 0 because smooth dielectric/conductor BxDFs are dirac delta distributions, model perfectly specular objects that scatter light to only one outgoing direction - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(random_type) u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(random_type) u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta.value*orientedEta.value, interaction.getNdotV(_clamp))[0]; @@ -54,9 +57,9 @@ struct SSmoothDielectric ray_dir_info_type L = V.reflectRefract(rr, transmitted, orientedEta.rcp[0]); return sample_type::create(L, interaction.getT(), interaction.getB(), interaction.getN()); } - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(random_type) u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(random_type) u, NBL_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return generate(anisotropic_interaction_type::create(interaction), u); + return generate(anisotropic_interaction_type::create(interaction), u, _cache); } scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC @@ -69,13 +72,13 @@ struct SSmoothDielectric } // smooth BxDFs are isotropic by definition - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(1.0, bit_cast(numeric_limits::infinity)); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotientAndWeight(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic, _cache); } fresnel::OrientedEtas orientedEta; @@ -88,6 +91,9 @@ struct SThinSmoothDielectric BXDF_CONFIG_TYPE_ALIASES(Config); using random_type = vector3_type; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; @@ -100,11 +106,11 @@ struct SThinSmoothDielectric return retval; } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } @@ -135,14 +141,14 @@ struct SThinSmoothDielectric return sample_type::create(L, interaction.getT(), interaction.getB(), N); } - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { vector3_type dummy; return generate(interaction, u, dummy); } - sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u) NBL_CONST_MEMBER_FUNC + sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return generate(anisotropic_interaction_type::create(interaction), u); + return generate(anisotropic_interaction_type::create(interaction), u, _cache); } scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC @@ -155,7 +161,7 @@ struct SThinSmoothDielectric } // smooth BxDFs are isotropic by definition - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(interaction.getNdotV(), _sample.getNdotL()); const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel(interaction.getNdotV(_clamp))); @@ -166,9 +172,9 @@ struct SThinSmoothDielectric const scalar_type _pdf = bit_cast(numeric_limits::infinity); return quotient_pdf_type::create(sampleValue / sampleProb, _pdf); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotientAndWeight(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic, _cache); } fresnel::Dielectric fresnel; From dab4a18431ff9d319198dbf1253b064bd84fbfa1 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 24 Mar 2026 16:56:56 +0700 Subject: [PATCH 39/55] eval should have its own cache type in concepts --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 1 + .../builtin/hlsl/bxdf/base/lambertian.hlsl | 5 +++-- .../builtin/hlsl/bxdf/base/oren_nayar.hlsl | 5 +++-- include/nbl/builtin/hlsl/bxdf/common.hlsl | 20 ++++++++++++------- .../bxdf/reflection/delta_distribution.hlsl | 5 +++-- .../bxdf/transmission/delta_distribution.hlsl | 5 +++-- .../bxdf/transmission/smooth_dielectric.hlsl | 10 ++++++---- 7 files changed, 32 insertions(+), 19 deletions(-) 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 f243d717ac..e9b3d0ee54 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -85,6 +85,7 @@ struct SCookTorrance NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = ndf_type::SupportedPaths != ndf::MTT_REFLECT; using random_type = conditional_t; NBL_HLSL_BXDF_ANISOTROPIC_COND_DECLS(IsAnisotropic); + using evalcache_type = conditional_t; // utility functions template, diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index 3b3dd21e59..2d4ae956ff 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -27,15 +27,16 @@ struct SLambertianBase struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; + using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { const spectral_type quo = hlsl::promote(_sample.getNdotL(_clamp) * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF)); return quotient_pdf_type::create(quo, forwardPdf(_sample, interaction)); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return evalAndWeight(_sample, interaction.isotropic, _cache); } diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index 137ab211cf..d6f2e6e6c8 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -27,6 +27,7 @@ struct SOrenNayarBase struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; + using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; @@ -63,13 +64,13 @@ struct SOrenNayarBase return hlsl::promote(NdotL * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF) * __rec_pi_factored_out_wo_clamps(query.getVdotL(), NdotL, interaction.getNdotV(_clamp))); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); return quotient_pdf_type::create(__eval(query, _sample, interaction), forwardPdf(_sample, interaction)); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return evalAndWeight(_sample, interaction.isotropic, _cache); } diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 494dcdd0c2..bfe1bf7aa8 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -889,7 +889,6 @@ namespace bxdf_concepts { namespace impl { - #define NBL_CONCEPT_NAME bxdf_common_typdefs #define NBL_CONCEPT_TPLT_PRM_KINDS (typename) #define NBL_CONCEPT_TPLT_PRM_NAMES (T) @@ -901,6 +900,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::evalcache_type)) ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE)(T::random_type)) @@ -920,20 +920,23 @@ NBL_BOOL_CONCEPT VecDim3OrMore = vector_traits::Dimension >= 3; #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) #define NBL_CONCEPT_PARAM_2 (aniso, typename T::anisotropic_interaction_type) #define NBL_CONCEPT_PARAM_3 (anisocache, typename T::anisocache_type) -NBL_CONCEPT_BEGIN(4) +#define NBL_CONCEPT_PARAM_4 (evalcache, typename T::evalcache_type) +NBL_CONCEPT_BEGIN(5) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define evalcache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, evalcache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) ); +#undef evalcache #undef anisocache #undef aniso #undef _sample @@ -947,20 +950,23 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) #define NBL_CONCEPT_PARAM_2 (iso, typename T::isotropic_interaction_type) #define NBL_CONCEPT_PARAM_3 (isocache, typename T::isocache_type) -NBL_CONCEPT_BEGIN(4) +#define NBL_CONCEPT_PARAM_4 (evalcache, typename T::evalcache_type) +NBL_CONCEPT_BEGIN(5) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define evalcache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval_and_weight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, evalcache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_weight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) ); +#undef evalcache #undef isocache #undef iso #undef _sample diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index c926096e59..07c75930e1 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -27,14 +27,15 @@ struct SDeltaDistribution struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; + using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index 5666231e2d..719d08dfcb 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -27,14 +27,15 @@ struct SDeltaDistribution struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; + using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index ad85be0c92..1835d4c1be 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -28,15 +28,16 @@ struct SSmoothDielectric struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; + using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; // eval and weight return 0 because smooth dielectric/conductor BxDFs are dirac delta distributions, model perfectly specular objects that scatter light to only one outgoing direction - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } @@ -94,6 +95,7 @@ struct SThinSmoothDielectric struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; + using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; @@ -106,11 +108,11 @@ struct SThinSmoothDielectric return retval; } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotient_pdf_type::create(0.0, 0.0); } From fb878a610b61dade047810f3f90d90910db7d621 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 25 Mar 2026 10:49:03 +0700 Subject: [PATCH 40/55] minor typo bug fixes --- include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl | 2 +- include/nbl/builtin/hlsl/bxdf/common.hlsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index 2d4ae956ff..e5948030e2 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -82,7 +82,7 @@ struct SLambertianBase qp = sampling::ProjectedSphere::template quotientAndPdf(_sample.getNdotL(_clamp)); else qp = sampling::ProjectedHemisphere::template quotientAndPdf(_sample.getNdotL(_clamp)); - return quotient_pdf_type::create(qp.quotient(), qp.pdf()); + return quotient_pdf_type::create(qp.quotient()[0], qp.pdf()); } quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index bfe1bf7aa8..cd548bf4e1 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -956,7 +956,7 @@ NBL_CONCEPT_BEGIN(5) #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 -#define evalcache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define evalcache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) From 4171059716089ebcc93b54ac6419f60a03b90917 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 25 Mar 2026 14:44:38 +0700 Subject: [PATCH 41/55] eval returns a value_and_weight type, fixed some bugs in bxdf concept --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 14 +-- .../builtin/hlsl/bxdf/base/lambertian.hlsl | 6 +- .../builtin/hlsl/bxdf/base/oren_nayar.hlsl | 6 +- include/nbl/builtin/hlsl/bxdf/common.hlsl | 8 +- include/nbl/builtin/hlsl/bxdf/config.hlsl | 4 + .../bxdf/reflection/delta_distribution.hlsl | 8 +- .../bxdf/transmission/delta_distribution.hlsl | 8 +- .../bxdf/transmission/smooth_dielectric.hlsl | 16 ++-- .../builtin/hlsl/path_tracing/concepts.hlsl | 3 +- .../hlsl/path_tracing/unidirectional.hlsl | 9 +- .../hlsl/sampling/value_and_weight.hlsl | 91 +++++++++++++++++++ src/nbl/builtin/CMakeLists.txt | 1 + 12 files changed, 136 insertions(+), 38 deletions(-) create mode 100644 include/nbl/builtin/hlsl/sampling/value_and_weight.hlsl 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 e9b3d0ee54..f43e242efc 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -158,11 +158,11 @@ struct SCookTorrance template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC { fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); if (!__checkValid(_f, _sample, interaction, cache)) - return quotient_pdf_type::create(scalar_type(0.0), scalar_type(0.0)); + return value_weight_type::create(scalar_type(0.0), scalar_type(0.0)); bool isInfinity; scalar_type _pdf = __forwardPdf(_sample, interaction, cache, isInfinity); @@ -183,22 +183,22 @@ struct SCookTorrance // immediately return only after all calls setting DG // allows compiler to throw away calls to ndf.D if using __overwriteDG, before that we only avoid computation for G2(correlated) if (isInfinity) - return quotient_pdf_type::create(scalar_type(0.0), scalar_type(0.0)); + return value_weight_type::create(scalar_type(0.0), scalar_type(0.0)); scalar_type clampedVdotH = cache.getVdotH(); NBL_IF_CONSTEXPR(IsBSDF) clampedVdotH = hlsl::abs(clampedVdotH); - spectral_type quo; + spectral_type eval; NBL_IF_CONSTEXPR(IsBSDF) { const spectral_type reflectance = impl::__implicit_promote::__call(_f(clampedVdotH)); - quo = hlsl::mix(reflectance, hlsl::promote(1.0) - reflectance, cache.isTransmission()) * DG; + eval = hlsl::mix(reflectance, hlsl::promote(1.0) - reflectance, cache.isTransmission()) * DG; } else - quo = impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; + eval = impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; - return quotient_pdf_type::create(quo, _pdf); + return value_weight_type::create(eval, _pdf); } sample_type __generate_common(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type localH, diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index e5948030e2..d79f191b60 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -31,12 +31,12 @@ struct SLambertianBase NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { const spectral_type quo = hlsl::promote(_sample.getNdotL(_clamp) * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF)); - return quotient_pdf_type::create(quo, forwardPdf(_sample, interaction)); + return value_weight_type::create(quo, forwardPdf(_sample, interaction)); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return evalAndWeight(_sample, interaction.isotropic, _cache); } diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index d6f2e6e6c8..cea334a5e8 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -64,13 +64,13 @@ struct SOrenNayarBase return hlsl::promote(NdotL * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF) * __rec_pi_factored_out_wo_clamps(query.getVdotL(), NdotL, interaction.getNdotV(_clamp))); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); - return quotient_pdf_type::create(__eval(query, _sample, interaction), forwardPdf(_sample, interaction)); + return value_weight_type::create(__eval(query, _sample, interaction), forwardPdf(_sample, interaction)); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { return evalAndWeight(_sample, interaction.isotropic, _cache); } diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index cd548bf4e1..42e2fd6058 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -15,6 +15,7 @@ #include "nbl/builtin/hlsl/cpp_compat/promote.hlsl" #include "nbl/builtin/hlsl/bxdf/fresnel.hlsl" #include "nbl/builtin/hlsl/sampling/quotient_and_pdf.hlsl" +#include "nbl/builtin/hlsl/sampling/value_and_weight.hlsl" #include "nbl/builtin/hlsl/vector_utils/vector_traits.hlsl" namespace nbl @@ -903,6 +904,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::evalcache_type)) ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::value_weight_type)) ((NBL_CONCEPT_REQ_TYPE)(T::random_type)) ); #undef bxdf @@ -929,8 +931,7 @@ NBL_CONCEPT_BEGIN(5) #define evalcache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, evalcache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, evalcache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) @@ -961,8 +962,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, evalcache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.pdf(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, evalcache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) ); diff --git a/include/nbl/builtin/hlsl/bxdf/config.hlsl b/include/nbl/builtin/hlsl/bxdf/config.hlsl index 0d49d45b3f..3141914242 100644 --- a/include/nbl/builtin/hlsl/bxdf/config.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/config.hlsl @@ -32,6 +32,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::value_weight_type)) ); #undef conf #include @@ -76,6 +77,7 @@ struct SConfiguration using sample_type = LS; using spectral_type = Spectrum; using quotient_pdf_type = sampling::quotient_and_pdf; + using value_weight_type = sampling::value_and_weight; }; #define CONF_ANISO LightSample && surface_interactions::Anisotropic && concepts::FloatingPointLikeVectorial @@ -98,6 +100,7 @@ struct SConfiguration using sample_type = LS; using spectral_type = Spectrum; using quotient_pdf_type = sampling::quotient_and_pdf; + using value_weight_type = sampling::value_and_weight; }; template @@ -150,6 +153,7 @@ NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config);\ NBL_BXDF_CONFIG_ALIAS(sample_type, Config);\ NBL_BXDF_CONFIG_ALIAS(spectral_type, Config);\ NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config);\ +NBL_BXDF_CONFIG_ALIAS(value_weight_type, Config);\ #define MICROFACET_BXDF_CONFIG_TYPE_ALIASES(Config) BXDF_CONFIG_TYPE_ALIASES(Config);\ NBL_BXDF_CONFIG_ALIAS(matrix3x3_type, Config);\ diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index 07c75930e1..7de8d56152 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -31,13 +31,13 @@ struct SDeltaDistribution NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotient_pdf_type::create(0.0, 0.0); + return value_weight_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotient_pdf_type::create(0.0, 0.0); + return value_weight_type::create(0.0, 0.0); } sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index 719d08dfcb..f44e313b9c 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -31,13 +31,13 @@ struct SDeltaDistribution NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotient_pdf_type::create(0.0, 0.0); + return value_weight_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotient_pdf_type::create(0.0, 0.0); + return value_weight_type::create(0.0, 0.0); } sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index 1835d4c1be..08f54950a1 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -33,13 +33,13 @@ struct SSmoothDielectric NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; // eval and weight return 0 because smooth dielectric/conductor BxDFs are dirac delta distributions, model perfectly specular objects that scatter light to only one outgoing direction - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotient_pdf_type::create(0.0, 0.0); + return value_weight_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotient_pdf_type::create(0.0, 0.0); + return value_weight_type::create(0.0, 0.0); } sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(random_type) u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC @@ -108,13 +108,13 @@ struct SThinSmoothDielectric return retval; } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotient_pdf_type::create(0.0, 0.0); + return value_weight_type::create(0.0, 0.0); } - quotient_pdf_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotient_pdf_type::create(0.0, 0.0); + return value_weight_type::create(0.0, 0.0); } // usually `luminosityContributionHint` would be the Rec.709 luma coefficients (the Y row of the RGB to CIE XYZ matrix) diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 438f0391de..0ac5c9166a 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -176,13 +176,14 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::material_id_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::value_weight_type)) ((NBL_CONCEPT_REQ_TYPE)(T::measure_type)) ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::cache_type)) ((NBL_CONCEPT_REQ_TYPE)(T::bxdfnode_type)) ((NBL_CONCEPT_REQ_TYPE)(T::create_params_t)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(BxdfNode, typename T::bxdfnode_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.evalAndWeight(matid, _sample, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.evalAndWeight(matid, _sample, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.generate(matid, aniso_inter, u, cache_)), ::nbl::hlsl::is_same_v, typename T::sample_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotientAndWeight(matid, _sample, aniso_inter, cache_)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getBxDFNode(matid, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::bxdfnode_type)) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 1752e9c768..d75ebeae39 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -46,7 +46,8 @@ struct Unidirectional using bxdfnode_type = typename MaterialSystem::bxdfnode_type; using anisotropic_interaction_type = typename MaterialSystem::anisotropic_interaction_type; using cache_type = typename MaterialSystem::cache_type; - using quotient_pdf_type = typename NextEventEstimator::quotient_pdf_type; + using quotient_pdf_type = typename MaterialSystem::quotient_pdf_type; + using value_weight_type = typename MaterialSystem::value_weight_type; using tolerance_method_type = typename NextEventEstimator::tolerance_method_type; scalar_type getLuma(NBL_CONST_REF_ARG(vector3_type) col) @@ -108,11 +109,11 @@ struct Unidirectional // This stops a discrepancy in MIS weights and NEE mistakenly trying to add non-delta lobe contributions with a MIS weight > 0 and creating energy from thin air. if (neeContrib.pdf() > scalar_type(0.0)) { - quotient_pdf_type bsdfContrib = materialSystem.evalAndWeight(matID, nee_sample, interaction); - neeContrib._quotient *= bsdfContrib.quotient() * rcpChoiceProb; + value_weight_type bsdfContrib = materialSystem.evalAndWeight(matID, nee_sample, interaction); + neeContrib._quotient *= bsdfContrib.value() * rcpChoiceProb; if (neeContrib.pdf() < bit_cast(numeric_limits::infinity)) { - const scalar_type otherGenOverLightAndChoice = bsdfContrib.pdf() * rcpChoiceProb / neeContrib.pdf(); + const scalar_type otherGenOverLightAndChoice = bsdfContrib.weight() * rcpChoiceProb / neeContrib.pdf(); neeContrib._quotient /= 1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice; // balance heuristic } diff --git a/include/nbl/builtin/hlsl/sampling/value_and_weight.hlsl b/include/nbl/builtin/hlsl/sampling/value_and_weight.hlsl new file mode 100644 index 0000000000..f1a9e2462a --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/value_and_weight.hlsl @@ -0,0 +1,91 @@ +// Copyright (C) 2018-2026 - 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_VALUE_AND_WEIGHT_INCLUDED_ +#define _NBL_BUILTIN_HLSL_SAMPLING_VALUE_AND_WEIGHT_INCLUDED_ + +#include "nbl/builtin/hlsl/sampling/value_and_pdf.hlsl" + +namespace nbl +{ +namespace hlsl +{ +namespace sampling +{ + +template +struct value_and_rcpWeight +{ + using this_t = value_and_rcpWeight; + using scalar_v = typename vector_traits::scalar_type; + + static this_t create(const V _value, const W _rcpWeight) + { + this_t retval; + retval._value = _value; + retval._rcpWeight = _rcpWeight; + return retval; + } + + static this_t create(const scalar_v _value, const W _rcpWeight) + { + this_t retval; + retval._value = hlsl::promote(_value); + retval._rcpWeight = _rcpWeight; + return retval; + } + + V value() { return _value; } + W rcpWeight() { return _rcpWeight; } + + V _value; + W _rcpWeight; +}; + +template +struct value_and_weight +{ + using this_t = value_and_weight; + using scalar_v = typename vector_traits::scalar_type; + + static this_t create(const V _value, const W _weight) + { + this_t retval; + retval._value = _value; + retval._weight = _weight; + return retval; + } + + static this_t create(const scalar_v _value, const W _weight) + { + this_t retval; + retval._value = hlsl::promote(_value); + retval._weight = _weight; + return retval; + } + + V value() { return _value; } + W weight() { return _weight; } + + V _value; + W _weight; +}; + +template +using codomain_and_rcpWeight = value_and_rcpWeight; + +template +using codomain_and_weight = value_and_weight; + +template +using domain_and_rcpWeight = value_and_rcpWeight; + +template +using domain_and_weight = value_and_weight; + +} // namespace sampling +} // namespace hlsl +} // namespace nbl + +#endif diff --git a/src/nbl/builtin/CMakeLists.txt b/src/nbl/builtin/CMakeLists.txt index 839c5e3778..2057e56ba4 100644 --- a/src/nbl/builtin/CMakeLists.txt +++ b/src/nbl/builtin/CMakeLists.txt @@ -281,6 +281,7 @@ LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/spherical_rectangle. 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/value_and_pdf.hlsl") +LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/value_and_weight.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/concepts.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/uniform_spheres.hlsl") LIST_BUILTIN_RESOURCE(NBL_RESOURCES_TO_EMBED "hlsl/sampling/alias_table.hlsl") From 0e16279073ad2e9d05137022c2292bd7269606f4 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 25 Mar 2026 15:06:45 +0700 Subject: [PATCH 42/55] made value_and_pdf compose from value_and_weight --- .../builtin/hlsl/sampling/value_and_pdf.hlsl | 26 ++++++++++--------- .../hlsl/sampling/value_and_weight.hlsl | 2 -- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/nbl/builtin/hlsl/sampling/value_and_pdf.hlsl b/include/nbl/builtin/hlsl/sampling/value_and_pdf.hlsl index a037c0e3d8..ac7e448295 100644 --- a/include/nbl/builtin/hlsl/sampling/value_and_pdf.hlsl +++ b/include/nbl/builtin/hlsl/sampling/value_and_pdf.hlsl @@ -5,6 +5,8 @@ #ifndef _NBL_BUILTIN_HLSL_SAMPLING_VALUE_AND_PDF_INCLUDED_ #define _NBL_BUILTIN_HLSL_SAMPLING_VALUE_AND_PDF_INCLUDED_ +#include "nbl/builtin/hlsl/sampling/value_and_weight.hlsl" + namespace nbl { namespace hlsl @@ -16,40 +18,40 @@ template struct value_and_rcpPdf { using this_t = value_and_rcpPdf; + using base_t = value_and_rcpWeight; static this_t create(const V _value, const P _rcpPdf) { this_t retval; - retval._value = _value; - retval._rcpPdf = _rcpPdf; + retval._base._value = _value; + retval._base._rcpWeight = _rcpPdf; return retval; } - V value() { return _value; } - P rcpPdf() { return _rcpPdf; } + V value() { return _base._value; } + P rcpPdf() { return _base._rcpWeight; } - V _value; - P _rcpPdf; + base_t _base; }; template struct value_and_pdf { using this_t = value_and_pdf; + using base_t = value_and_weight; static this_t create(const V _value, const P _pdf) { this_t retval; - retval._value = _value; - retval._pdf = _pdf; + retval._base._value = _value; + retval._base._weight = _pdf; return retval; } - V value() { return _value; } - P pdf() { return _pdf; } + V value() { return _base._value; } + P pdf() { return _base._weight; } - V _value; - P _pdf; + base_t _base; }; // Returned by TractableSampler::generate, codomain sample bundled with its rcpPdf diff --git a/include/nbl/builtin/hlsl/sampling/value_and_weight.hlsl b/include/nbl/builtin/hlsl/sampling/value_and_weight.hlsl index f1a9e2462a..19711174c5 100644 --- a/include/nbl/builtin/hlsl/sampling/value_and_weight.hlsl +++ b/include/nbl/builtin/hlsl/sampling/value_and_weight.hlsl @@ -5,8 +5,6 @@ #ifndef _NBL_BUILTIN_HLSL_SAMPLING_VALUE_AND_WEIGHT_INCLUDED_ #define _NBL_BUILTIN_HLSL_SAMPLING_VALUE_AND_WEIGHT_INCLUDED_ -#include "nbl/builtin/hlsl/sampling/value_and_pdf.hlsl" - namespace nbl { namespace hlsl From 345865f6537182c873ed2933be61fd5429f17e71 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 25 Mar 2026 16:34:56 +0700 Subject: [PATCH 43/55] reuse quant query and other stuff from forwardPdf in quotient and eval methods --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 105 ++++++++++-------- 1 file changed, 56 insertions(+), 49 deletions(-) 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 f43e242efc..7626d1d3ba 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -87,6 +87,16 @@ struct SCookTorrance NBL_HLSL_BXDF_ANISOTROPIC_COND_DECLS(IsAnisotropic); using evalcache_type = conditional_t; + struct PdfQuery + { + scalar_type pdf; + fresnel_type orientedFresnel; + typename ndf_type::quant_query_type quantQuery; + + spectral_type reflectance; + scalar_type scaled_reflectance; + }; + // utility functions template, class MicrofacetCache=conditional_t @@ -160,19 +170,20 @@ struct SCookTorrance NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC { - fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); - if (!__checkValid(_f, _sample, interaction, cache)) + PdfQuery pdfQuery = __forwardPdf(_sample, interaction, cache); + scalar_type _pdf = pdfQuery.pdf; + if (_pdf == scalar_type(0.0)) return value_weight_type::create(scalar_type(0.0), scalar_type(0.0)); - bool isInfinity; - scalar_type _pdf = __forwardPdf(_sample, interaction, cache, isInfinity); + fresnel_type _f = pdfQuery.orientedFresnel; using quant_query_type = typename ndf_type::quant_query_type; - quant_query_type qq = impl::quant_query_helper::template __call(ndf, _f, interaction, cache); + quant_query_type qq = pdfQuery.quantQuery; using g2g1_query_type = typename ndf_type::g2g1_query_type; g2g1_query_type gq = ndf.template createG2G1Query(_sample, interaction); + bool isInfinity; quant_type D = ndf.template D(qq, _sample, interaction, cache, isInfinity); scalar_type DG = D.projectedLightMeasure; if (!isInfinity) @@ -347,41 +358,48 @@ struct SCookTorrance return s; } - template - scalar_type __forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache, NBL_REF_ARG(bool) isInfinity) NBL_CONST_MEMBER_FUNC + template + PdfQuery __forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC { - using quant_query_type = typename ndf_type::quant_query_type; - using dg1_query_type = typename ndf_type::dg1_query_type; + PdfQuery query; + query.pdf = scalar_type(0.0); + query.orientedFresnel = __getOrientedFresnel(fresnel, interaction.getNdotV()); + const bool cacheIsValid = __checkValid(query.orientedFresnel, _sample, interaction, cache); + assert(!FromGenerator || cacheIsValid); + const bool isValid = FromGenerator ? _sample.isValid() : cacheIsValid; // when from generator, expect the generated sample to always be valid, different checks for brdf and btdf + + if (isValid) + { + using dg1_query_type = typename ndf_type::dg1_query_type; + dg1_query_type dq = ndf.template createDG1Query(interaction, cache); - dg1_query_type dq = ndf.template createDG1Query(interaction, cache); + bool isInfinity; + query.quantQuery = impl::quant_query_helper::template __call(ndf, query.orientedFresnel, interaction, cache); + quant_type DG1 = ndf.template DG1(dq, query.quantQuery, _sample, interaction, isInfinity); - fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); - quant_query_type qq = impl::quant_query_helper::template __call(ndf, _f, interaction, cache); - quant_type DG1 = ndf.template DG1(dq, qq, _sample, interaction, isInfinity); + if (isInfinity) + return query; - NBL_IF_CONSTEXPR(IsBSDF) - { - spectral_type dummy; - const scalar_type reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(cache.getVdotH()), cache.isTransmission(), dummy); - return reflectance * DG1.projectedLightMeasure; - } - else - { - return DG1.projectedLightMeasure; + NBL_IF_CONSTEXPR(IsBSDF) + { + query.scaled_reflectance = __getScaledReflectance(query.orientedFresnel, interaction, hlsl::abs(cache.getVdotH()), cache.isTransmission(), query.reflectance); + query.pdf = query.scaled_reflectance * DG1.projectedLightMeasure; + } + else + { + query.pdf = DG1.projectedLightMeasure; + } } + + return query; } template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) scalar_type forwardPdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC { - fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); - if (!__checkValid(_f, _sample, interaction, cache)) - return scalar_type(0.0); - - bool isInfinity; - scalar_type _pdf = __forwardPdf(_sample, interaction, cache, isInfinity); - return hlsl::mix(_pdf, scalar_type(0.0), isInfinity); + PdfQuery query = __forwardPdf(_sample, interaction, cache); + return query.pdf; } template, @@ -389,23 +407,16 @@ struct SCookTorrance NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC { - if (!_sample.isValid()) - return quotient_pdf_type::create(scalar_type(0.0), scalar_type(0.0)); // set pdf=0 when quo=0 because we don't want to give high weight to sampling strategy that yields 0 contribution - - bool isInfinity; - scalar_type _pdf = __forwardPdf(_sample, interaction, cache, isInfinity); - fresnel_type _f = __getOrientedFresnel(fresnel, interaction.getNdotV()); + PdfQuery pdfQuery = __forwardPdf(_sample, interaction, cache); + scalar_type _pdf = pdfQuery.pdf; + if (_pdf == scalar_type(0.0)) + return quotient_pdf_type::create(scalar_type(0.0), scalar_type(0.0)); - const bool valid = __checkValid(_f, _sample, interaction, cache); - assert(valid); // expect the generated sample to always be valid, different checks for brdf and btdf + fresnel_type _f = pdfQuery.orientedFresnel; - scalar_type G2_over_G1 = scalar_type(1.0); - if (!isInfinity) - { - using g2g1_query_type = typename N::g2g1_query_type; - g2g1_query_type gq = ndf.template createG2G1Query(_sample, interaction); - G2_over_G1 = ndf.template G2_over_G1(gq, _sample, interaction, cache); - } + using g2g1_query_type = typename N::g2g1_query_type; + g2g1_query_type gq = ndf.template createG2G1Query(_sample, interaction); + scalar_type G2_over_G1 = ndf.template G2_over_G1(gq, _sample, interaction, cache); spectral_type quo; NBL_IF_CONSTEXPR(IsBSDF) @@ -413,11 +424,7 @@ struct SCookTorrance NBL_IF_CONSTEXPR(fresnel_type::ReturnsMonochrome) quo = hlsl::promote(G2_over_G1); else - { - 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; - } + quo = pdfQuery.reflectance / pdfQuery.scaled_reflectance * G2_over_G1; } else { From a98cd4512471a8d825ce242178aa9a127e0130d7 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 25 Mar 2026 17:05:03 +0700 Subject: [PATCH 44/55] reuse reflectance from forwardpdf in eval --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) 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 7626d1d3ba..9d86777222 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -155,7 +155,9 @@ struct SCookTorrance 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) { scalar_type reflectance = orientedFresnel(clampedVdotH)[0]; - return hlsl::mix(reflectance, scalar_type(1.0)-reflectance, transmitted); + reflectance = hlsl::mix(reflectance, scalar_type(1.0)-reflectance, transmitted); + outFresnelVal = hlsl::promote(reflectance); + return reflectance; } bool __dotIsValue(const vector3_type a, const vector3_type b, const scalar_type value) NBL_CONST_MEMBER_FUNC @@ -202,12 +204,10 @@ struct SCookTorrance spectral_type eval; NBL_IF_CONSTEXPR(IsBSDF) - { - const spectral_type reflectance = impl::__implicit_promote::__call(_f(clampedVdotH)); - eval = hlsl::mix(reflectance, hlsl::promote(1.0) - reflectance, cache.isTransmission()) * DG; - } + eval = pdfQuery.reflectance; else - eval = impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; + eval = impl::__implicit_promote::__call(_f(clampedVdotH)); + eval *= DG; return value_weight_type::create(eval, _pdf); } @@ -378,7 +378,10 @@ struct SCookTorrance quant_type DG1 = ndf.template DG1(dq, query.quantQuery, _sample, interaction, isInfinity); if (isInfinity) + { + query.pdf = bit_cast(numeric_limits::infinity); return query; + } NBL_IF_CONSTEXPR(IsBSDF) { From d0ed7d3855c39d52d0fb99d31edec414552682ab Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 26 Mar 2026 10:41:48 +0700 Subject: [PATCH 45/55] renamed quotient_pdf type to quotient_weight type --- .../builtin/hlsl/bxdf/base/cook_torrance_base.hlsl | 6 +++--- include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl | 6 +++--- include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl | 8 ++++---- include/nbl/builtin/hlsl/bxdf/common.hlsl | 6 +++--- include/nbl/builtin/hlsl/bxdf/config.hlsl | 8 ++++---- .../hlsl/bxdf/reflection/delta_distribution.hlsl | 6 +++--- .../hlsl/bxdf/transmission/delta_distribution.hlsl | 6 +++--- .../hlsl/bxdf/transmission/smooth_dielectric.hlsl | 12 ++++++------ include/nbl/builtin/hlsl/path_tracing/concepts.hlsl | 10 +++++----- .../builtin/hlsl/path_tracing/unidirectional.hlsl | 6 +++--- 10 files changed, 37 insertions(+), 37 deletions(-) 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 9d86777222..47e9ea90ca 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -408,12 +408,12 @@ struct SCookTorrance template, class MicrofacetCache=conditional_t NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC { PdfQuery pdfQuery = __forwardPdf(_sample, interaction, cache); scalar_type _pdf = pdfQuery.pdf; if (_pdf == scalar_type(0.0)) - return quotient_pdf_type::create(scalar_type(0.0), scalar_type(0.0)); + return quotient_weight_type::create(scalar_type(0.0), scalar_type(0.0)); fresnel_type _f = pdfQuery.orientedFresnel; @@ -436,7 +436,7 @@ struct SCookTorrance quo = _f(VdotH) * G2_over_G1; } - return quotient_pdf_type::create(quo, _pdf); + return quotient_weight_type::create(quo, _pdf); } ndf_type ndf; diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index d79f191b60..f2ba2e259a 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -75,16 +75,16 @@ struct SLambertianBase return forwardPdf(_sample, interaction.isotropic); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { sampling::quotient_and_pdf qp; NBL_IF_CONSTEXPR (IsBSDF) qp = sampling::ProjectedSphere::template quotientAndPdf(_sample.getNdotL(_clamp)); else qp = sampling::ProjectedHemisphere::template quotientAndPdf(_sample.getNdotL(_clamp)); - return quotient_pdf_type::create(qp.quotient()[0], qp.pdf()); + return quotient_weight_type::create(qp.quotient()[0], qp.pdf()); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotientAndWeight(_sample, interaction.isotropic, _cache); } diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index cea334a5e8..f4a036ce98 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -110,19 +110,19 @@ struct SOrenNayarBase } template - quotient_pdf_type __quotientAndWeight(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + quotient_weight_type __quotientAndWeight(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { scalar_type _pdf = forwardPdf(_sample, interaction); scalar_type q = __rec_pi_factored_out_wo_clamps(query.getVdotL(), _sample.getNdotL(_clamp), interaction.getNdotV(_clamp)); - return quotient_pdf_type::create(q, _pdf); + return quotient_weight_type::create(q, _pdf); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); return __quotientAndWeight(query, _sample, interaction); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotientAndWeight(_sample, interaction.isotropic, _cache); } diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 42e2fd6058..628c153848 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -903,7 +903,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) ((NBL_CONCEPT_REQ_TYPE)(T::evalcache_type)) ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE)(T::value_weight_type)) ((NBL_CONCEPT_REQ_TYPE)(T::random_type)) ); @@ -932,7 +932,7 @@ NBL_CONCEPT_BEGIN(5) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, evalcache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) @@ -963,7 +963,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, evalcache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) ); #undef evalcache diff --git a/include/nbl/builtin/hlsl/bxdf/config.hlsl b/include/nbl/builtin/hlsl/bxdf/config.hlsl index 3141914242..25ddf20881 100644 --- a/include/nbl/builtin/hlsl/bxdf/config.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/config.hlsl @@ -31,7 +31,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE)(T::value_weight_type)) ); #undef conf @@ -76,7 +76,7 @@ struct SConfiguration using anisotropic_interaction_type = surface_interactions::SAnisotropic; using sample_type = LS; using spectral_type = Spectrum; - using quotient_pdf_type = sampling::quotient_and_pdf; + using quotient_weight_type = sampling::quotient_and_pdf; using value_weight_type = sampling::value_and_weight; }; @@ -99,7 +99,7 @@ struct SConfiguration using anisotropic_interaction_type = Interaction; using sample_type = LS; using spectral_type = Spectrum; - using quotient_pdf_type = sampling::quotient_and_pdf; + using quotient_weight_type = sampling::quotient_and_pdf; using value_weight_type = sampling::value_and_weight; }; @@ -152,7 +152,7 @@ NBL_BXDF_CONFIG_ALIAS(isotropic_interaction_type, Config);\ NBL_BXDF_CONFIG_ALIAS(anisotropic_interaction_type, Config);\ NBL_BXDF_CONFIG_ALIAS(sample_type, Config);\ NBL_BXDF_CONFIG_ALIAS(spectral_type, Config);\ -NBL_BXDF_CONFIG_ALIAS(quotient_pdf_type, Config);\ +NBL_BXDF_CONFIG_ALIAS(quotient_weight_type, Config);\ NBL_BXDF_CONFIG_ALIAS(value_weight_type, Config);\ #define MICROFACET_BXDF_CONFIG_TYPE_ALIASES(Config) BXDF_CONFIG_TYPE_ALIASES(Config);\ diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index 7de8d56152..eb9bbb6943 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -67,12 +67,12 @@ struct SDeltaDistribution return 0; } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { const scalar_type _pdf = bit_cast(numeric_limits::infinity); - return quotient_pdf_type::create(1.0, _pdf); + return quotient_weight_type::create(1.0, _pdf); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotientAndWeight(_sample, interaction.isotropic, _cache); } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index f44e313b9c..600405a533 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -64,12 +64,12 @@ struct SDeltaDistribution return 0; } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { const scalar_type _pdf = bit_cast(numeric_limits::infinity); - return quotient_pdf_type::create(1.0, _pdf); + return quotient_weight_type::create(1.0, _pdf); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotientAndWeight(_sample, interaction.isotropic); } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index 08f54950a1..d04c4cd821 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -73,11 +73,11 @@ struct SSmoothDielectric } // smooth BxDFs are isotropic by definition - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return quotient_pdf_type::create(1.0, bit_cast(numeric_limits::infinity)); + return quotient_weight_type::create(1.0, bit_cast(numeric_limits::infinity)); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotientAndWeight(_sample, interaction.isotropic, _cache); } @@ -163,7 +163,7 @@ struct SThinSmoothDielectric } // smooth BxDFs are isotropic by definition - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { const bool transmitted = ComputeMicrofacetNormal::isTransmissionPath(interaction.getNdotV(), _sample.getNdotL()); const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter(fresnel(interaction.getNdotV(_clamp))); @@ -172,9 +172,9 @@ struct SThinSmoothDielectric const scalar_type sampleProb = nbl::hlsl::dot(sampleValue,interaction.getLuminosityContributionHint()); const scalar_type _pdf = bit_cast(numeric_limits::infinity); - return quotient_pdf_type::create(sampleValue / sampleProb, _pdf); + return quotient_weight_type::create(sampleValue / sampleProb, _pdf); } - quotient_pdf_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC + quotient_weight_type quotientAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return quotientAndWeight(_sample, interaction.isotropic, _cache); } diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 0ac5c9166a..94cc51490d 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -175,7 +175,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::vector3_type)) ((NBL_CONCEPT_REQ_TYPE)(T::material_id_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE)(T::value_weight_type)) ((NBL_CONCEPT_REQ_TYPE)(T::measure_type)) ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) @@ -185,7 +185,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(BxdfNode, typename T::bxdfnode_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.evalAndWeight(matid, _sample, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.generate(matid, aniso_inter, u, cache_)), ::nbl::hlsl::is_same_v, typename T::sample_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotientAndWeight(matid, _sample, aniso_inter, cache_)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.quotientAndWeight(matid, _sample, aniso_inter, cache_)), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.getBxDFNode(matid, aniso_inter)), ::nbl::hlsl::is_same_v, typename T::bxdfnode_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.hasEmission(matid)), ::nbl::hlsl::is_same_v, bool)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((matsys.setMonochromeEta(matid, cie_y)), ::nbl::hlsl::is_same_v, typename T::scalar_type)) @@ -216,10 +216,10 @@ NBL_CONCEPT_BEGIN(1) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::scalar_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getSample()), ::nbl::hlsl::is_same_v, typename T::sample_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getQuotientPdf()), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getQuotientPdf()), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getT()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getLightObjectID()), ::nbl::hlsl::is_same_v, typename T::object_handle_type)) ); @@ -253,7 +253,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::ray_type)) ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE)(T::interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_quotient_return_type)) ((NBL_CONCEPT_REQ_TYPE)(T::tolerance_method_type)) diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index d75ebeae39..33a906fff7 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -46,7 +46,7 @@ struct Unidirectional using bxdfnode_type = typename MaterialSystem::bxdfnode_type; using anisotropic_interaction_type = typename MaterialSystem::anisotropic_interaction_type; using cache_type = typename MaterialSystem::cache_type; - using quotient_pdf_type = typename MaterialSystem::quotient_pdf_type; + using quotient_weight_type = typename MaterialSystem::quotient_weight_type; using value_weight_type = typename MaterialSystem::value_weight_type; using tolerance_method_type = typename NextEventEstimator::tolerance_method_type; @@ -101,7 +101,7 @@ struct Unidirectional ); scalar_type t = ret.getT(); sample_type nee_sample = ret.getSample(); - quotient_pdf_type neeContrib = ret.getQuotientPdf(); + quotient_weight_type neeContrib = ret.getQuotientPdf(); // While NEE or other generators are not supposed to pick up Delta lobes by accident, we need the MIS weights to add up to 1 for the non-delta lobes. // So we need to weigh the Delta lobes as if the MIS weight is always 1, but other areas regularly. @@ -141,7 +141,7 @@ struct Unidirectional return false; // the value of the bsdf divided by the probability of the sample being generated - quotient_pdf_type bsdf_quotient_weight = materialSystem.quotientAndWeight(matID, bsdf_sample, interaction, _cache); + quotient_weight_type bsdf_quotient_weight = materialSystem.quotientAndWeight(matID, bsdf_sample, interaction, _cache); throughput *= bsdf_quotient_weight.quotient(); bxdfPdf = bsdf_quotient_weight.pdf(); bxdfSample = bsdf_sample.getL().getDirection(); From 110547a6143c825034299b21b84bb1ce5f3d8821 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 26 Mar 2026 11:46:36 +0700 Subject: [PATCH 46/55] minor fixes to cook torrance, removed redundant functions --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 34 ++----------------- 1 file changed, 3 insertions(+), 31 deletions(-) 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 47e9ea90ca..328dc8e549 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -20,27 +20,6 @@ namespace bxdf namespace impl { -template -struct __implicit_promote; - -template -struct __implicit_promote -{ - static T __call(const T v) - { - return v; - } -}; - -template -struct __implicit_promote::scalar_type, 1> > -{ - static T __call(const vector::scalar_type, 1> v) - { - return hlsl::promote(v[0]); - } -}; - template struct quant_query_helper; @@ -197,16 +176,12 @@ struct SCookTorrance // allows compiler to throw away calls to ndf.D if using __overwriteDG, before that we only avoid computation for G2(correlated) if (isInfinity) return value_weight_type::create(scalar_type(0.0), scalar_type(0.0)); - - scalar_type clampedVdotH = cache.getVdotH(); - NBL_IF_CONSTEXPR(IsBSDF) - clampedVdotH = hlsl::abs(clampedVdotH); spectral_type eval; NBL_IF_CONSTEXPR(IsBSDF) eval = pdfQuery.reflectance; else - eval = impl::__implicit_promote::__call(_f(clampedVdotH)); + eval = _f(cache.getVdotH()); eval *= DG; return value_weight_type::create(eval, _pdf); @@ -383,14 +358,11 @@ struct SCookTorrance return query; } + query.pdf = DG1.projectedLightMeasure; NBL_IF_CONSTEXPR(IsBSDF) { query.scaled_reflectance = __getScaledReflectance(query.orientedFresnel, interaction, hlsl::abs(cache.getVdotH()), cache.isTransmission(), query.reflectance); - query.pdf = query.scaled_reflectance * DG1.projectedLightMeasure; - } - else - { - query.pdf = DG1.projectedLightMeasure; + query.pdf *= query.scaled_reflectance; } } From ef9156ee7302b0647ae202ae40e3f5c00580760d Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 26 Mar 2026 15:04:12 +0700 Subject: [PATCH 47/55] only microfacet bxdfs need eval cache type, allow for overload with existing cache type too --- .../builtin/hlsl/bxdf/base/lambertian.hlsl | 5 +- .../builtin/hlsl/bxdf/base/oren_nayar.hlsl | 5 +- include/nbl/builtin/hlsl/bxdf/common.hlsl | 165 +++++++++++++++--- .../bxdf/reflection/delta_distribution.hlsl | 5 +- .../bxdf/transmission/delta_distribution.hlsl | 5 +- .../bxdf/transmission/smooth_dielectric.hlsl | 10 +- 6 files changed, 157 insertions(+), 38 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index f2ba2e259a..39e24c3ac2 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -27,16 +27,15 @@ struct SLambertianBase struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; - using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { const spectral_type quo = hlsl::promote(_sample.getNdotL(_clamp) * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF)); return value_weight_type::create(quo, forwardPdf(_sample, interaction)); } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return evalAndWeight(_sample, interaction.isotropic, _cache); } diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index f4a036ce98..0ea8c95584 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -27,7 +27,6 @@ struct SOrenNayarBase struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; - using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; @@ -64,13 +63,13 @@ struct SOrenNayarBase return hlsl::promote(NdotL * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF) * __rec_pi_factored_out_wo_clamps(query.getVdotL(), NdotL, interaction.getNdotV(_clamp))); } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); return value_weight_type::create(__eval(query, _sample, interaction), forwardPdf(_sample, interaction)); } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return evalAndWeight(_sample, interaction.isotropic, _cache); } diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 628c153848..d2d557d2b0 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -901,7 +901,6 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::sample_type)) ((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type)) - ((NBL_CONCEPT_REQ_TYPE)(T::evalcache_type)) ((NBL_CONCEPT_REQ_TYPE)(T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE)(T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE)(T::value_weight_type)) @@ -922,22 +921,19 @@ NBL_BOOL_CONCEPT VecDim3OrMore = vector_traits::Dimension >= 3; #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) #define NBL_CONCEPT_PARAM_2 (aniso, typename T::anisotropic_interaction_type) #define NBL_CONCEPT_PARAM_3 (anisocache, typename T::anisocache_type) -#define NBL_CONCEPT_PARAM_4 (evalcache, typename T::evalcache_type) -NBL_CONCEPT_BEGIN(5) +NBL_CONCEPT_BEGIN(4) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 -#define evalcache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, evalcache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type)) ); -#undef evalcache #undef anisocache #undef aniso #undef _sample @@ -951,27 +947,73 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) #define NBL_CONCEPT_PARAM_2 (iso, typename T::isotropic_interaction_type) #define NBL_CONCEPT_PARAM_3 (isocache, typename T::isocache_type) -#define NBL_CONCEPT_PARAM_4 (evalcache, typename T::evalcache_type) -NBL_CONCEPT_BEGIN(5) +NBL_CONCEPT_BEGIN(4) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 -#define evalcache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, evalcache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) ); -#undef evalcache #undef isocache #undef iso #undef _sample #undef bxdf #include + +#define NBL_CONCEPT_NAME microfacet_bxdf_common +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (bxdf, T) +#define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) +#define NBL_CONCEPT_PARAM_2 (aniso, typename T::anisotropic_interaction_type) +#define NBL_CONCEPT_PARAM_3 (evalcache, typename T::evalcache_type) +NBL_CONCEPT_BEGIN(4) +#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define evalcache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::evalcache_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, evalcache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) +); +#undef evalcache +#undef aniso +#undef _sample +#undef bxdf +#include + +#define NBL_CONCEPT_NAME microfacet_iso_bxdf_common +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (bxdf, T) +#define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) +#define NBL_CONCEPT_PARAM_2 (iso, typename T::isotropic_interaction_type) +#define NBL_CONCEPT_PARAM_3 (evalcache, typename T::evalcache_type) +NBL_CONCEPT_BEGIN(4) +#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define evalcache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE)(T::evalcache_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(iso_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, evalcache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) +); +#undef evalcache +#undef iso +#undef _sample +#undef bxdf +#include } #define NBL_CONCEPT_NAME BRDF @@ -1071,17 +1113,100 @@ NBL_BOOL_CONCEPT BxDF = BRDF || BSDF; template NBL_BOOL_CONCEPT IsotropicBxDF = IsotropicBRDF || IsotropicBSDF; -template -NBL_BOOL_CONCEPT MicrofacetBRDF = BRDF && AnisotropicMicrofacetCache; -template -NBL_BOOL_CONCEPT MicrofacetBSDF = BSDF && AnisotropicMicrofacetCache; -template -NBL_BOOL_CONCEPT MicrofacetBxDF = MicrofacetBRDF || MicrofacetBSDF; +#define NBL_CONCEPT_NAME MicrofacetBRDF +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (bxdf, T) +#define NBL_CONCEPT_PARAM_1 (aniso, typename T::anisotropic_interaction_type) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) +#define NBL_CONCEPT_PARAM_3 (anisocache, typename T::anisocache_type) +NBL_CONCEPT_BEGIN(4) +#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::microfacet_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim2OrMore, typename T::random_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u,anisocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) +); +#undef anisocache +#undef u +#undef aniso +#undef bxdf +#include + +#define NBL_CONCEPT_NAME MicrofacetBSDF +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (bxdf, T) +#define NBL_CONCEPT_PARAM_1 (aniso, typename T::anisotropic_interaction_type) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) +#define NBL_CONCEPT_PARAM_3 (anisocache, typename T::anisocache_type) +NBL_CONCEPT_BEGIN(4) +#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::microfacet_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim3OrMore, typename T::random_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u,anisocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) +); +#undef anisocache +#undef u +#undef aniso +#undef bxdf +#include + +#define NBL_CONCEPT_NAME IsotropicMicrofacetBRDF +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (bxdf, T) +#define NBL_CONCEPT_PARAM_1 (iso, typename T::isotropic_interaction_type) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) +#define NBL_CONCEPT_PARAM_3 (isocache, typename T::isocache_type) +NBL_CONCEPT_BEGIN(4) +#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::microfacet_iso_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim2OrMore, typename T::random_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u,isocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) +); +#undef isocache +#undef u +#undef iso +#undef bxdf +#include + +#define NBL_CONCEPT_NAME IsotropicMicrofacetBSDF +#define NBL_CONCEPT_TPLT_PRM_KINDS (typename) +#define NBL_CONCEPT_TPLT_PRM_NAMES (T) +#define NBL_CONCEPT_PARAM_0 (bxdf, T) +#define NBL_CONCEPT_PARAM_1 (iso, typename T::isotropic_interaction_type) +#define NBL_CONCEPT_PARAM_2 (u, typename T::random_type) +#define NBL_CONCEPT_PARAM_3 (isocache, typename T::isocache_type) +NBL_CONCEPT_BEGIN(4) +#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 +#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 +#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 +#define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +NBL_CONCEPT_END( + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::microfacet_iso_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(impl::VecDim3OrMore, typename T::random_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u,isocache)), ::nbl::hlsl::is_same_v, typename T::sample_type)) +); +#undef isocache +#undef u +#undef iso +#undef bxdf +#include template -NBL_BOOL_CONCEPT IsotropicMicrofacetBRDF = IsotropicBRDF && AnisotropicMicrofacetCache && CreatableIsotropicMicrofacetCache; -template -NBL_BOOL_CONCEPT IsotropicMicrofacetBSDF = IsotropicBSDF && AnisotropicMicrofacetCache && CreatableIsotropicMicrofacetCache; +NBL_BOOL_CONCEPT MicrofacetBxDF = MicrofacetBRDF || MicrofacetBSDF; template NBL_BOOL_CONCEPT IsotropicMicrofacetBxDF = IsotropicMicrofacetBRDF || IsotropicMicrofacetBSDF; } diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index eb9bbb6943..cfebabd5fb 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -27,15 +27,14 @@ struct SDeltaDistribution struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; - using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index 600405a533..d1b221560e 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -27,15 +27,14 @@ struct SDeltaDistribution struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; - using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); } diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index d04c4cd821..673bc42472 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -28,16 +28,15 @@ struct SSmoothDielectric struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; - using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; // eval and weight return 0 because smooth dielectric/conductor BxDFs are dirac delta distributions, model perfectly specular objects that scatter light to only one outgoing direction - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); } @@ -95,7 +94,6 @@ struct SThinSmoothDielectric struct Cache {}; using isocache_type = Cache; using anisocache_type = Cache; - using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; @@ -108,11 +106,11 @@ struct SThinSmoothDielectric return retval; } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(evalcache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); } From 05124c2eb2cf8f525120bef75d4e0808514545a8 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 26 Mar 2026 15:08:39 +0700 Subject: [PATCH 48/55] latest example --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index ebb025be79..789a88c514 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit ebb025be792c6dcd37855526180a9dcf8119322e +Subproject commit 789a88c51453b4719473ec80c5af5f4611236c92 From 01ee867fa1e4d3be64e31394a3cc55ad5dd73522 Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Thu, 26 Mar 2026 11:30:27 +0100 Subject: [PATCH 49/55] fixes in HLSL, SPIR-V intrinsics and Mitsuba parsing merged --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index 4db014b562..02fac2d163 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 4db014b562162fa6006a3b4acf5bda0592732cd1 +Subproject commit 02fac2d1633dd0405210e40a463e1180426133b9 From e11b118dd2e80393b5b7eb309c6abb25f51a818c Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Thu, 26 Mar 2026 15:00:23 +0100 Subject: [PATCH 50/55] update submodule pointer after merge --- examples_tests | 2 +- include/nbl/core/sampling/OwenSampler.h | 21 ++++++++------------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/examples_tests b/examples_tests index 02fac2d163..46826a50d9 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 02fac2d1633dd0405210e40a463e1180426133b9 +Subproject commit 46826a50d9f7cd081cb422c2457618405794b62a diff --git a/include/nbl/core/sampling/OwenSampler.h b/include/nbl/core/sampling/OwenSampler.h index c51362472d..b218647314 100644 --- a/include/nbl/core/sampling/OwenSampler.h +++ b/include/nbl/core/sampling/OwenSampler.h @@ -1,22 +1,19 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2026 - 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_CORE_CORE_OWEN_SAMPLER_H_ -#define __NBL_CORE_CORE_OWEN_SAMPLER_H_ +#ifndef _NBL_CORE_CORE_OWEN_SAMPLER_H_ +#define _NBL_CORE_CORE_OWEN_SAMPLER_H_ #include "nbl/core/sampling/RandomSampler.h" #include "nbl/core/sampling/SobolSampler.h" -namespace nbl -{ -namespace core +namespace nbl::core { - //! TODO: make the tree sampler/generator configurable and let RandomSampler be default - template - class OwenSampler : protected SequenceSampler - { +//! TODO: make the tree sampler/generator configurable and let RandomSampler be default +template +class OwenSampler : protected SequenceSampler +{ public: OwenSampler(uint32_t _dimensions, uint32_t _seed) : SequenceSampler(_dimensions) { @@ -104,6 +101,4 @@ namespace core } -} - #endif From bea47804dd8f84fd56ae8ebd31820f0597bc56be Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 27 Mar 2026 11:55:47 +0700 Subject: [PATCH 51/55] minor changes to concepts, microfacet bxdfs have eval that autogenerates cache --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 27 ++++++++++++++++++- include/nbl/builtin/hlsl/bxdf/common.hlsl | 22 ++++++++------- .../builtin/hlsl/path_tracing/concepts.hlsl | 2 +- .../hlsl/path_tracing/unidirectional.hlsl | 2 +- 4 files changed, 40 insertions(+), 13 deletions(-) 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 328dc8e549..05e632177b 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -64,7 +64,7 @@ struct SCookTorrance NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = ndf_type::SupportedPaths != ndf::MTT_REFLECT; using random_type = conditional_t; NBL_HLSL_BXDF_ANISOTROPIC_COND_DECLS(IsAnisotropic); - using evalcache_type = conditional_t; + using cache_type = conditional_t; struct PdfQuery { @@ -145,6 +145,20 @@ struct SCookTorrance return hlsl::max(ab, value / ab) <= scalar_type(value + 1e-3); } + template, + typename C=bool_constant NBL_FUNC_REQUIRES(C::value && !fresnel_type::ReturnsMonochrome) + static scalar_type __getMonochromeEta(NBL_CONST_REF_ARG(fresnel_type) fresnel, NBL_CONST_REF_ARG(Interaction) interaction) + { + spectral_type throughputWeights = interaction.getLuminosityContributionHint(); + return hlsl::dot(fresnel.eta, throughputWeights) / (throughputWeights.r + throughputWeights.g + throughputWeights.b); + } + template, + typename C=bool_constant NBL_FUNC_REQUIRES(C::value && fresnel_type::ReturnsMonochrome) + static scalar_type __getMonochromeEta(NBL_CONST_REF_ARG(fresnel_type) fresnel, NBL_CONST_REF_ARG(Interaction) interaction) + { + return fresnel.getRefractionOrientedEta(); + } + // bxdf stuff template, class MicrofacetCache=conditional_t @@ -187,6 +201,17 @@ struct SCookTorrance return value_weight_type::create(eval, _pdf); } + template, + class MicrofacetCache=conditional_t + NBL_FUNC_REQUIRES(RequiredInteraction && RequiredMicrofacetCache) + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction) NBL_CONST_MEMBER_FUNC + { + const scalar_type eta = __getMonochromeEta(fresnel, interaction); + fresnel::OrientedEtas orientedEta = fresnel::OrientedEtas::create(interaction.getNdotV(), hlsl::promote(eta)); + MicrofacetCache cache = MicrofacetCache::template create(interaction, _sample, orientedEta); + return evalAndWeight(_sample, interaction, cache); + } + sample_type __generate_common(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type localH, const scalar_type NdotV, const scalar_type VdotH, const scalar_type LdotH, bool transmitted, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps) rcpEta, diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index d2d557d2b0..7980d11793 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -972,19 +972,20 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_0 (bxdf, T) #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) #define NBL_CONCEPT_PARAM_2 (aniso, typename T::anisotropic_interaction_type) -#define NBL_CONCEPT_PARAM_3 (evalcache, typename T::evalcache_type) +#define NBL_CONCEPT_PARAM_3 (microfacetCache, typename T::cache_type) NBL_CONCEPT_BEGIN(4) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -#define evalcache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define microfacetCache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE)(T::evalcache_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::cache_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, evalcache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, microfacetCache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ); -#undef evalcache +#undef microfacetCache #undef aniso #undef _sample #undef bxdf @@ -996,20 +997,21 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_0 (bxdf, T) #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) #define NBL_CONCEPT_PARAM_2 (iso, typename T::isotropic_interaction_type) -#define NBL_CONCEPT_PARAM_3 (evalcache, typename T::evalcache_type) +#define NBL_CONCEPT_PARAM_3 (microfacetCache, typename T::cache_type) NBL_CONCEPT_BEGIN(4) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 -#define evalcache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define microfacetCache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( - ((NBL_CONCEPT_REQ_TYPE)(T::evalcache_type)) + ((NBL_CONCEPT_REQ_TYPE)(T::cache_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(iso_bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, evalcache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, microfacetCache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ); -#undef evalcache +#undef microfacetCache #undef iso #undef _sample #undef bxdf diff --git a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 94cc51490d..76f5c61e47 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -219,7 +219,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE)(T::object_handle_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getSample()), ::nbl::hlsl::is_same_v, typename T::sample_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getQuotientPdf()), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getQuotientWeight()), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getT()), ::nbl::hlsl::is_same_v, typename T::scalar_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((sqr.getLightObjectID()), ::nbl::hlsl::is_same_v, typename T::object_handle_type)) ); diff --git a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl index 33a906fff7..1f1e0da37f 100644 --- a/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/unidirectional.hlsl @@ -101,7 +101,7 @@ struct Unidirectional ); scalar_type t = ret.getT(); sample_type nee_sample = ret.getSample(); - quotient_weight_type neeContrib = ret.getQuotientPdf(); + quotient_weight_type neeContrib = ret.getQuotientWeight(); // While NEE or other generators are not supposed to pick up Delta lobes by accident, we need the MIS weights to add up to 1 for the non-delta lobes. // So we need to weigh the Delta lobes as if the MIS weight is always 1, but other areas regularly. From 7d2708136a78be62fbffd575fa738f48343c8046 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 27 Mar 2026 14:59:40 +0700 Subject: [PATCH 52/55] added additional checks for a=0 to get pdf=inf --- .../builtin/hlsl/bxdf/base/cook_torrance_base.hlsl | 4 ++-- include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) 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 05e632177b..0159e15b55 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -167,7 +167,7 @@ struct SCookTorrance { PdfQuery pdfQuery = __forwardPdf(_sample, interaction, cache); scalar_type _pdf = pdfQuery.pdf; - if (_pdf == scalar_type(0.0)) + if (_pdf == scalar_type(0.0) || hlsl::isinf(_pdf)) return value_weight_type::create(scalar_type(0.0), scalar_type(0.0)); fresnel_type _f = pdfQuery.orientedFresnel; @@ -409,7 +409,7 @@ struct SCookTorrance { PdfQuery pdfQuery = __forwardPdf(_sample, interaction, cache); scalar_type _pdf = pdfQuery.pdf; - if (_pdf == scalar_type(0.0)) + if (_pdf == scalar_type(0.0) || hlsl::isinf(_pdf)) return quotient_weight_type::create(scalar_type(0.0), scalar_type(0.0)); fresnel_type _f = pdfQuery.orientedFresnel; diff --git a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl index 2c5f0c819c..a79eb65f55 100644 --- a/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/ndf/ggx.hlsl @@ -226,8 +226,10 @@ struct GGX enable_if_t createDG1Query(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC { dg1_query_type dg1_query; - bool dummy; - dg1_query.ndf = __ndf_base.template D(cache, dummy); + bool isInfinity; + dg1_query.ndf = __ndf_base.template D(cache, isInfinity); + if (isInfinity) + return dg1_query; scalar_type clampedNdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS); dg1_query.G1_over_2NdotV = G1_wo_numerator(clampedNdotV, __ndf_base.devsh_part(interaction.getNdotV2())); return dg1_query; @@ -244,8 +246,10 @@ struct GGX enable_if_t createDG1Query(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache) NBL_CONST_MEMBER_FUNC { dg1_query_type dg1_query; - bool dummy; - dg1_query.ndf = __ndf_base.template D(cache, dummy); + bool isInfinity; + dg1_query.ndf = __ndf_base.template D(cache, isInfinity); + if (isInfinity) + return dg1_query; scalar_type clampedNdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS); dg1_query.G1_over_2NdotV = G1_wo_numerator(clampedNdotV, __ndf_base.devsh_part(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2())); return dg1_query; From d8271b978d5f8fb0e95719f6085c5953f6ef5270 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 27 Mar 2026 16:07:31 +0700 Subject: [PATCH 53/55] regular bxdfs also have eval that don't need cache as input param --- .../hlsl/bxdf/base/cook_torrance_base.hlsl | 12 ++++++++---- .../nbl/builtin/hlsl/bxdf/base/lambertian.hlsl | 10 ++++++++++ .../nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl | 12 +++++++++++- include/nbl/builtin/hlsl/bxdf/common.hlsl | 4 ++-- .../hlsl/bxdf/reflection/delta_distribution.hlsl | 8 ++++++++ .../bxdf/transmission/delta_distribution.hlsl | 8 ++++++++ .../bxdf/transmission/smooth_dielectric.hlsl | 16 ++++++++++++++++ 7 files changed, 63 insertions(+), 7 deletions(-) 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 0159e15b55..9bcb8d03a3 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -409,14 +409,18 @@ struct SCookTorrance { PdfQuery pdfQuery = __forwardPdf(_sample, interaction, cache); scalar_type _pdf = pdfQuery.pdf; - if (_pdf == scalar_type(0.0) || hlsl::isinf(_pdf)) + if (_pdf == scalar_type(0.0)) return quotient_weight_type::create(scalar_type(0.0), scalar_type(0.0)); fresnel_type _f = pdfQuery.orientedFresnel; - using g2g1_query_type = typename N::g2g1_query_type; - g2g1_query_type gq = ndf.template createG2G1Query(_sample, interaction); - scalar_type G2_over_G1 = ndf.template G2_over_G1(gq, _sample, interaction, cache); + scalar_type G2_over_G1 = scalar_type(1.0); + if (_pdf < bit_cast(numeric_limits::infinity)) + { + using g2g1_query_type = typename N::g2g1_query_type; + g2g1_query_type gq = ndf.template createG2G1Query(_sample, interaction); + G2_over_G1 = ndf.template G2_over_G1(gq, _sample, interaction, cache); + } spectral_type quo; NBL_IF_CONSTEXPR(IsBSDF) diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index 39e24c3ac2..a50d58b922 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -39,6 +39,16 @@ struct SLambertianBase { return evalAndWeight(_sample, interaction.isotropic, _cache); } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + Cache dummy; + return evalAndWeight(_sample, interaction.isotropic, dummy); + } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + Cache dummy; + return evalAndWeight(_sample, interaction.isotropic, dummy); + } template > enable_if_t generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index 0ea8c95584..f77e92d485 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -71,7 +71,17 @@ struct SOrenNayarBase } value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return evalAndWeight(_sample, interaction.isotropic, _cache); + return evalAndWeight(_sample, interaction.isotropic, _cache); + } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + Cache dummy; + return evalAndWeight(_sample, interaction.isotropic, dummy); + } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + Cache dummy; + return evalAndWeight(_sample, interaction.isotropic, dummy); } template > diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index 7980d11793..eb4a0c1d01 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -928,6 +928,7 @@ NBL_CONCEPT_BEGIN(4) #define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) @@ -956,6 +957,7 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) @@ -982,7 +984,6 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::cache_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, microfacetCache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ); #undef microfacetCache @@ -1008,7 +1009,6 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(iso_bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, microfacetCache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ); #undef microfacetCache diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index cfebabd5fb..ee91ebe9ea 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -38,6 +38,14 @@ struct SDeltaDistribution { return value_weight_type::create(0.0, 0.0); } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + return value_weight_type::create(0.0, 0.0); + } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + return value_weight_type::create(0.0, 0.0); + } sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index d1b221560e..f4a915bbed 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -38,6 +38,14 @@ struct SDeltaDistribution { return value_weight_type::create(0.0, 0.0); } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + return value_weight_type::create(0.0, 0.0); + } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + return value_weight_type::create(0.0, 0.0); + } sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const random_type u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index 673bc42472..a89108e6b8 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -40,6 +40,14 @@ struct SSmoothDielectric { return value_weight_type::create(0.0, 0.0); } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + return value_weight_type::create(0.0, 0.0); + } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + return value_weight_type::create(0.0, 0.0); + } sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(random_type) u, NBL_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { @@ -114,6 +122,14 @@ struct SThinSmoothDielectric { return value_weight_type::create(0.0, 0.0); } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + return value_weight_type::create(0.0, 0.0); + } + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC + { + return value_weight_type::create(0.0, 0.0); + } // usually `luminosityContributionHint` would be the Rec.709 luma coefficients (the Y row of the RGB to CIE XYZ matrix) // its basically a set of weights that determine From e4a8319e2ad529ecde0fa4cc1f3689ac2c85859a Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 27 Mar 2026 16:53:56 +0700 Subject: [PATCH 54/55] regular bxdfs don't take cache for eval --- .../nbl/builtin/hlsl/bxdf/base/lambertian.hlsl | 14 ++------------ .../nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl | 14 ++------------ include/nbl/builtin/hlsl/bxdf/common.hlsl | 15 +++++++++++---- .../hlsl/bxdf/reflection/delta_distribution.hlsl | 8 -------- .../bxdf/transmission/delta_distribution.hlsl | 8 -------- .../bxdf/transmission/smooth_dielectric.hlsl | 16 ---------------- 6 files changed, 15 insertions(+), 60 deletions(-) diff --git a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index a50d58b922..d6a3e7a8c5 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -30,24 +30,14 @@ struct SLambertianBase NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { const spectral_type quo = hlsl::promote(_sample.getNdotL(_clamp) * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF)); return value_weight_type::create(quo, forwardPdf(_sample, interaction)); } value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { - return evalAndWeight(_sample, interaction.isotropic, _cache); - } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC - { - Cache dummy; - return evalAndWeight(_sample, interaction.isotropic, dummy); - } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC - { - Cache dummy; - return evalAndWeight(_sample, interaction.isotropic, dummy); + return evalAndWeight(_sample, interaction.isotropic); } template > diff --git a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl index f77e92d485..1225cd9fb3 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -63,25 +63,15 @@ struct SOrenNayarBase return hlsl::promote(NdotL * numbers::inv_pi * hlsl::mix(1.0, 0.5, IsBSDF) * __rec_pi_factored_out_wo_clamps(query.getVdotL(), NdotL, interaction.getNdotV(_clamp))); } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC + value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { SQuery query; query.VdotL = hlsl::dot(interaction.getV().getDirection(), _sample.getL().getDirection()); return value_weight_type::create(__eval(query, _sample, interaction), forwardPdf(_sample, interaction)); } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC - { - return evalAndWeight(_sample, interaction.isotropic, _cache); - } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC - { - Cache dummy; - return evalAndWeight(_sample, interaction.isotropic, dummy); - } value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { - Cache dummy; - return evalAndWeight(_sample, interaction.isotropic, dummy); + return evalAndWeight(_sample, interaction.isotropic); } template > diff --git a/include/nbl/builtin/hlsl/bxdf/common.hlsl b/include/nbl/builtin/hlsl/bxdf/common.hlsl index eb4a0c1d01..54e4a1b145 100644 --- a/include/nbl/builtin/hlsl/bxdf/common.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/common.hlsl @@ -929,7 +929,6 @@ NBL_CONCEPT_BEGIN(4) NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common_typdefs, T)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type)) @@ -958,7 +957,6 @@ NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type)) ((NBL_CONCEPT_REQ_TYPE)(T::isocache_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) - ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotientAndWeight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::quotient_weight_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type)) ); @@ -975,17 +973,21 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) #define NBL_CONCEPT_PARAM_2 (aniso, typename T::anisotropic_interaction_type) #define NBL_CONCEPT_PARAM_3 (microfacetCache, typename T::cache_type) -NBL_CONCEPT_BEGIN(4) +#define NBL_CONCEPT_PARAM_4 (anisocache, typename T::anisocache_type) +NBL_CONCEPT_BEGIN(5) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define microfacetCache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::cache_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, anisocache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, aniso, microfacetCache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ); +#undef anisocache #undef microfacetCache #undef aniso #undef _sample @@ -999,18 +1001,23 @@ NBL_CONCEPT_END( #define NBL_CONCEPT_PARAM_1 (_sample, typename T::sample_type) #define NBL_CONCEPT_PARAM_2 (iso, typename T::isotropic_interaction_type) #define NBL_CONCEPT_PARAM_3 (microfacetCache, typename T::cache_type) -NBL_CONCEPT_BEGIN(4) +#define NBL_CONCEPT_PARAM_4 (isocache, typename T::isocache_type) +NBL_CONCEPT_BEGIN(5) #define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0 #define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1 #define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2 #define microfacetCache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3 +#define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4 NBL_CONCEPT_END( ((NBL_CONCEPT_REQ_TYPE)(T::cache_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(iso_bxdf_common, T)) + ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(microfacet_bxdf_common, T)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type)) ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) + ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, isocache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.evalAndWeight(_sample, iso, microfacetCache)), ::nbl::hlsl::is_same_v, typename T::value_weight_type)) ); +#undef isocache #undef microfacetCache #undef iso #undef _sample diff --git a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl index ee91ebe9ea..3ca10587d3 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -30,14 +30,6 @@ struct SDeltaDistribution NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_MAX; - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC - { - return value_weight_type::create(0.0, 0.0); - } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC - { - return value_weight_type::create(0.0, 0.0); - } value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl index f4a915bbed..59b58bfedf 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -30,14 +30,6 @@ struct SDeltaDistribution NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC - { - return value_weight_type::create(0.0, 0.0); - } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC - { - return value_weight_type::create(0.0, 0.0); - } value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); diff --git a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl index a89108e6b8..bb70e46805 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -32,14 +32,6 @@ struct SSmoothDielectric NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; // eval and weight return 0 because smooth dielectric/conductor BxDFs are dirac delta distributions, model perfectly specular objects that scatter light to only one outgoing direction - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC - { - return value_weight_type::create(0.0, 0.0); - } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC - { - return value_weight_type::create(0.0, 0.0); - } value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); @@ -114,14 +106,6 @@ struct SThinSmoothDielectric return retval; } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) _cache) NBL_CONST_MEMBER_FUNC - { - return value_weight_type::create(0.0, 0.0); - } - value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC - { - return value_weight_type::create(0.0, 0.0); - } value_weight_type evalAndWeight(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) NBL_CONST_MEMBER_FUNC { return value_weight_type::create(0.0, 0.0); From 920c44a8c3f360196229558a623ec59b525c9a9c Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 27 Mar 2026 16:55:37 +0700 Subject: [PATCH 55/55] latest example --- examples_tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_tests b/examples_tests index ee0c4ab29d..5d64bab814 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit ee0c4ab29d0b48d09df420a0d4ddd68f07182376 +Subproject commit 5d64bab814a35f7fbf02f5cbf6030cfbe537a7cf