Skip to content

Commit 0a3d58a

Browse files
author
devsh
committed
rework CFrontend::CThinDielectricScatterCorrection
1 parent 1315b7a commit 0a3d58a

File tree

3 files changed

+55
-43
lines changed

3 files changed

+55
-43
lines changed

examples_tests

include/nbl/asset/material_compiler3/CFrontendIR.h

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -500,37 +500,6 @@ class CFrontendIR : public CNodePool
500500
inline uint32_t getSize() const override { return calc_size(); }
501501
inline CComplement() = default;
502502
};
503-
// Compute Inifinite Scatter and extinction between two parallel infinite planes
504-
// Reflective Component is: R, T E R E T, T E (R E)^3 T, T E (R E)^5 T, ...
505-
// Transmissive Component is: T E T, T E (R E)^2 T, T E (R E)^4 T, ...
506-
// 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 reflectance.
507-
class CThinInfiniteScatterCorrection final : public IExprNode
508-
{
509-
protected:
510-
inline TypedHandle<IExprNode> getChildHandle_impl(const uint8_t ix) const override final {return ix ? (ix!=1 ? extinction:transmittance):reflectance;}
511-
NBL_API bool invalid(const SInvalidCheckArgs& args) const override;
512-
513-
inline std::string_view getChildName_impl(const uint8_t ix) const override {return ix ? (ix>1 ? "extinction":"reflectance"):"transmittance";}
514-
inline void printDot(std::ostringstream& sstr, const core::string& selfID) const override
515-
{
516-
sstr << "\n\t" << selfID << " -> " << selfID << "_computeTransmittance [label=\"computeTransmittance = " << (computeTransmittance ? "true":"false") << "\"]";
517-
}
518-
519-
public:
520-
inline uint8_t getChildCount() const override final {return 3;}
521-
inline const std::string_view getTypeName() const override {return "nbl::CThinInfiniteScatterCorrection";}
522-
523-
// you can set the children later
524-
static inline uint32_t calc_size() { return sizeof(CThinInfiniteScatterCorrection); }
525-
inline uint32_t getSize() const override { return calc_size(); }
526-
inline CThinInfiniteScatterCorrection() = default;
527-
528-
TypedHandle<IExprNode> reflectance = {};
529-
TypedHandle<IExprNode> transmittance = {};
530-
TypedHandle<IExprNode> extinction = {};
531-
// Whether to compute reflectance or transmittance
532-
uint8_t computeTransmittance : 1 = false;
533-
};
534503
// Emission nodes are only allowed in BRDF expressions, not BTDF. To allow different emission on both sides, expressed unambigously.
535504
// Basic Emitter - note that it is of unit radiance so its easier to importance sample
536505
class CEmitter final : public IContributor
@@ -564,13 +533,15 @@ class CFrontendIR : public CNodePool
564533
//! Due to the Helmholtz Reciprocity handling outlined in the comments for the entire front-end you can usually count on these nodes
565534
//! getting applied once using `VdotH` for Cook-Torrance BRDF, twice using `VdotN` and `LdotN` for Diffuse BRDF, and using their
566535
//! complements before multiplication for BTDFs.
567-
//! ----------------------------------------------------------------------------------------------------------------
536+
class IContributorDependant : public IExprNode
537+
{
538+
};
568539
// Beer's Law Node, behaves differently depending on where it is:
569540
// - to get an extinction medium, multiply it with CDeltaTransmission BTDF placed between two BRDFs in the same medium
570541
// - to get a scattering medium between two Layers, create a layer with just a BTDF set up like above
571542
// - to apply the beer's law on a single microfacet or a BRDF or BTDF multiply it with a BxDF
572543
// Note: Even it makes little sense, Beer can be applied to the most outermost BRDF to simulate a correllated "foggy" coating without an extra BRDF layer.
573-
class CBeer final : public IExprNode
544+
class CBeer final : public IContributorDependant
574545
{
575546
public:
576547
inline const std::string_view getTypeName() const override {return "nbl::CBeer";}
@@ -593,7 +564,8 @@ class CFrontendIR : public CNodePool
593564
NBL_API bool invalid(const SInvalidCheckArgs& args) const override;
594565
};
595566
// The "oriented" in the Etas means from frontface to backface, so there's no need to reciprocate them when creating matching BTDF for BRDF
596-
class CFresnel final : public IExprNode
567+
// @kept_secret TODO: Thin Film Interference Fresnel
568+
class CFresnel final : public IContributorDependant
597569
{
598570
public:
599571
inline uint8_t getChildCount() const override {return 2;}
@@ -616,7 +588,43 @@ class CFrontendIR : public CNodePool
616588
inline std::string_view getChildName_impl(const uint8_t ix) const override {return ix ? "Real":"Imaginary";}
617589
NBL_API void printDot(std::ostringstream& sstr, const core::string& selfID) const override;
618590
};
619-
// @kept_secret TODO: Thin Film Interference Fresnel
591+
// Compute Inifinite Scatter and extinction between two parallel infinite planes.
592+
// It's a specialization of what would be a layer of two identical smooth BRDF and BTDF with arbitrary Fresnel function and beer's
593+
// extinction on the BRDFs (not BTDFs), all applied on a per micro-facet basis (layering per microfacet, not whole surface).
594+
//
595+
// 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.
596+
// 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
597+
// meaning that in such case you can no longer optimize the BTDF contributor into a DeltaTransmission but need a CookTorrance with
598+
// 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).
599+
//
600+
// Because we split BRDF and BTDF into separate expressions, what this node computes differs depending on where it gets used:
601+
// 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)
602+
// 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)
603+
// 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`
604+
//
605+
// 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.
606+
class CThinInfiniteScatterCorrection final : public IExprNode
607+
{
608+
protected:
609+
inline TypedHandle<IExprNode> getChildHandle_impl(const uint8_t ix) const override final {return ix ? (ix>1 ? reflectanceBottom:extinction):reflectanceTop;}
610+
NBL_API bool invalid(const SInvalidCheckArgs& args) const override;
611+
612+
inline std::string_view getChildName_impl(const uint8_t ix) const override {return ix ? (ix>1 ? "reflectanceBottom":"extinction"):"reflectanceTop";}
613+
614+
public:
615+
inline uint8_t getChildCount() const override final {return 3;}
616+
inline const std::string_view getTypeName() const override {return "nbl::CThinInfiniteScatterCorrection";}
617+
618+
// you can set the children later
619+
static inline uint32_t calc_size() {return sizeof(CThinInfiniteScatterCorrection);}
620+
inline uint32_t getSize() const override {return calc_size();}
621+
inline CThinInfiniteScatterCorrection() = default;
622+
623+
TypedHandle<IExprNode> reflectanceTop = {};
624+
// optional
625+
TypedHandle<IExprNode> extinction = {};
626+
TypedHandle<IExprNode> reflectanceBottom = {};
627+
};
620628
//! Basic BxDF nodes
621629
// Every BxDF leaf node is supposed to pass WFT test and must not create energy, color and extinction is added on later via multipliers
622630
class IBxDF : public IContributor

