You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb none #preflight none #fyi charles.derousiers, chris.kulla [CL 20039247 by Sebastien Hillaire in ue5-main branch]
487 lines
22 KiB
Plaintext
487 lines
22 KiB
Plaintext
|
|
#include "/Engine/Private/Strata/StrataEvaluation.ush"
|
|
#include "/Engine/Private/ShadingCommon.ush"
|
|
|
|
#ifndef STRATA_OPAQUE_MATERIAL
|
|
#define STRATA_OPAQUE_MATERIAL 0
|
|
#endif
|
|
|
|
// STRATA_TODO We assume layers all have the same IOR. But we should take into account the material later.
|
|
// For now, we suppose no IOR changes at interface until they are tracked properly
|
|
#define WATER_IOR 1.0f
|
|
#define AIR_IOR 1.0f
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Utilities
|
|
|
|
void PostProcessSlabBeforeLighting(inout FStrataBSDF BSDF)
|
|
{
|
|
// Two sided lighting can only be evaluated on the bottom layer, together with option SSS. Otherwise, the simple volume will be used.
|
|
const bool bIsThin = BSDF.bIsBottom && BSDF_GETISTHIN(BSDF);
|
|
|
|
// We want simple volumetric only if the MFP input is pluged in, otherwise we avoid dealing with simple volumetric.
|
|
// We also do not want to use the simple volumetric path if we are dealing with an opaque material and this slab is not at the bottom.
|
|
// And when opaque, the bottom layer must be opaque and thus will ahve a solid opaque diffuse color.
|
|
const bool bIsSimpleVolume = !bIsThin && BSDF_GETHASMFP(BSDF) && (!STRATA_OPAQUE_MATERIAL || (STRATA_OPAQUE_MATERIAL && !BSDF.bIsBottom));
|
|
|
|
if (bIsSimpleVolume)
|
|
{
|
|
EnableSlabBSDFSimpleVolumetric(BSDF);
|
|
}
|
|
|
|
if (bIsThin)
|
|
{
|
|
EnableSlabBSDFIsThin(BSDF);
|
|
}
|
|
else
|
|
{
|
|
// If not selected or not possible to have two sided lighting enabled (not bottom layer or SSSProfile is enabled), let's make sure we disable the feature.
|
|
BSDF_SETISTHIN(BSDF, 0);
|
|
}
|
|
}
|
|
|
|
void PostProcessBSDFBeforeLighting(inout FStrataBSDF BSDF)
|
|
{
|
|
// Sanitize BSDF before it is used for forward shading
|
|
StrataSanitizeBSDF(BSDF);
|
|
|
|
switch (BSDF_GETTYPE(BSDF))
|
|
{
|
|
case STRATA_BSDF_TYPE_SLAB:
|
|
{
|
|
PostProcessSlabBeforeLighting(BSDF);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Tree processing functions
|
|
|
|
void UpdateSingleBSDFOperatorCoverageTransmittance(
|
|
FStrataPixelHeader StrataPixelHeader,
|
|
inout FStrataTree StrataTree,
|
|
inout FStrataBSDF CurrentBSDF,
|
|
FStrataIntegrationSettings Settings,
|
|
FStrataAddressing NullStrataAddressing,
|
|
float3 V
|
|
)
|
|
{
|
|
#if STRATA_INLINE_SHADING
|
|
|
|
#define Op StrataTree.Operators[CurrentBSDF.OperatorIndex]
|
|
|
|
// this feature is only needed for development/editor - we can compile it out for a shipping build (see r.CompileShadersForDevelopment cvar help)
|
|
#if USE_DEVELOPMENT_SHADERS
|
|
// We progressively remove top layers but never the bottom one.
|
|
CurrentBSDF.Coverage = Op.LayerDepth < Settings.PeelLayersAboveDepth && !CurrentBSDF.bIsBottom ? 0.0f : CurrentBSDF.Coverage;
|
|
#endif
|
|
|
|
const float Roughness = StrataGetBSDFRoughness(CurrentBSDF);
|
|
|
|
// Extract the true thickness of the BSDF before it is cast into the normalised Slab thickness we used.
|
|
// for opaque rough refraction, we are only interested in thickness of non bottom layers.
|
|
Op.OpaqueRoughRefractThicknessCm = CurrentBSDF.bIsBottom ? 0.0f : CurrentBSDF.ThicknessCm;
|
|
Op.OpaqueRoughRefractCoverage = CurrentBSDF.bIsTop ? CurrentBSDF.Coverage : 0.0f;
|
|
Op.OpaqueRoughRefractTopRoughness = CurrentBSDF.bIsTop ? Roughness : 0.0f;
|
|
|
|
PostProcessBSDFBeforeLighting(CurrentBSDF);
|
|
|
|
// Compute current BSDF transmittance
|
|
float3x3 TangentBasis = StrataGetBSDFSharedBasis_InlineShading(StrataPixelHeader, CurrentBSDF, NullStrataAddressing);
|
|
float3 LEqualN = TangentBasis[2];
|
|
FStrataBSDFContext StrataBSDFContext = StrataCreateBSDFContext(StrataPixelHeader, CurrentBSDF, NullStrataAddressing, V, LEqualN);
|
|
FStrataEvaluateResult BSDFEvaluate = StrataEvaluateBSDF(StrataBSDFContext, Settings);
|
|
|
|
float CurrentBSDFCoverage = CurrentBSDF.Coverage;
|
|
|
|
// We multiply this weight with coverage:
|
|
// - Because when coming from parameter blending, we need to scale the output down according to coverage resulting from parameter blending.
|
|
// - That will be applied on emissive and reflected light, so we do not need to apply that anywhere else.
|
|
// - When coming from non parameter blending, this is equal identity 1 and the operator parsing will continue to correctly weight the BSDF according to the tree.
|
|
CurrentBSDF.LuminanceWeightV = float3(1.0f, 1.0f, 1.0f) * CurrentBSDFCoverage;
|
|
|
|
CurrentBSDF.TransmittanceAboveAlongN = float3(1.0f, 1.0f, 1.0f); // Initialise to "no matter above"
|
|
|
|
// All BSDF are considered for contribution as the top BSDF can have their coverage dip to 0 and then another BSDF need to be considered for the top layer representation.
|
|
CurrentBSDF.TopLayerDataWeight = 1.0f;
|
|
|
|
Op.Coverage = CurrentBSDFCoverage;
|
|
Op.ThroughputAlongV = BSDFEvaluate.ThroughputV;
|
|
Op.TransmittanceAlongN = BSDFEvaluate.TransmittanceAlongN;
|
|
|
|
// Evaluate refraction lobes.
|
|
const FBxDFEnergyTerms EnergyTerms = ComputeGGXSpecEnergyTerms(Roughness, StrataBSDFContext.SatNoV, StrataGetBSDFSpecularColor(CurrentBSDF), StrataGetBSDFSpecularF90(CurrentBSDF));
|
|
{
|
|
const float TopDownInterfaceEta = CurrentBSDF.bIsTop ? AIR_IOR / WATER_IOR : 1.0f;
|
|
FStrataLobeStatistic RefractedLobe = StrataGetRefractedLobe(StrataGetDiracLobe(V), EnergyTerms.E, Roughness, TopDownInterfaceEta);
|
|
// Account for transmittance of the medium (but ignoring scattering for now)
|
|
RefractedLobe.E *= BSDFEvaluate.ThroughputV;
|
|
// Reduce according to coverage. It is a approximation to handle rough refraction reduction according to coverage when relying on a single lob.
|
|
RefractedLobe = StrataWeightLobe(RefractedLobe, CurrentBSDFCoverage);
|
|
Op.TopDownRefractionLobeStat = RefractedLobe;
|
|
}
|
|
|
|
// Same operation but for a bottom up lob
|
|
{
|
|
const float BottomUpInterfaceEta = CurrentBSDF.bIsTop ? WATER_IOR / AIR_IOR : 1.0f;
|
|
const float3 NormalDown = float3(0, 0, -1);
|
|
FStrataLobeStatistic RefractedLobe = StrataGetRefractedLobe(StrataGetDiracLobe(NormalDown), EnergyTerms.E, Roughness, BottomUpInterfaceEta);
|
|
// Account for transmittance of the medium (but ignoring scattering for now)
|
|
RefractedLobe.E *= BSDFEvaluate.ThroughputV;
|
|
// Reduce according to coverage. It is a approximation to handle rough refraction reduction according to coverage when relying on a single lob.
|
|
RefractedLobe = StrataWeightLobe(RefractedLobe, CurrentBSDFCoverage);
|
|
Op.BottomUpRefractionLobeStat = RefractedLobe;
|
|
}
|
|
|
|
Op.TopDownRefractionWorldNormal = TangentBasis[2];
|
|
|
|
#undef Op
|
|
|
|
#endif // STRATA_INLINE_SHADING
|
|
}
|
|
|
|
|
|
void UpdateSingleOperatorCoverageTransmittance(inout FStrataTree StrataTree, int OpIndex)
|
|
{
|
|
#if STRATA_INLINE_SHADING
|
|
|
|
#define Op StrataTree.Operators[OpIndex]
|
|
#define OpA StrataTree.Operators[StrataTree.Operators[OpIndex].LeftIndex]
|
|
#define OpB StrataTree.Operators[StrataTree.Operators[OpIndex].RightIndex]
|
|
|
|
// Propagate bIsTop:
|
|
// - horizontal, add, weight operations: Any of the operand bIsTop should be enough to determine this operator depth
|
|
// - vertical: takes the top layer depth information (operand A)
|
|
// => we always take the operand A information.
|
|
Op.LayerDepth = OpA.LayerDepth;
|
|
|
|
switch (Op.Type)
|
|
{
|
|
|
|
case STRATA_OPERATOR_WEIGHT:
|
|
{
|
|
const float Weight = saturate(Op.Weight);
|
|
Op.Coverage = Weight * OpA.Coverage;
|
|
Op.ThroughputAlongV = OpA.ThroughputAlongV;
|
|
Op.TransmittanceAlongN = OpA.TransmittanceAlongN;
|
|
|
|
Op.TopDownRefractionLobeStat = StrataWeightLobe(OpA.TopDownRefractionLobeStat, Weight);
|
|
Op.TopDownRefractionWorldNormal = OpA.TopDownRefractionWorldNormal * Weight;
|
|
|
|
Op.BottomUpRefractionLobeStat = StrataWeightLobe(OpA.BottomUpRefractionLobeStat, Weight);
|
|
|
|
// Coverage does not affect rough refraction data
|
|
Op.OpaqueRoughRefractThicknessCm = OpA.OpaqueRoughRefractThicknessCm;
|
|
Op.OpaqueRoughRefractCoverage = OpA.OpaqueRoughRefractCoverage * Weight;
|
|
Op.OpaqueRoughRefractTopRoughness = OpA.OpaqueRoughRefractTopRoughness;
|
|
break;
|
|
}
|
|
|
|
case STRATA_OPERATOR_VERTICAL:
|
|
{
|
|
const float3 TopThroughputAlongV = OpA.ThroughputAlongV;
|
|
const float3 TopTransmittanceAlongN = OpA.TransmittanceAlongN;
|
|
const float3 BotThroughputAlongV = OpB.ThroughputAlongV;
|
|
const float3 BotTransmittanceAlongN = OpB.TransmittanceAlongN;
|
|
const float TopCoverage = OpA.Coverage;
|
|
const float BotCoverage = OpB.Coverage;
|
|
|
|
#if 0
|
|
// This is coverage assuming correlation
|
|
const float LayerMaxCoverage = max(TopCoverage, BotCoverage);
|
|
const float LayerMaxCoverageInv = LayerMaxCoverage <= 0.0f ? 1.0f : 1.0f / LayerMaxCoverage;
|
|
|
|
// If a layer has a lower coverage than the other, we account for that when compuing the Transmittance.
|
|
const float TransmittanceOne = 1.0f;
|
|
const float3 TopTransmittanceAdjustedV = lerp(TransmittanceOne, TopThroughputAlongV, TopCoverage * LayerMaxCoverageInv);
|
|
const float3 TopTransmittanceAdjustedL = lerp(TransmittanceOne, TopTransmittanceAlongN, TopCoverage * LayerMaxCoverageInv);
|
|
const float3 BotTransmittanceAdjustedV = lerp(TransmittanceOne, BotThroughputAlongV, BotCoverage * LayerMaxCoverageInv);
|
|
const float3 BotTransmittanceAdjustedL = lerp(TransmittanceOne, BotTransmittanceAlongN, BotCoverage * LayerMaxCoverageInv);
|
|
|
|
// Now we update the material transmittance and coverage
|
|
Op.Coverage = LayerMaxCoverage;
|
|
Op.ThroughputAlongV = TopTransmittanceAdjustedV * BotTransmittanceAdjustedV;
|
|
Op.TransmittanceAlongN = TopTransmittanceAdjustedL * BotTransmittanceAdjustedL;
|
|
#else
|
|
FVerticalLayeringInfo Info = GetVerticalLayeringInfo(TopCoverage, BotCoverage);
|
|
|
|
Op.Coverage = Info.Coverage;
|
|
Op.ThroughputAlongV = Info.TransmittanceOnlyTop * TopThroughputAlongV + Info.TransmittanceOnlyBottom * BotThroughputAlongV + Info.TransmittanceTopAndBottom * TopThroughputAlongV * BotThroughputAlongV;
|
|
Op.TransmittanceAlongN = Info.TransmittanceOnlyTop * TopTransmittanceAlongN + Info.TransmittanceOnlyBottom * BotTransmittanceAlongN + Info.TransmittanceTopAndBottom * TopTransmittanceAlongN * BotTransmittanceAlongN;
|
|
#endif
|
|
|
|
Op.VerticalTop_Coverage = OpA.Coverage;
|
|
Op.VerticalTop_ThroughputAlongV = OpA.ThroughputAlongV;
|
|
Op.VerticalTop_TransmittanceAlongN = OpA.TransmittanceAlongN;
|
|
|
|
Op.VerticalBot_Coverage = OpB.Coverage;
|
|
Op.VerticalBot_ThroughputAlongV = OpB.ThroughputAlongV;
|
|
Op.VerticalBot_TransmittanceAlongN = OpB.TransmittanceAlongN;
|
|
|
|
// Project top lob through bottom layer assuming medium have same IOR.
|
|
{
|
|
const float TopDownInterfaceEta = 1.0f;
|
|
Op.TopDownRefractionLobeStat = StrataGetRefractedLobe(StrataOppositeLobe(OpA.TopDownRefractionLobeStat),
|
|
/*InterfaceFDG*/0.0f, StrataLobeVarianceToRoughness(OpB.TopDownRefractionLobeStat.Sigma), TopDownInterfaceEta);
|
|
Op.TopDownRefractionLobeStat.E *= saturate(OpB.ThroughputAlongV + (1.0f - OpB.Coverage)); // Combine with botton layer medium and specular transmittance
|
|
|
|
// The ratio of BottomSurfaceOnlyVisibile and visible through top layer (with a scale of 0.5 because at maximum both surfaces will be visible) as a fonction of the total coverage.
|
|
const float RefractionNormalMix = saturate((Info.SurfaceBottom + 0.5f * Info.TransmittanceTopAndBottom * dot(TopThroughputAlongV, 0.33.xxx)) / max(STRATA_EPSILON, Info.Coverage));
|
|
Op.TopDownRefractionWorldNormal = lerp(OpA.TopDownRefractionWorldNormal, OpB.TopDownRefractionWorldNormal, RefractionNormalMix);
|
|
}
|
|
|
|
// Project bottom lob through top layer.
|
|
{
|
|
const bool bIsTop = Op.LayerDepth == 0;
|
|
const float BottomUpInterfaceEta = bIsTop ? WATER_IOR / AIR_IOR : 1.0f;
|
|
Op.BottomUpRefractionLobeStat = StrataGetRefractedLobe(StrataOppositeLobe(OpB.BottomUpRefractionLobeStat),
|
|
/*InterfaceFDG*/0.0f, StrataLobeVarianceToRoughness(OpA.BottomUpRefractionLobeStat.Sigma), BottomUpInterfaceEta);
|
|
Op.BottomUpRefractionLobeStat.E *= saturate(OpA.ThroughputAlongV + (1.0f - OpA.Coverage)); // Combine with top layer medium and specular transmittance
|
|
}
|
|
|
|
// Vertical layering accumulates both operators thickness and only keep the top layer roughness and coverage, see OpaqueRoughRefractTopRoughness declaration for the details.
|
|
Op.OpaqueRoughRefractThicknessCm = OpA.OpaqueRoughRefractThicknessCm + OpB.OpaqueRoughRefractThicknessCm;
|
|
Op.OpaqueRoughRefractCoverage = OpA.OpaqueRoughRefractCoverage;
|
|
Op.OpaqueRoughRefractTopRoughness = OpA.OpaqueRoughRefractTopRoughness;
|
|
|
|
Op.VerticalTop_TopDownRefractionLobeStat = OpA.TopDownRefractionLobeStat;
|
|
Op.VerticalTop_BottomUpRefractionLobeStat = OpA.BottomUpRefractionLobeStat;
|
|
break;
|
|
}
|
|
|
|
case STRATA_OPERATOR_HORIZONTAL:
|
|
{
|
|
const float Mix = saturate(Op.Weight);
|
|
const float AMix = 1.0 - Mix;
|
|
const float BMix = Mix;
|
|
|
|
Op.Coverage = AMix * OpA.Coverage + BMix * OpB.Coverage;
|
|
Op.ThroughputAlongV = (AMix * OpA.Coverage * OpA.ThroughputAlongV + BMix * OpB.Coverage * OpB.ThroughputAlongV) / max(STRATA_EPSILON, AMix * OpA.Coverage + BMix * OpB.Coverage);
|
|
Op.TransmittanceAlongN = (AMix * OpA.Coverage * OpA.TransmittanceAlongN + BMix * OpB.Coverage * OpB.TransmittanceAlongN) / max(STRATA_EPSILON, AMix * OpA.Coverage + BMix * OpB.Coverage);
|
|
|
|
Op.TopDownRefractionLobeStat = StrataHorizontalMixLobes(OpA.TopDownRefractionLobeStat, OpB.TopDownRefractionLobeStat, BMix);
|
|
Op.TopDownRefractionWorldNormal = lerp(OpA.TopDownRefractionWorldNormal, OpB.TopDownRefractionWorldNormal, BMix);
|
|
|
|
Op.BottomUpRefractionLobeStat = StrataHorizontalMixLobes(OpA.BottomUpRefractionLobeStat, OpB.BottomUpRefractionLobeStat, BMix);
|
|
|
|
// Horizontal mixes both operators thickness
|
|
Op.OpaqueRoughRefractThicknessCm = lerp(OpA.OpaqueRoughRefractThicknessCm, OpB.OpaqueRoughRefractThicknessCm, BMix);
|
|
Op.OpaqueRoughRefractCoverage = lerp(OpA.OpaqueRoughRefractCoverage, OpB.OpaqueRoughRefractCoverage, BMix);
|
|
Op.OpaqueRoughRefractTopRoughness = lerp(OpA.OpaqueRoughRefractTopRoughness, OpB.OpaqueRoughRefractTopRoughness, BMix);
|
|
break;
|
|
}
|
|
|
|
case STRATA_OPERATOR_ADD:
|
|
{
|
|
const float SafeABSDFCoverage = saturate(OpA.Coverage);
|
|
const float SafeBBSDFCoverage = saturate(OpB.Coverage);
|
|
const float AMixFactor = SafeABSDFCoverage / max(STRATA_EPSILON, SafeABSDFCoverage + SafeBBSDFCoverage);
|
|
|
|
Op.Coverage = saturate(OpA.Coverage + OpB.Coverage);
|
|
Op.ThroughputAlongV = lerp(OpB.ThroughputAlongV, OpA.ThroughputAlongV, AMixFactor);
|
|
Op.TransmittanceAlongN = lerp(OpB.TransmittanceAlongN, OpA.TransmittanceAlongN, AMixFactor);
|
|
|
|
Op.TopDownRefractionLobeStat = StrataHorizontalMixLobes(OpA.TopDownRefractionLobeStat, OpB.TopDownRefractionLobeStat, 0.5f);
|
|
Op.TopDownRefractionWorldNormal = lerp(OpA.TopDownRefractionWorldNormal, OpB.TopDownRefractionWorldNormal, 0.5f);
|
|
|
|
Op.BottomUpRefractionLobeStat = StrataHorizontalMixLobes(OpA.BottomUpRefractionLobeStat, OpB.BottomUpRefractionLobeStat, 0.5f);
|
|
|
|
Op.OpaqueRoughRefractThicknessCm = lerp(OpA.OpaqueRoughRefractThicknessCm, OpB.OpaqueRoughRefractThicknessCm, 0.5f);
|
|
Op.OpaqueRoughRefractCoverage = lerp(OpA.OpaqueRoughRefractCoverage, OpB.OpaqueRoughRefractCoverage, 0.5f);
|
|
Op.OpaqueRoughRefractTopRoughness = lerp(OpA.OpaqueRoughRefractTopRoughness, OpB.OpaqueRoughRefractTopRoughness, 0.5f);
|
|
break;
|
|
}
|
|
|
|
case STRATA_OPERATOR_THINFILM:
|
|
{
|
|
// Thin-film does not affect these property
|
|
Op.Coverage = OpA.Coverage;
|
|
Op.ThroughputAlongV = OpA.ThroughputAlongV;
|
|
Op.TransmittanceAlongN = OpA.TransmittanceAlongN;
|
|
Op.TopDownRefractionLobeStat = OpA.TopDownRefractionLobeStat;
|
|
Op.TopDownRefractionWorldNormal = OpA.TopDownRefractionWorldNormal;
|
|
Op.BottomUpRefractionLobeStat = OpA.BottomUpRefractionLobeStat;
|
|
Op.OpaqueRoughRefractThicknessCm = OpA.OpaqueRoughRefractThicknessCm;
|
|
Op.OpaqueRoughRefractCoverage = OpA.OpaqueRoughRefractCoverage;
|
|
Op.OpaqueRoughRefractTopRoughness = OpA.OpaqueRoughRefractTopRoughness;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
#undef Op
|
|
#undef OpA
|
|
#undef OpB
|
|
|
|
#endif // STRATA_INLINE_SHADING
|
|
}
|
|
|
|
|
|
|
|
#define Op StrataTree.Operators[OpIndex]
|
|
|
|
// Post-update visit operators
|
|
void UpdateAllBSDFWithBottomUpOperatorVisit_Weight(
|
|
inout FStrataTree StrataTree,
|
|
inout FStrataBSDF CurrentBSDF,
|
|
int OpIndex,
|
|
int PreviousIsInputA)
|
|
{
|
|
#if STRATA_INLINE_SHADING
|
|
|
|
const float Weight = saturate(Op.Weight);
|
|
CurrentBSDF.LuminanceWeightV *= Weight;
|
|
CurrentBSDF.Coverage *= Weight;
|
|
|
|
CurrentBSDF.TopLayerDataWeight *= Weight;
|
|
|
|
#endif // STRATA_INLINE_SHADING
|
|
}
|
|
|
|
void UpdateAllBSDFWithBottomUpOperatorVisit_Horizontal(
|
|
inout FStrataTree StrataTree,
|
|
inout FStrataBSDF CurrentBSDF,
|
|
int OpIndex,
|
|
int PreviousIsInputA)
|
|
{
|
|
#if STRATA_INLINE_SHADING
|
|
|
|
const float Mix = saturate(Op.Weight);
|
|
const float AMix = 1.0 - Mix;
|
|
const float BMix = Mix;
|
|
const float Weight = PreviousIsInputA > 0 ? AMix : BMix;
|
|
CurrentBSDF.LuminanceWeightV *= Weight;
|
|
CurrentBSDF.Coverage *= Weight;
|
|
|
|
CurrentBSDF.TopLayerDataWeight *= Weight;
|
|
|
|
#endif // STRATA_INLINE_SHADING
|
|
}
|
|
|
|
void UpdateAllBSDFWithBottomUpOperatorVisit_Vertical(
|
|
inout FStrataTree StrataTree,
|
|
inout FStrataBSDF CurrentBSDF,
|
|
int OpIndex,
|
|
int PreviousIsInputA)
|
|
{
|
|
#if STRATA_INLINE_SHADING
|
|
|
|
// PreviousIsInputA > 0 means it is a BSDF affecting the top layer so we do not affect it by anything for this vertical layering.
|
|
// Otherise, the BSDF comes from the bottom part so we affect it weights from the gathered top layer coverage/transmittance
|
|
CurrentBSDF.LuminanceWeightV *= PreviousIsInputA > 0 ? 1.0f : saturate(1.0f - Op.VerticalTop_Coverage) + saturate(Op.VerticalTop_Coverage) * Op.VerticalTop_ThroughputAlongV;
|
|
CurrentBSDF.TransmittanceAboveAlongN*= PreviousIsInputA > 0 ? 1.0f : saturate(1.0f - Op.VerticalTop_Coverage) + saturate(Op.VerticalTop_Coverage) * Op.VerticalTop_TransmittanceAlongN;
|
|
CurrentBSDF.TransmittanceBelowAlongN*= PreviousIsInputA ==0 ? 1.0f : saturate(1.0f - Op.VerticalBot_Coverage) + saturate(Op.VerticalBot_Coverage) * Op.VerticalBot_TransmittanceAlongN;
|
|
|
|
// If the BSDF is from the bottom, we reduce its contribution according to the top layer coverage only.
|
|
CurrentBSDF.TopLayerDataWeight *= PreviousIsInputA ? 1.0 : saturate(1.0f - Op.VerticalTop_Coverage);
|
|
|
|
if (PreviousIsInputA <= 0) // Only apply to BSDF from the bottom layer
|
|
{
|
|
// We propagate the bottom layer lob up after the top layer top layer simulate the fact that light rays are potentially heavily scattered/refracted by the top layer.
|
|
// The outcome is that we affect the bottom layer roughness to aggregate that fact.
|
|
// STRATA_TODO: take into account thickness (far-field for now).
|
|
|
|
// Evaluate the reflected lobe, as an approximation we only evaluate along the normal.
|
|
const float3 DummyWi = float3(0, 0, 1);
|
|
const float DummyInterfaceDFG = 0.5f;
|
|
FStrataLobeStatistic ReflectedLobe = StrataGetReflectedLobe(StrataGetDiracLobe(DummyWi), DummyInterfaceDFG, StrataGetBSDFRoughness(CurrentBSDF));
|
|
|
|
// Evaluate the reflected lobe after refraction through the top layer of the vertical operator.
|
|
// => This tells how light should be integrated when traveling up thorugh that layer.
|
|
const float TopLayerSigma = Op.VerticalTop_BottomUpRefractionLobeStat.Sigma;
|
|
const bool bIsTop = Op.LayerDepth == 0;
|
|
const float BottomUpInterfaceEta = bIsTop ? WATER_IOR / AIR_IOR : 1.0f;
|
|
FStrataLobeStatistic RefractedLobe = StrataGetRefractedLobe(StrataOppositeLobe(ReflectedLobe), DummyInterfaceDFG, StrataLobeVarianceToRoughness(TopLayerSigma), BottomUpInterfaceEta);/////////////////
|
|
|
|
StrataSetBSDFRoughness(CurrentBSDF, StrataLobeVarianceToRoughness(RefractedLobe.Sigma));
|
|
}
|
|
|
|
#endif // STRATA_INLINE_SHADING
|
|
}
|
|
|
|
// Pre-update visit operators
|
|
void PreUpdateAllBSDFWithBottomUpOperatorVisit_Vertical(
|
|
FStrataPixelHeader StrataPixelHeader,
|
|
inout FStrataTree StrataTree,
|
|
inout FStrataBSDF CurrentBSDF,
|
|
FStrataAddressing NullStrataAddressing,
|
|
inout bool bCanReceiveThinFilm,
|
|
float3 V,
|
|
int OpIndex,
|
|
int PreviousIsInputA)
|
|
{
|
|
#if STRATA_INLINE_SHADING
|
|
|
|
// If the BSDF is coated with a vertical layer, it can no longer receive thin film-coating
|
|
if (PreviousIsInputA <= 0)
|
|
{
|
|
bCanReceiveThinFilm = false;
|
|
}
|
|
|
|
#endif // STRATA_INLINE_SHADING
|
|
}
|
|
|
|
void PreUpdateAllBSDFWithBottomUpOperatorVisit_ThinFilm(
|
|
FStrataPixelHeader StrataPixelHeader,
|
|
inout FStrataTree StrataTree,
|
|
inout FStrataBSDF CurrentBSDF,
|
|
FStrataAddressing NullStrataAddressing,
|
|
inout bool bCanReceiveThinFilm,
|
|
float3 V,
|
|
int OpIndex,
|
|
int PreviousIsInputA)
|
|
{
|
|
#if STRATA_INLINE_SHADING
|
|
|
|
if (bCanReceiveThinFilm && BSDF_GETTYPE(CurrentBSDF) == STRATA_BSDF_TYPE_SLAB)
|
|
{
|
|
const float3x3 TangentBasis = StrataGetBSDFSharedBasis_InlineShading(StrataPixelHeader, CurrentBSDF, NullStrataAddressing);
|
|
const float3 N = TangentBasis[2];
|
|
const float NoV = saturate(dot(N, V));
|
|
StrataGetThinFilmF0F90(NoV, Op.ThinFilmThickness, Op.ThinFilmIOR, SLAB_F0(CurrentBSDF), SLAB_F90(CurrentBSDF));
|
|
}
|
|
|
|
#endif // STRATA_INLINE_SHADING
|
|
}
|
|
|
|
#undef Op
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Legacy materials conversion
|
|
|
|
void LegacyUpdateBSDFsOperators(inout FStrataPixelHeader StrataPixelHeader, inout FStrataTree StrataTree, FStrataIntegrationSettings Settings, FStrataAddressing NullStrataAddressing, float3 V)
|
|
{
|
|
// This must match what is done in StrataConvertLegacyMaterialDynamic
|
|
UpdateSingleBSDFOperatorCoverageTransmittance(StrataPixelHeader, StrataTree, StrataTree.BSDFs[0], Settings, NullStrataAddressing, V);
|
|
UpdateSingleBSDFOperatorCoverageTransmittance(StrataPixelHeader, StrataTree, StrataTree.BSDFs[1], Settings, NullStrataAddressing, V);
|
|
|
|
// MaxDistanceFromLeaves = 1
|
|
UpdateSingleOperatorCoverageTransmittance(StrataTree, /*OperatorIndex*/2);
|
|
UpdateSingleOperatorCoverageTransmittance(StrataTree, /*OperatorIndex*/3);
|
|
// MaxDistanceFromLeaves = 2
|
|
UpdateSingleOperatorCoverageTransmittance(StrataTree, /*OperatorIndex*/4);
|
|
#if STRATA_LEGACY_MATERIAL_APPLIES_FINAL_WEIGHT
|
|
// MaxDistanceFromLeaves = 3
|
|
UpdateSingleOperatorCoverageTransmittance(StrataTree, /*OperatorIndex*/5);
|
|
#endif
|
|
|
|
UpdateAllBSDFWithBottomUpOperatorVisit_Weight (StrataTree, StrataTree.BSDFs[0], /*OperatorIndex*/2, /*PreviousIsInputA*/1);
|
|
UpdateAllBSDFWithBottomUpOperatorVisit_Vertical (StrataTree, StrataTree.BSDFs[0], /*OperatorIndex*/4, /*PreviousIsInputA*/1);
|
|
#if STRATA_LEGACY_MATERIAL_APPLIES_FINAL_WEIGHT
|
|
UpdateAllBSDFWithBottomUpOperatorVisit_Weight (StrataTree, StrataTree.BSDFs[0], /*OperatorIndex*/5, /*PreviousIsInputA*/1);
|
|
#endif
|
|
UpdateAllBSDFWithBottomUpOperatorVisit_Weight (StrataTree, StrataTree.BSDFs[1], /*OperatorIndex*/3, /*PreviousIsInputA*/1);
|
|
UpdateAllBSDFWithBottomUpOperatorVisit_Vertical (StrataTree, StrataTree.BSDFs[1], /*OperatorIndex*/4, /*PreviousIsInputA*/0);
|
|
#if STRATA_LEGACY_MATERIAL_APPLIES_FINAL_WEIGHT
|
|
UpdateAllBSDFWithBottomUpOperatorVisit_Weight (StrataTree, StrataTree.BSDFs[1], /*OperatorIndex*/5, /*PreviousIsInputA*/1);
|
|
#endif
|
|
}
|
|
|
|
|