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 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/examples_tests b/examples_tests index 24baa877d2..ebb025be79 160000 --- a/examples_tests +++ b/examples_tests @@ -1 +1 @@ -Subproject commit 24baa877d25cf8eaf0461ba0bce371a4bad57537 +Subproject commit ebb025be792c6dcd37855526180a9dcf8119322e 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/include/nbl/asset/material_compiler3/CFrontendIR.h b/include/nbl/asset/material_compiler3/CFrontendIR.h index d43b0be0ae..4269af5d6f 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)); @@ -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. @@ -538,18 +584,24 @@ 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: - inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override {return perpTransmittance;} + COPY_DEFAULT_IMPL + + 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 + { + *(ix ? &perpTransmittance:&thickness) = 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,30 +624,71 @@ 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; }; // 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 + // 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. + // // 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: - 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";} @@ -644,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 @@ -662,9 +755,17 @@ 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 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. - // If one wants to emulate non-linear diffuse TIR color shifts, abuse `CThinInfiniteScatterCorrection`. class CDeltaTransmission final : public IBxDF { public: @@ -673,7 +774,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 +788,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 +817,123 @@ 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 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) + 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; + + 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)) + { + m_rootNodes.push_back(rootNode); + return true; + } + return false; + } // For Debug Visualization struct SDotPrinter final diff --git a/include/nbl/asset/utils/IShaderCompiler.h b/include/nbl/asset/utils/IShaderCompiler.h index 0c24c2b1d0..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,6 +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::function onPartialOutputOnFailure = {}; }; // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#debugging 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..f43e242efc 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl @@ -83,7 +83,9 @@ 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); + using evalcache_type = conditional_t; // utility functions template, @@ -156,11 +158,14 @@ 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 + 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 hlsl::promote(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); 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 +173,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 +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 hlsl::promote(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 eval; 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; + eval = hlsl::mix(reflectance, hlsl::promote(1.0) - reflectance, cache.isTransmission()) * DG; } else - return impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; + eval = impl::__implicit_promote::__call(_f(clampedVdotH)) * DG; + + return value_weight_type::create(eval, _pdf); } sample_type __generate_common(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type localH, @@ -245,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) @@ -275,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; @@ -332,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); @@ -341,7 +348,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; @@ -366,27 +373,27 @@ 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); } 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 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); @@ -436,6 +443,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/base/lambertian.hlsl b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl index 3f7b85875a..d79f191b60 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl @@ -23,19 +23,26 @@ struct SLambertianBase using this_t = SLambertianBase; BXDF_CONFIG_TYPE_ALIASES(Config); + using random_type = conditional_t; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; + using evalcache_type = Cache; + 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 + 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 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 value_weight_type::create(quo, forwardPdf(_sample, interaction)); } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) 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 eval(_sample, interaction.isotropic); + return evalAndWeight(_sample, interaction.isotropic, _cache); } 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_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedHemisphere::cache_type cache; ray_dir_info_type L; @@ -43,7 +50,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_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedSphere::cache_type cache; vector3_type _u = u; @@ -51,30 +58,24 @@ 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_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 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 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 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) @@ -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 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 quotient_and_pdf(_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 ab06e8d43a..cea334a5e8 100644 --- a/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/base/oren_nayar.hlsl @@ -23,6 +23,12 @@ struct SOrenNayarBase using this_t = SOrenNayarBase; BXDF_CONFIG_TYPE_ALIASES(Config); + using random_type = conditional_t; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; + using evalcache_type = Cache; + NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = conditional_value::value; struct SCreationParams @@ -58,19 +64,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))); } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction) 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 __eval(query, _sample, interaction); + return value_weight_type::create(__eval(query, _sample, interaction), forwardPdf(_sample, interaction)); } - spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction) 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 eval(_sample, interaction.isotropic); + return evalAndWeight(_sample, interaction.isotropic, _cache); } 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_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedHemisphere::cache_type cache; ray_dir_info_type L; @@ -78,7 +84,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_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { typename sampling::ProjectedSphere::cache_type cache; vector3_type _u = u; @@ -86,45 +92,39 @@ 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_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 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 __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 __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); } - 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 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 __quotient_and_pdf(query, _sample, interaction); + return __quotientAndWeight(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 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 quotient_and_pdf(_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 c114222c7c..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 @@ -889,7 +890,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) @@ -900,196 +900,73 @@ 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::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 #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.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) -#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 -#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, vector) -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_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, vector) -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_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, vector) -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_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, vector) -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_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; +NBL_BOOL_CONCEPT VecDim2OrMore = vector_traits::Dimension >= 2; template -NBL_BOOL_CONCEPT IsotropicBxDF = IsotropicBRDF || IsotropicBSDF; +NBL_BOOL_CONCEPT VecDim3OrMore = vector_traits::Dimension >= 3; - -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) #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_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_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)) ((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 evalcache #undef anisocache #undef aniso #undef _sample #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) #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_4 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(_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_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)) - ((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type)) ); +#undef evalcache #undef isocache #undef iso #undef _sample @@ -1097,12 +974,12 @@ 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) #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 @@ -1110,7 +987,8 @@ 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)) ); #undef anisocache @@ -1119,12 +997,12 @@ 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) #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 @@ -1132,7 +1010,8 @@ 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)) ); #undef anisocache @@ -1141,12 +1020,12 @@ 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) #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 @@ -1154,7 +1033,8 @@ 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)) ); #undef isocache @@ -1163,12 +1043,12 @@ 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) #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 @@ -1176,7 +1056,8 @@ 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)) ); #undef isocache @@ -1185,8 +1066,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/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 9bf9e16aa9..7de8d56152 100644 --- a/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/reflection/delta_distribution.hlsl @@ -23,18 +23,24 @@ struct SDeltaDistribution using this_t = SDeltaDistribution; BXDF_CONFIG_TYPE_ALIASES(Config); + using random_type = vector2_type; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; + using evalcache_type = Cache; + 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 + 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 hlsl::promote(0); + return value_weight_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 + 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 hlsl::promote(0); + return value_weight_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_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { vector3_type V = interaction.getV().getDirection(); bxdf::Reflect r = bxdf::Reflect::create(V, interaction.getN()); @@ -47,28 +53,28 @@ 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_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 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; } - 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 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 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 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 quotient_and_pdf(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic, _cache); } }; 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 7ec41aeed5..f44e313b9c 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/delta_distribution.hlsl @@ -23,18 +23,24 @@ struct SDeltaDistribution using this_t = SDeltaDistribution; BXDF_CONFIG_TYPE_ALIASES(Config); + using random_type = vector2_type; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; + using evalcache_type = Cache; + 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 + 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 hlsl::promote(0); + return value_weight_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 + 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 hlsl::promote(0); + return value_weight_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_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()); @@ -44,28 +50,28 @@ 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_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 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; } - 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 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 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 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 quotient_and_pdf(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic); } }; @@ -78,6 +84,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 bee202dc8d..08f54950a1 100644 --- a/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl +++ b/include/nbl/builtin/hlsl/bxdf/transmission/smooth_dielectric.hlsl @@ -24,18 +24,25 @@ struct SSmoothDielectric using this_t = SSmoothDielectric; BXDF_CONFIG_TYPE_ALIASES(Config); + using random_type = vector3_type; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; + using evalcache_type = Cache; + 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 + // 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 { - return hlsl::promote(0); + return value_weight_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 + 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 hlsl::promote(0); + return value_weight_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_REF_ARG(anisocache_type) _cache) NBL_CONST_MEMBER_FUNC { const scalar_type reflectance = fresnel::Dielectric::__call(orientedEta.value*orientedEta.value, interaction.getNdotV(_clamp))[0]; @@ -51,29 +58,28 @@ 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_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); } - // 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 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; } // 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 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 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 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 quotient_and_pdf(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic, _cache); } fresnel::OrientedEtas orientedEta; @@ -83,16 +89,13 @@ 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; + struct Cache {}; + using isocache_type = Cache; + using anisocache_type = Cache; + using evalcache_type = Cache; NBL_CONSTEXPR_STATIC_INLINE BxDFClampMode _clamp = BxDFClampMode::BCM_ABS; @@ -105,20 +108,20 @@ 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 + 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 hlsl::promote(0); + return value_weight_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 + 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 hlsl::promote(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) // its basically a set of weights that determine // assert(1.0==luminosityContributionHint.r+luminosityContributionHint.g+luminosityContributionHint.b); - // `remainderMetadata` is a variable which the generator function returns byproducts of sample generation that would otherwise have to be redundantly calculated `quotient_and_pdf` - sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(spectral_type) remainderMetadata) NBL_CONST_MEMBER_FUNC + // `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 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,27 +143,27 @@ 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_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 vector3_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 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; } // 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 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))); @@ -171,9 +174,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 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 quotient_and_pdf(_sample, interaction.isotropic); + return quotientAndWeight(_sample, interaction.isotropic, _cache); } fresnel::Dielectric fresnel; @@ -188,6 +191,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 +201,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/path_tracing/concepts.hlsl b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl index 25ca98772c..0ac5c9166a 100644 --- a/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl +++ b/include/nbl/builtin/hlsl/path_tracing/concepts.hlsl @@ -176,16 +176,16 @@ 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.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::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.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)) @@ -235,17 +235,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,13 +259,12 @@ 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.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)) ); #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 98a81738cb..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) @@ -54,7 +55,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(); @@ -69,14 +70,14 @@ struct Unidirectional if (ray.shouldDoMIS() && matLightID.isLight()) { emissive *= ray.getPayloadThroughput(); - const scalar_type pdf = nee.deferred_pdf(scene, lightID, ray); - assert(!hlsl::isinf(pdf)); - emissive *= ray.foundEmissiveMIS(pdf * pdf); + const scalar_type weight = nee.deferredWeight(scene, lightID, ray); + assert(!hlsl::isinf(weight)); + emissive *= ray.foundEmissiveMIS(weight * weight); } ray.addPayloadContribution(emissive); } - if (!matLightID.canContinuePath()) + if (lastBounce || !matLightID.canContinuePath()) return false; bxdfnode_type bxdf = materialSystem.getBxDFNode(matID, interaction); @@ -94,9 +95,9 @@ 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( - scene, materialSystem, intersectP, interaction, - eps0, depth + typename nee_type::sample_quotient_return_type ret = nee.template generateAndQuotientAndWeight( + scene, materialSystem, intersectP, + interaction, eps0, ray ); scalar_type t = ret.getT(); sample_type nee_sample = ret.getSample(); @@ -108,12 +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)) { - // 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; + 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 = bsdf_pdf * rcpChoiceProb / neeContrib.pdf(); + const scalar_type otherGenOverLightAndChoice = bsdfContrib.weight() * rcpChoiceProb / neeContrib.pdf(); neeContrib._quotient /= 1.f + otherGenOverLightAndChoice * otherGenOverLightAndChoice; // balance heuristic } @@ -141,9 +141,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,12 +170,12 @@ 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 weight = nee.deferredWeight(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); } @@ -185,16 +185,18 @@ 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); + ray.setDepth(d); closest_hit_type intersection = intersector_type::traceClosestHit(scene, ray); - continuePath = intersection.foundHit(); - if (continuePath) - continuePath = closestHitProgram(d, sampleIndex, ray, intersection); + 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; 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 new file mode 100644 index 0000000000..19711174c5 --- /dev/null +++ b/include/nbl/builtin/hlsl/sampling/value_and_weight.hlsl @@ -0,0 +1,89 @@ +// 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_ + +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/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl b/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl index 118f022640..60c4b68df7 100644 --- a/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl +++ b/include/nbl/builtin/hlsl/shapes/spherical_triangle.hlsl @@ -45,7 +45,8 @@ struct SphericalTriangle vector3_type __getCosVertices() { - // using Spherical Law of Cosines (TODO: do we need to clamp anymore? since the pyramid angles method introduction?) + // 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 return hlsl::clamp((cos_sides - cos_sides.yzx * cos_sides.zxy) * csc_sides.yzx * csc_sides.zxy, hlsl::promote(-1.0), hlsl::promote(1.0)); } @@ -70,7 +71,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; @@ -81,10 +82,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]; diff --git a/include/nbl/ext/MitsubaLoader/SContext.h b/include/nbl/ext/MitsubaLoader/SContext.h index d91fd98a9b..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; @@ -95,6 +96,7 @@ struct SContext final { Albedo, Opacity, + Weight, MitsubaExtraFactor, Count }; 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 } diff --git a/src/nbl/asset/material_compiler3/CFrontendIR.cpp b/src/nbl/asset/material_compiler3/CFrontendIR.cpp index c4b7fd49a8..d77ac287da 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" @@ -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()!=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; + } return false; } @@ -111,18 +116,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 + { + 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 fresnelH = getObjectPool().emplace(); - auto* fresnel = getObjectPool().deref(fresnelH); - *fresnel = *in; - fresnel->reciprocateEtas = ~in->reciprocateEtas; - return fresnelH; + 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 diff --git a/src/nbl/asset/utils/CWaveStringResolver.cpp b/src/nbl/asset/utils/CWaveStringResolver.cpp index aae872668f..8da0e828ec 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,6 +50,22 @@ using namespace nbl::asset; namespace { +constexpr size_t kWaveFailureLogOutputTailMaxChars = 4096ull; +constexpr size_t kWaveFailureLogOutputTailMaxLines = 16ull; +constexpr size_t kWaveFailureLogTokenPreviewMaxChars = 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 +102,94 @@ 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 = static_cast(std::count(text.begin(), text.end(), '\n')); + 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 makeFailureLogOutputTail(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 > kWaveFailureLogOutputTailMaxLines) + { + ++start; + break; + } + } + + if (chars >= kWaveFailureLogOutputTailMaxChars) + 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 +200,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), kWaveFailureLogTokenPreviewMaxChars); const auto snippet = getLineSnippet(code, lineNo); if (!snippet.empty() && fileName && preprocessOptions.sourceIdentifier == fileName) @@ -115,6 +232,10 @@ std::string makeWaveFailureContext( stream << "\n " << caret; } + const auto outputTail = makeFailureLogOutputTail(renderProgress.output); + if (!outputTail.empty()) + stream << "\n partial_output_tail:\n" << indentMultiline(outputTail, " "); + return stream.str(); } @@ -134,14 +255,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 +272,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( @@ -194,16 +316,26 @@ 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))); - core::string resolvedString; + WaveRenderProgress renderProgress; const char* phase = "registering built-in macros"; std::string activeMacroDefinition; + const auto reportPartialOutputOnFailure = [&]() + { + if (preprocessOptions.onPartialOutputOnFailure) + preprocessOptions.onPartialOutputOnFailure(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 { + 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) { @@ -215,31 +347,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()); + 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()); + 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); + 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()); return {}; } catch (const std::exception& e) { - const auto failureContext = makeWaveFailureContext(preprocessOptions, code, phase, activeMacroDefinition, preprocessOptions.sourceIdentifier.data(), 0, 0); + 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 (...) { - const auto failureContext = makeWaveFailureContext(preprocessOptions, code, phase, activeMacroDefinition, preprocessOptions.sourceIdentifier.data(), 0, 0); + 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()); return {}; @@ -247,7 +385,7 @@ std::string preprocessImpl( post(context); - return resolvedString; + return std::move(renderProgress.output); } } 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 660c86ad08..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" @@ -18,6 +19,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 +584,6 @@ template<> inline bool boost::wave::impl::pp_iterator_functor inline bool boost::wave::impl::pp_iterator_functor(); auto* const combiner = frontPool.deref(combinerH); combiner->lhs = foundEmitter->second._const_cast(); @@ -418,62 +419,72 @@ 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",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():""); @@ -500,10 +511,21 @@ 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; } } else @@ -527,7 +549,17 @@ hlsl::float32_t2x3 SContext::getParameters(const std::span 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 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(); @@ -624,22 +657,23 @@ 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 mulH; + 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 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(); @@ -664,6 +698,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; @@ -675,13 +710,13 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW mul->lhs = ctH; mul->rhs = fresnelH; } - return handle; + return frontIR->createMul(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 +726,75 @@ 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; + // TODO: derivative maps + } + 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); + 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); + } + layer->btdf = transH; + // 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; + } } - return mulH; }; + 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* const leaf = frontPool.deref(retval); + auto* leaf = frontPool.deref(retval); switch (_bsdf->type) { case CElementBSDF::DIFFUSE: [[fallthrough]]; @@ -782,10 +876,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); + expr_handle_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) { @@ -803,34 +897,34 @@ auto SContext::genMaterial(const CElementBSDF* bsdf, system::ISystem* debugFileW mul->lhs = deltaTransmission._const_cast(); mul->rhs = thinInfiniteScatterH; } - // we're rethreading the fresnel here - btdf->lhs = correctedTransmissionH; +#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 + { + // beautiful thing we can reuse the same BxDF nodes for a transmission function without touching Etas or anything! + btdfH = brdf->lhs; + leaf->brdfBottom = brdfH; } - else // beautiful thing we can reuse the same nodes for a transmission function without touching Etas or anything! - btdf->lhs = brdf->lhs; } - leaf->btdf = btdfH; + 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 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; - } - 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); } @@ -861,81 +955,220 @@ 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: assert(false); // we shouldn't get this case here - return errorMaterial._const_cast(); + retval = errorMaterial._const_cast(); + break; } - assert(!leaf->brdfBottom); + leaf = frontPool.deref(retval); + assert(leaf->brdfTop); return retval; }; // Post-order Depth First Traversal (create children first, then create parent) struct SStackEntry { - const CElementBSDF* bsdf; - uint64_t visited : 1 = false; - uint64_t expectedNodeType : 2 = false; - uint64_t unused : 61 = false; + SEntry immutable; + bool visited = false; }; 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; - core::unordered_map localCache; - while (!stack.front().visited) + stack.emplace_back() = {.immutable={.bsdf=bsdf}}; + // + frontend_ir_t::typed_pointer_type rootH = {}; + while (!stack.empty()) { - const auto entry = stack.back(); - assert(entry.bsdf); - if (!entry.visited) + auto& entry = stack.back(); + const auto* const _bsdf = entry.immutable.bsdf; + assert(_bsdf); + // we only do post-dfs for non-leafs + if (_bsdf->isMeta() && !entry.visited) { - node_t newNodeH = {}; // TODO: {errorFactor,errorBRDF,errorLayer}[entry.expectedNodeType]; - if (entry.bsdf->isMeta()) + if (_bsdf->isMeta()) { - return errorMaterial;// temporary - switch(entry.bsdf->type) + const auto& meta_common = _bsdf->meta_common; + assert(meta_common.childCount); + switch(_bsdf->type) { + case CElementBSDF::COATING: [[fallthrough]]; + case CElementBSDF::ROUGHCOATING: + assert(meta_common.childCount==1); + break; + case CElementBSDF::BUMPMAP: [[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; + case CElementBSDF::MIXTURE_BSDF: + break; + case CElementBSDF::BLEND_BSDF: + assert(meta_common.childCount==2); + break; + case CElementBSDF::TWO_SIDED: + assert(meta_common.childCount<=2); + break; + default: + assert(false); // we shouldn't get this case here + break; + } + // TODO : make sure child gets pushed with derivative map info + for (decltype(meta_common.childCount) i=0; 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]]; + case CElementBSDF::ROUGHCOATING: + { + const auto coatingH = frontPool.emplace(); + auto* const coating = frontPool.deref(coatingH); + // 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 = getChildFromCache(_bsdf->mask.bsdf[0]); + newMaterialH = 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 = ; - newNodeH = maskH; + // + 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); + mask->brdfTop = frontIR->createMul(nested->brdfTop,opacityH); + { + mask->btdf = frontIR->createAdd( + frontIR->createMul(deltaTransmission._const_cast(),frontIR->createComplement(opacityH)), + frontIR->createMul(nested->btdf,opacityH) + ); + } + mask->brdfBottom = frontIR->createMul(nested->brdfBottom,opacityH); + newMaterialH = maskH; + break; + } + case CElementBSDF::BUMPMAP: [[fallthrough]]; + case CElementBSDF::NORMALMAP: + { + // we basically ignore and skip because derivative map already applied + newMaterialH = getChildFromCache(_bsdf->mask.bsdf[0]); + break; + } + case CElementBSDF::MIXTURE_BSDF: + { + 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(); + 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]); + createWeightedSum(frontPool.deref(blendH),loH,tComplementH,hiH,tH); + newMaterialH = blendH; + break; + } + case CElementBSDF::TWO_SIDED: + { + 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))) + { + logger.log("Mitsuba cannot be used to glue two transmissive BxDF Layer Stacks together!",LoggerError,debugName.c_str()); + break; + } + // this is the stack we attach to the back, but we need to reverse it + const auto backH = frontIR->reverse(chosenBackH); + if (!backH) + { + logger.log("Failed to reverse and reciprocate the BxDF Layer Stack!",LoggerError,debugName.c_str()); + break; + } + // 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; } - //case CElementBSDF::TWO_SIDED: - // 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; 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 - { + newMaterialH = createMistubaLeaf(entry.immutable); + if (!newMaterialH) + newMaterialH = errorMaterial; + 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 = block_allocator_t::_static_cast(found->second); if (!rootH) return errorMaterial; + + // add debug info auto* const root = frontPool.deref(rootH); root->debugInfo = frontPool.emplace(debugName); @@ -1068,6 +1301,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(); { @@ -1096,6 +1334,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..14b9f5d434 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: @@ -188,203 +163,4 @@ CMitsubaMaterialCompilerFrontend::tex_ass_type CMitsubaMaterialCompilerFrontend: std::tie(node->texture.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::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(); - 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; - } - -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); - uint32_t child_num = 0u; - bool twosided = false; - 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::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]; - 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; - child_node.front = parent.front; - 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); - } - } - - 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; - 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 }; -} - -} + break; \ No newline at end of file diff --git a/tools/nsc/main.cpp b/tools/nsc/main.cpp index 736a06db8f..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; @@ -167,6 +220,7 @@ class ShaderCompiler final : public IApplicationFramework argparse::ArgumentParser program("nsc"); program.add_argument("--dump-build-info").default_value(false).implicit_value(true); program.add_argument("--self-test-unmount-builtins").default_value(false).implicit_value(true); + program.add_argument("--dump-preprocessed-on-failure").default_value(false).implicit_value(true); program.add_argument("--file").default_value(std::string{}); program.add_argument("-P").default_value(false).implicit_value(true); program.add_argument("-no-nbl-builtins").default_value(false).implicit_value(true); @@ -191,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()) @@ -203,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(); @@ -242,6 +295,7 @@ class ShaderCompiler final : public IApplicationFramework } const bool preprocessOnly = program.get("-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 +307,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()) @@ -277,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; @@ -329,6 +388,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 +399,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 +423,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(); - return false; - } - - out.close(); - if (out.fail()) - { - m_logger->log("Failed to close output file: %s", ILogger::ELL_ERROR, outputFilepath.c_str()); + if (!writeOutputFile(outputFilepath, job.view, "output")) return false; - } if (dep.enabled) m_logger->log("Dependency file written to %s", ILogger::ELL_INFO, dep.path.c_str()); @@ -403,6 +445,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; @@ -436,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"]; @@ -465,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"); @@ -473,18 +516,28 @@ 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; } - 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) + { + if (!writeFileWithSystem(m_system.get(), outputFilepath, content)) + { + m_logger->log("Failed to write %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 +600,22 @@ 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.onPartialOutputOnFailure = [&](const std::string_view partialOutput) + { + partialOutputOnFailure.assign(partialOutput); + }; + } 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; } 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