src/nbl/asset/material_compiler3/CFrontendIR.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ bool CFrontendIR::CFresnel::invalid(const SInvalidCheckArgs& args) const
4242
args.logger.log("Oriented Real Eta node of correct type must be attached, but is %u of type %s",ELL_ERROR,orientedRealEta,args.pool->getTypeName(orientedRealEta).data());
4343
return true;
4444
}
45-
if (const auto imagEta = args.pool->deref(orientedImagEta); imagEta)
45+
if (const auto imagEta=args.pool->deref(orientedImagEta); imagEta)
4646
{
4747
if (args.isBTDF)
4848
{
@@ -59,25 +59,29 @@ bool CFrontendIR::CFresnel::invalid(const SInvalidCheckArgs& args) const
5959
}
6060
else if (orientedImagEta)
6161
{
62-
args.logger.log("Oriented Imaginary Eta node of correct type must be attached, but is %u of type %s",ELL_ERROR,orientedImagEta,args.pool->getTypeName(orientedImagEta).data());
62+
args.logger.log("Oriented Imaginary Eta node of incorrect type attached, but is %u of type %s",ELL_ERROR,orientedImagEta,args.pool->getTypeName(orientedImagEta).data());
6363
return true;
6464
}
6565
return false;
6666
}
6767

6868
bool CFrontendIR::CThinInfiniteScatterCorrection::invalid(const SInvalidCheckArgs& args) const
6969
{
70-
if (!args.pool->deref(reflectance))
70+
if (!args.pool->deref(reflectanceTop))
7171
{
72-
args.logger.log("Reflectance node of correct type must be attached, but is %u of type %s",ELL_ERROR,reflectance,args.pool->getTypeName(reflectance).data());
72+
args.logger.log("Top reflectance node of correct type must be attached, but is %u of type %s",ELL_ERROR,reflectanceTop,args.pool->getTypeName(reflectanceTop).data());
7373
return true;
7474
}
75-
if (!args.pool->deref(transmittance))
75+
if (extinction && !args.pool->deref(extinction))
7676
{
77-
args.logger.log("Reflectance node of correct type must be attached, but is %u of type %s",ELL_ERROR,transmittance,args.pool->getTypeName(transmittance).data());
77+
args.logger.log("Extinction node of incorrect type attached, but is %u of type %s",ELL_ERROR,extinction,args.pool->getTypeName(extinction).data());
78+
return true;
79+
}
80+
if (!args.pool->deref(reflectanceBottom))
81+
{
82+
args.logger.log("Top reflectance node of correct type must be attached, but is %u of type %s",ELL_ERROR,reflectanceBottom,args.pool->getTypeName(reflectanceBottom).data());
7883
return true;
7984
}
80-
// TODO: check extinction
8185
return false;
8286
}
8387

0 commit comments

Comments
 (0)