Skip to content

Commit 0dc31aa

Browse files
add CFrontendIR node copying, stack reversal and individual BxDF expression reciprocation (swapping of interface order)
Unfortunately I realized our THINDIELECTRIC correction is badly designed
1 parent 1a7bf02 commit 0dc31aa

2 files changed

Lines changed: 289 additions & 40 deletions

File tree

include/nbl/asset/material_compiler3/CFrontendIR.h

Lines changed: 170 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,26 @@ class CFrontendIR final : public CNodePool
271271

272272
protected:
273273
friend class CFrontendIR;
274+
// copy
275+
virtual _typed_pointer_type<IExprNode> copy(CFrontendIR* ir) const = 0;
276+
#define COPY_DEFAULT_IMPL inline _typed_pointer_type<IExprNode> copy(CFrontendIR* ir) const override final \
277+
{ \
278+
auto& pool = ir->getObjectPool(); \
279+
const auto copyH = pool.emplace<std::remove_const_t<std::remove_pointer_t<decltype(this)> > >(); \
280+
if (auto* const copy = pool.deref(copyH); copyH) \
281+
*copy = *this; \
282+
return copyH; \
283+
}
284+
285+
// child managment
286+
virtual inline _typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const {assert(false); return {};}
287+
inline void setChild(const uint8_t ix, _typed_pointer_type<IExprNode> newChild)
288+
{
289+
assert(ix<getChildCount());
290+
setChild_impl(ix,newChild);
291+
}
292+
virtual inline void setChild_impl(const uint8_t ix, _typed_pointer_type<IExprNode> newChild) {assert(false);}
293+
274294
// default is no special checks beyond the above
275295
struct SInvalidCheckArgs
276296
{
@@ -291,7 +311,10 @@ class CFrontendIR final : public CNodePool
291311
}
292312
return false;
293313
}
294-
virtual _typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const = 0;
314+
315+
virtual bool inline reciprocatable() const {return false;}
316+
// unless you override it, you're not supposed to call it
317+
virtual void reciprocate(IExprNode* dst) const {assert(reciprocatable() && dst);}
295318

296319
virtual inline core::string getLabelSuffix() const {return "";}
297320
virtual inline std::string_view getChildName_impl(const uint8_t ix) const {return "";}
@@ -356,6 +379,10 @@ class CFrontendIR final : public CNodePool
356379
}
357380
std::construct_at(reinterpret_cast<SCreationParams<Count>*>(this+1),std::move(params));
358381
}
382+
inline CSpectralVariable(const CSpectralVariable& other)
383+
{
384+
std::uninitialized_copy_n(other.pWonky(),other.getKnotCount(),pWonky());
385+
}
359386

360387
// encapsulation due to padding abuse
361388
inline uint8_t& uvSlot() {return pWonky()->knots.uvSlot();}
@@ -390,11 +417,11 @@ class CFrontendIR final : public CNodePool
390417
{
391418
return sizeof(CSpectralVariable)+sizeof(SCreationParams<Count>);
392419
}
393-
/*
394-
inline uint32_t getSize() const override
420+
// for copying
421+
static inline uint32_t calc_size(const CSpectralVariable& other)
395422
{
396-
return sizeof(CSpectralVariable)+sizeof(SCreationParams<1>)+(getKnotCount()-1)*sizeof(SParameter);
397-
}*/
423+
return sizeof(CSpectralVariable)+sizeof(SCreationParams<1>)+(other.getKnotCount()-1)*sizeof(SParameter);
424+
}
398425

399426
inline operator bool() const {return !invalid(SInvalidCheckArgs{.pool=nullptr,.logger=nullptr});}
400427

@@ -403,8 +430,13 @@ class CFrontendIR final : public CNodePool
403430
{
404431
std::destroy_n(pWonky()->knots.params,getKnotCount());
405432
}
433+
inline _typed_pointer_type<IExprNode> copy(CFrontendIR* ir) const override final
434+
{
435+
auto& pool = ir->getObjectPool();
436+
const uint8_t count = getKnotCount();
437+
return pool.emplace<CSpectralVariable>(*this);
438+
}
406439

407-
inline _typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const override {return {};}
408440
inline bool invalid(const SInvalidCheckArgs& args) const override
409441
{
410442
const auto knotCount = getKnotCount();
@@ -442,13 +474,14 @@ class CFrontendIR final : public CNodePool
442474
private:
443475
SCreationParams<1>* pWonky() {return reinterpret_cast<SCreationParams<1>*>(this+1);}
444476
const SCreationParams<1>* pWonky() const {return reinterpret_cast<const SCreationParams<1>*>(this+1);}
445-
const uint8_t* paramsBeginPadding() const {return pWonky()->knots.params[0].padding; }
477+
const uint8_t* paramsBeginPadding() const {return pWonky()->knots.params[0].padding;}
446478
};
447479
//
448480
class IUnaryOp : public obj_pool_type::INonTrivial, public IExprNode
449481
{
450482
protected:
451483
inline typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const override final {return child;}
484+
inline void setChild_impl(const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override final {child = newChild;}
452485

453486
public:
454487
inline uint8_t getChildCount() const override final {return 1;}
@@ -459,6 +492,8 @@ class CFrontendIR final : public CNodePool
459492
{
460493
protected:
461494
inline typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const override final {return ix ? rhs:lhs;}
495+
inline void setChild_impl(const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override final {*(ix ? &rhs:&lhs) = newChild;}
496+
462497
inline std::string_view getChildName_impl(const uint8_t ix) const override final {return ix ? "rhs":"lhs";}
463498

464499
public:
@@ -476,6 +511,9 @@ class CFrontendIR final : public CNodePool
476511

477512
// you can set the children later
478513
inline CMul() = default;
514+
515+
protected:
516+
COPY_DEFAULT_IMPL
479517
};
480518
class CAdd final : public IBinOp
481519
{
@@ -485,6 +523,9 @@ class CFrontendIR final : public CNodePool
485523

486524
// you can set the children later
487525
inline CAdd() = default;
526+
527+
protected:
528+
COPY_DEFAULT_IMPL
488529
};
489530
// does `1-expression`
490531
class CComplement final : public IUnaryOp
@@ -494,6 +535,9 @@ class CFrontendIR final : public CNodePool
494535

495536
// you can set the children later
496537
inline CComplement() = default;
538+
539+
protected:
540+
COPY_DEFAULT_IMPL
497541
};
498542
// Emission nodes are only allowed in BRDF expressions, not BTDF. To allow different emission on both sides, expressed unambigously.
499543
// Basic Emitter - note that it is of unit radiance so its easier to importance sample
@@ -517,8 +561,10 @@ class CFrontendIR final : public CNodePool
517561
// TODO: semantic flags/metadata (symmetries of the profile)
518562

519563
protected:
520-
inline typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const override {return {};}
564+
COPY_DEFAULT_IMPL
565+
521566
NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override;
567+
522568
NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override;
523569
};
524570
//! Special nodes meant to be used as `CMul::rhs`, their behaviour depends on the IContributor in its MUL node relative subgraph.
@@ -547,9 +593,16 @@ class CFrontendIR final : public CNodePool
547593
// Absorption and thickness of the interface combined into a single variable, eta and `LdotX` is taken from the leaf BTDF node.
548594
// With refractions from Dielectrics, we get just `1/LdotX`, for Delta Transmission we get `1/VdotN` since its the same
549595
typed_pointer_type<CSpectralVariable> perpTransmittance = {};
596+
550597

551598
protected:
599+
COPY_DEFAULT_IMPL
600+
552601
inline typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const override {return perpTransmittance;}
602+
inline void setChild_impl(const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override
603+
{
604+
perpTransmittance = block_allocator_type::_static_cast<CSpectralVariable>(newChild);
605+
}
553606

554607
inline std::string_view getChildName_impl(const uint8_t ix) const override {return "Perpendicular\\nTransmittance";}
555608
NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override;
@@ -572,8 +625,22 @@ class CFrontendIR final : public CNodePool
572625
uint8_t reciprocateEtas : 1 = false;
573626

574627
protected:
628+
COPY_DEFAULT_IMPL
629+
575630
inline typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const override {return ix ? orientedImagEta:orientedRealEta;}
631+
inline void setChild_impl(const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override
632+
{
633+
*(ix ? &orientedImagEta:&orientedRealEta) = block_allocator_type::_static_cast<CSpectralVariable>(newChild);
634+
}
635+
576636
NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override;
637+
638+
inline bool reciprocatable() const override {return true;}
639+
inline void reciprocate(IExprNode* dst) const override
640+
{
641+
(*static_cast<CFresnel*>(dst) = *this).reciprocateEtas = ~reciprocateEtas;
642+
}
643+
577644
inline std::string_view getChildName_impl(const uint8_t ix) const override {return ix ? "Imaginary":"Real";}
578645
NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override;
579646
};
@@ -595,7 +662,14 @@ class CFrontendIR final : public CNodePool
595662
class CThinInfiniteScatterCorrection final : public obj_pool_type::INonTrivial, public IExprNode
596663
{
597664
protected:
598-
inline typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const override final {return ix ? (ix>1 ? reflectanceBottom:extinction):reflectanceTop;}
665+
COPY_DEFAULT_IMPL
666+
667+
inline typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const override {return ix ? (ix>1 ? reflectanceBottom:extinction):reflectanceTop;}
668+
inline void setChild_impl(const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override
669+
{
670+
*(ix ? (ix>1 ? &reflectanceBottom:&extinction):&reflectanceTop) = newChild;
671+
}
672+
599673
NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override;
600674

601675
inline std::string_view getChildName_impl(const uint8_t ix) const override {return ix ? (ix>1 ? "reflectanceBottom":"extinction"):"reflectanceTop";}
@@ -662,9 +736,8 @@ class CFrontendIR final : public CNodePool
662736
// - Delta Reflection -> Any Cook Torrance BxDF with roughness=0 attached as BRDF
663737
// - Smooth Conductor -> above multiplied with Conductor-Fresnel
664738
// - 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)
665-
// - Thindielectric -> Any Cook Torrance BxDF multiplied with Dielectric-Fresnel as BRDF in both sides and a Delta Transmission BTDF with `CThinInfiniteScatterCorrection` on the fresnel
739+
// - Thindielectric -> Any Cook Torrance BxDF multiplied with Dielectric-Fresnel .... TODO description ....
666740
// - Plastic -> Similar to layering the above over Diffuse BRDF, its of uttmost importance that the BTDF is Delta Transmission.
667-
// If one wants to emulate non-linear diffuse TIR color shifts, abuse `CThinInfiniteScatterCorrection`.
668741
class CDeltaTransmission final : public IBxDF
669742
{
670743
public:
@@ -673,7 +746,7 @@ class CFrontendIR final : public CNodePool
673746
inline CDeltaTransmission() = default;
674747

675748
protected:
676-
inline _typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const override {return {};}
749+
COPY_DEFAULT_IMPL
677750
};
678751
//! Because of Schussler et. al 2017 every one of these nodes splits into 2 (if no L dependence) or 3 during canonicalization
679752
// Base diffuse node
@@ -687,8 +760,10 @@ class CFrontendIR final : public CNodePool
687760
SBasicNDFParams ndParams = {};
688761

689762
protected:
690-
inline _typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const override {return {};}
763+
COPY_DEFAULT_IMPL
764+
691765
NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override;
766+
692767
NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override;
693768
};
694769
// Supports anisotropy for all models
@@ -714,49 +789,114 @@ class CFrontendIR final : public CNodePool
714789
// BRDFs and BTDFs components into separate expressions, and also importance sample much better, for details see comments in CTrueIR.
715790
typed_pointer_type<CSpectralVariable> orientedRealEta = {};
716791
//
717-
NDF ndf = NDF::GGX;
792+
NDF ndf : 7 = NDF::GGX;
793+
uint8_t reciprocateEta : 1 = false;
718794

719795
protected:
796+
COPY_DEFAULT_IMPL
797+
720798
inline typed_pointer_type<IExprNode> getChildHandle_impl(const uint8_t ix) const override {return orientedRealEta;}
799+
inline void setChild_impl(const uint8_t ix, _typed_pointer_type<IExprNode> newChild) override {orientedRealEta = block_allocator_type::_static_cast<CSpectralVariable>(newChild);}
800+
721801
NBL_API2 bool invalid(const SInvalidCheckArgs& args) const override;
802+
803+
inline bool reciprocatable() const override {return true;}
804+
inline void reciprocate(IExprNode* dst) const override
805+
{
806+
(*static_cast<CCookTorrance*>(dst) = *this).reciprocateEta = ~reciprocateEta;
807+
}
722808

723809
inline core::string getLabelSuffix() const override {return ndf!=NDF::GGX ? "\\nNDF = Beckmann":"\\nNDF = GGX";}
724810
inline std::string_view getChildName_impl(const uint8_t ix) const override {return "Oriented η";}
725811
NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override;
726812
};
813+
#undef COPY_DEFAULT_IMPL
727814
#undef TYPE_NAME_STR
728815

729816

730-
// Each material comes down to this, YOU MUST NOT MODIFY THE NODES AFTER ADDING THEIR PARENT TO THE ROOT NODES!
731-
// TODO: shall we copy and hand out a new handle?
732-
inline std::span<const typed_pointer_type<const CLayer>> getMaterials() {return m_rootNodes;}
733-
inline bool addMaterial(const typed_pointer_type<const CLayer> rootNode, system::logger_opt_ptr logger)
817+
// basic utilities
818+
inline typed_pointer_type<CMul> createMul(const typed_pointer_type<IExprNode> lhs, const typed_pointer_type<IExprNode> rhs)
734819
{
735-
if (valid(rootNode,logger))
736-
{
737-
m_rootNodes.push_back(rootNode);
738-
return true;
739-
}
740-
return false;
820+
if (!lhs || !rhs) // acceptable premaute optimization
821+
return {};
822+
const auto mulH = getObjectPool().emplace<CMul>();
823+
auto* const mul = getObjectPool().deref(mulH);
824+
mul->lhs = lhs;
825+
mul->rhs = rhs;
826+
return mulH;
827+
}
828+
inline typed_pointer_type<IExprNode> createAdd(const typed_pointer_type<IExprNode> lhs, const typed_pointer_type<IExprNode> rhs)
829+
{
830+
if (!lhs)
831+
return rhs;
832+
if (!rhs)
833+
return lhs;
834+
const auto addH = getObjectPool().emplace<CAdd>();
835+
auto* const add = getObjectPool().deref(addH);
836+
add->lhs = lhs;
837+
add->rhs = rhs;
838+
return addH;
839+
}
840+
inline typed_pointer_type<CComplement> createComplement(const typed_pointer_type<IExprNode> child)
841+
{
842+
if (!child)
843+
return {};
844+
const auto complH = getObjectPool().emplace<CComplement>();
845+
getObjectPool().deref(complH)->child = child;
846+
return complH;
741847
}
742848

743-
// To quickly make a matching backface material from a frontface or vice versa
744-
NBL_API2 typed_pointer_type<IExprNode> reciprocate(const typed_pointer_type<const IExprNode> other);
849+
// To quickly make a fresnel
745850
NBL_API2 typed_pointer_type<CFresnel> createNamedFresnel(const std::string_view name);
746851
inline typed_pointer_type<CFresnel> createConstantMonochromeRealFresnel(const hlsl::float32_t orientedRealEta)
747852
{
748-
const auto fresnelH = getObjectPool().emplace<CFresnel>();
853+
auto& pool = getObjectPool();
854+
const auto fresnelH = pool.emplace<CFresnel>();
749855
CSpectralVariable::SCreationParams<1> params = {};
750856
params.knots.params[0].scale = orientedRealEta;
751-
if (auto* const fresnel=getObjectPool().deref(fresnelH); fresnel)
752-
fresnel->orientedRealEta = getObjectPool().emplace<CSpectralVariable>(std::move(params));
857+
if (auto* const fresnel=pool.deref(fresnelH); fresnel)
858+
fresnel->orientedRealEta = pool.emplace<CSpectralVariable>(std::move(params));
753859
return fresnelH;
754860
}
755861

862+
// To quickly make a matching backface BxDF from a frontface or vice versa
863+
NBL_API2 typed_pointer_type<const IExprNode> reciprocate(const typed_pointer_type<const IExprNode> orig);
864+
865+
// a deep copy of the layer stack, wont copy the BxDFs
866+
NBL_API2 typed_pointer_type<CLayer> copyLayers(const typed_pointer_type<const CLayer> orig);
867+
// Reverse the linked list of layers and reciprocate their Etas
868+
NBL_API2 typed_pointer_type<CLayer> reverse(const typed_pointer_type<const CLayer> orig);
869+
870+
// first query, we check presence of btdf layers all the way through the layer stack
871+
inline bool transmissive(const typed_pointer_type<const CLayer> rootHandle) const
872+
{
873+
auto& pool = getObjectPool();
874+
for (auto layer=pool.deref(rootHandle); layer; layer=pool.deref(layer->coated))
875+
{
876+
// it takes only one layer without transmission to break the chain
877+
if (!layer->btdf)
878+
return false;
879+
}
880+
return true;
881+
}
882+
756883
// IMPORTANT: Two BxDFs are not allowed to be multiplied together.
757884
// NOTE: Right now all Spectral Variables are required to be Monochrome or 3 bucket fixed semantics, all the same wavelength.
758885
// Some things we can't check such as the compatibility of the BTDF with the BRDF (matching indices of refraction, etc.)
759-
bool valid(const typed_pointer_type<const CLayer> rootHandle, system::logger_opt_ptr logger) const;
886+
NBL_API2 bool valid(const typed_pointer_type<const CLayer> rootHandle, system::logger_opt_ptr logger) const;
887+
888+
// Each material comes down to this, YOU MUST NOT MODIFY THE NODES AFTER ADDING THEIR PARENT TO THE ROOT NODES!
889+
// TODO: shall we copy and hand out a new handle?
890+
inline std::span<const typed_pointer_type<const CLayer>> getMaterials() {return m_rootNodes;}
891+
inline bool addMaterial(const typed_pointer_type<const CLayer> rootNode, system::logger_opt_ptr logger)
892+
{
893+
if (valid(rootNode,logger))
894+
{
895+
m_rootNodes.push_back(rootNode);
896+
return true;
897+
}
898+
return false;
899+
}
760900

761901
// For Debug Visualization
762902
struct SDotPrinter final

0 commit comments

Comments
 (0)