431 lines
21 KiB
HLSL
431 lines
21 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
#pragma once
|
|
|
|
#include "/Engine/Private/Substrate/SubstrateEvaluation.ush"
|
|
#include "/Engine/Private/ShadingCommon.ush"
|
|
|
|
#ifndef SUBSTRATE_OPAQUE_MATERIAL
|
|
#error SUBSTRATE_OPAQUE_MATERIAL not defined
|
|
#endif
|
|
|
|
// SUBSTRATE_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
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Tree processing functions
|
|
|
|
#if SUBSTRATE_COMPILER_SUPPORTS_STRUCT_FORWARD_DECLARATION
|
|
void FSubstrateTree::UpdateSingleBSDFOperatorCoverageTransmittance(
|
|
FSubstratePixelHeader SubstratePixelHeader,
|
|
int BSDFIndex,
|
|
FSubstrateIntegrationSettings Settings,
|
|
float3 V)
|
|
#else
|
|
void UpdateSingleBSDFOperatorCoverageTransmittance(
|
|
inout FSubstrateTree InSubstrateTree,
|
|
FSubstratePixelHeader SubstratePixelHeader,
|
|
int BSDFIndex,
|
|
FSubstrateIntegrationSettings Settings,
|
|
float3 V)
|
|
#endif
|
|
{
|
|
#if SUBSTRATE_INLINE_SHADING
|
|
|
|
#if SUBSTRATE_COMPILER_SUPPORTS_STRUCT_FORWARD_DECLARATION
|
|
#define CurrentBSDF this.BSDFs[BSDFIndex]
|
|
#define Op this.Operators[CurrentBSDF.OperatorIndex]
|
|
#else
|
|
#define CurrentBSDF InSubstrateTree.BSDFs[BSDFIndex]
|
|
#define Op InSubstrateTree.Operators[CurrentBSDF.OperatorIndex]
|
|
#endif
|
|
|
|
// 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 = int(Op.LayerDepth) < Settings.PeelLayersAboveDepth && !CurrentBSDF.bIsBottom ? 0.0f : CurrentBSDF.Coverage;
|
|
#endif
|
|
|
|
float CurrentBSDFCoverage = CurrentBSDF.Coverage;
|
|
float Roughness = SubstrateGetBSDFRoughness(CurrentBSDF);
|
|
float3x3 TangentBasis = SubstrateGetBSDFSharedBasis_InlineShading(SubstratePixelHeader, CurrentBSDF);
|
|
|
|
#if USE_DEVELOPMENT_SHADERS
|
|
Roughness = Settings.bRoughnessTracking ? Roughness : 0.0;
|
|
#endif
|
|
|
|
// Sanitize BSDF before it is used for forward shading or exported to GBuffer
|
|
CurrentBSDF.SubstrateSanitizeBSDF();
|
|
|
|
// Distortion and translucency passes accumnulate pass calls for colored transmittance, coverage and TopDownRefractionLobeStat to be setup correctly.
|
|
#if (SUBSTRATE_FASTPATH==0 || MATERIALBLENDING_ANY_TRANSLUCENT) && !SUBSTRATE_OPTIMIZED_UNLIT // !Fast path or rendering translucent materials
|
|
|
|
// 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;
|
|
|
|
const bool bIsSubstrateOpaqueMaterial = SUBSTRATE_OPAQUE_MATERIAL > 0;
|
|
CurrentBSDF.PostProcessBSDFBeforeLighting(bIsSubstrateOpaqueMaterial);
|
|
|
|
// Compute current BSDF transmittance
|
|
float3 LEqualN = TangentBasis[2];
|
|
FSubstrateAddressing NullSubstrateAddressing = (FSubstrateAddressing)0;
|
|
FSubstrateBSDFContext SubstrateBSDFContext = SubstrateCreateBSDFContext(SubstratePixelHeader, CurrentBSDF, NullSubstrateAddressing, V, LEqualN);
|
|
FSubstrateEvaluateResult BSDFEvaluate = SubstrateEvaluateBSDF(SubstrateBSDFContext, Settings);
|
|
|
|
// 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;
|
|
|
|
// Initialise to "no matter above"
|
|
CurrentBSDF.CoverageAboveAlongN = 0.0f;
|
|
CurrentBSDF.TransmittanceAboveAlongN = float3(1.0f, 1.0f, 1.0f);
|
|
|
|
// 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, SubstrateBSDFContext.SatNoV, SubstrateGetBSDFSpecularF0(CurrentBSDF), SubstrateGetBSDFSpecularF90(CurrentBSDF));
|
|
{
|
|
const float TopDownInterfaceEta = CurrentBSDF.bIsTop ? AIR_IOR / WATER_IOR : 1.0f;
|
|
FSubstrateLobeStatistic RefractedLobe = SubstrateGetRefractedLobe(SubstrateGetDiracLobe(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 = SubstrateWeightLobe(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);
|
|
FSubstrateLobeStatistic RefractedLobe = SubstrateGetRefractedLobe(SubstrateGetDiracLobe(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 = SubstrateWeightLobe(RefractedLobe, CurrentBSDFCoverage);
|
|
Op.BottomUpRefractionLobeStat = RefractedLobe;
|
|
}
|
|
|
|
#elif SUBSTRATE_OPTIMIZED_UNLIT
|
|
|
|
CurrentBSDF.LuminanceWeightV = CurrentBSDFCoverage;
|
|
CurrentBSDF.CoverageAboveAlongN = 0.0f;
|
|
CurrentBSDF.TransmittanceAboveAlongN= float3(1.0f, 1.0f, 1.0f);
|
|
CurrentBSDF.TopLayerDataWeight = 1.0f;
|
|
|
|
Op.OpaqueRoughRefractThicknessCm = 0.0;
|
|
Op.OpaqueRoughRefractCoverage = CurrentBSDFCoverage;
|
|
Op.OpaqueRoughRefractTopRoughness = Roughness;
|
|
|
|
Op.Coverage = CurrentBSDFCoverage;
|
|
Op.ThroughputAlongV = UNLIT_TRANSMITTANCE(CurrentBSDF);
|
|
Op.TransmittanceAlongN = UNLIT_TRANSMITTANCE(CurrentBSDF);
|
|
|
|
Op.TopDownRefractionLobeStat = SubstrateGetNullLobe();
|
|
Op.BottomUpRefractionLobeStat = SubstrateGetNullLobe();
|
|
|
|
#else //
|
|
|
|
CurrentBSDF.LuminanceWeightV = CurrentBSDFCoverage;
|
|
CurrentBSDF.CoverageAboveAlongN = 0.0f;
|
|
CurrentBSDF.TransmittanceAboveAlongN= float3(1.0f, 1.0f, 1.0f);
|
|
CurrentBSDF.TopLayerDataWeight = 1.0f;
|
|
|
|
Op.OpaqueRoughRefractThicknessCm = 0.0;
|
|
Op.OpaqueRoughRefractCoverage = CurrentBSDFCoverage;
|
|
Op.OpaqueRoughRefractTopRoughness = Roughness;
|
|
|
|
Op.Coverage = CurrentBSDFCoverage;
|
|
Op.ThroughputAlongV = 1.0f;
|
|
Op.TransmittanceAlongN = 1.0f;
|
|
|
|
Op.TopDownRefractionLobeStat = SubstrateGetNullLobe();
|
|
Op.BottomUpRefractionLobeStat = SubstrateGetNullLobe();
|
|
|
|
#endif // SUBSTRATE_COMPLEXPATH
|
|
|
|
Op.TopDownRefractionWorldNormal = TangentBasis[2];
|
|
|
|
#undef Op
|
|
#undef CurrentBSDF
|
|
|
|
#endif // SUBSTRATE_INLINE_SHADING
|
|
}
|
|
|
|
|
|
void FSubstrateTree::UpdateSingleOperatorCoverageTransmittance(int OpIndex)
|
|
{
|
|
#if SUBSTRATE_INLINE_SHADING
|
|
|
|
#define Op this.Operators[OpIndex]
|
|
#define OpA this.Operators[this.Operators[OpIndex].LeftIndex]
|
|
#define OpB this.Operators[this.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 SUBSTRATE_OPERATOR_WEIGHT:
|
|
{
|
|
const float Weight = saturate(Op.Weight);
|
|
Op.Coverage = Weight * OpA.Coverage;
|
|
Op.ThroughputAlongV = OpA.ThroughputAlongV;
|
|
Op.TransmittanceAlongN = OpA.TransmittanceAlongN;
|
|
|
|
Op.TopDownRefractionLobeStat = SubstrateWeightLobe(OpA.TopDownRefractionLobeStat, Weight);
|
|
Op.TopDownRefractionWorldNormal = OpA.TopDownRefractionWorldNormal * Weight;
|
|
|
|
Op.BottomUpRefractionLobeStat = SubstrateWeightLobe(OpA.BottomUpRefractionLobeStat, Weight);
|
|
|
|
// Coverage does not affect rough refraction data
|
|
Op.OpaqueRoughRefractThicknessCm = OpA.OpaqueRoughRefractThicknessCm;
|
|
Op.OpaqueRoughRefractCoverage = OpA.OpaqueRoughRefractCoverage * Weight;
|
|
Op.OpaqueRoughRefractTopRoughness = OpA.OpaqueRoughRefractTopRoughness;
|
|
break;
|
|
}
|
|
|
|
#if !SUBSTRATE_OPTIMIZED_UNLIT
|
|
|
|
case SUBSTRATE_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 = SubstrateGetRefractedLobe(SubstrateOppositeLobe(OpA.TopDownRefractionLobeStat),
|
|
/*InterfaceFDG*/0.0f, SubstrateLobeVarianceToRoughness(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(SUBSTRATE_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 = SubstrateGetRefractedLobe(SubstrateOppositeLobe(OpB.BottomUpRefractionLobeStat),
|
|
/*InterfaceFDG*/0.0f, SubstrateLobeVarianceToRoughness(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 SUBSTRATE_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(SUBSTRATE_EPSILON, AMix * OpA.Coverage + BMix * OpB.Coverage);
|
|
Op.TransmittanceAlongN = (AMix * OpA.Coverage * OpA.TransmittanceAlongN + BMix * OpB.Coverage * OpB.TransmittanceAlongN) / max(SUBSTRATE_EPSILON, AMix * OpA.Coverage + BMix * OpB.Coverage);
|
|
|
|
Op.TopDownRefractionLobeStat = SubstrateHorizontalMixLobes(OpA.TopDownRefractionLobeStat, OpB.TopDownRefractionLobeStat, BMix);
|
|
Op.TopDownRefractionWorldNormal = lerp(OpA.TopDownRefractionWorldNormal, OpB.TopDownRefractionWorldNormal, BMix);
|
|
|
|
Op.BottomUpRefractionLobeStat = SubstrateHorizontalMixLobes(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 SUBSTRATE_OPERATOR_ADD:
|
|
{
|
|
const float SafeABSDFCoverage = saturate(OpA.Coverage);
|
|
const float SafeBBSDFCoverage = saturate(OpB.Coverage);
|
|
const float AMixFactor = SafeABSDFCoverage / max(SUBSTRATE_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 = SubstrateHorizontalMixLobes(OpA.TopDownRefractionLobeStat, OpB.TopDownRefractionLobeStat, 0.5f);
|
|
Op.TopDownRefractionWorldNormal = lerp(OpA.TopDownRefractionWorldNormal, OpB.TopDownRefractionWorldNormal, 0.5f);
|
|
|
|
Op.BottomUpRefractionLobeStat = SubstrateHorizontalMixLobes(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;
|
|
}
|
|
|
|
#endif // !SUBSTRATE_OPTIMIZED_UNLIT
|
|
|
|
}
|
|
|
|
#undef Op
|
|
#undef OpA
|
|
#undef OpB
|
|
|
|
#endif // SUBSTRATE_INLINE_SHADING
|
|
}
|
|
|
|
|
|
|
|
#define CurrentBSDF this.BSDFs[BSDFIndex]
|
|
#define Op this.Operators[OpIndex]
|
|
|
|
// Post-update visit operators
|
|
void FSubstrateTree::UpdateAllBSDFWithBottomUpOperatorVisit_Weight(
|
|
int BSDFIndex,
|
|
int OpIndex,
|
|
int PreviousIsInputA)
|
|
{
|
|
#if SUBSTRATE_INLINE_SHADING
|
|
|
|
const float Weight = saturate(Op.Weight);
|
|
CurrentBSDF.LuminanceWeightV *= Weight;
|
|
CurrentBSDF.Coverage *= Weight;
|
|
|
|
CurrentBSDF.TopLayerDataWeight *= Weight;
|
|
|
|
#endif // SUBSTRATE_INLINE_SHADING
|
|
}
|
|
|
|
void FSubstrateTree::UpdateAllBSDFWithBottomUpOperatorVisit_Horizontal(
|
|
int BSDFIndex,
|
|
int OpIndex,
|
|
int PreviousIsInputA)
|
|
{
|
|
#if SUBSTRATE_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 // SUBSTRATE_INLINE_SHADING
|
|
}
|
|
|
|
void FSubstrateTree::UpdateAllBSDFWithBottomUpOperatorVisit_Vertical(
|
|
int BSDFIndex,
|
|
int OpIndex,
|
|
int PreviousIsInputA)
|
|
{
|
|
#if SUBSTRATE_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.
|
|
const bool bBSDFComesFromTopLayer = PreviousIsInputA > 0;
|
|
|
|
// Otherise, the BSDF comes from the bottom part so we affect it weights from the gathered top layer coverage/transmittance
|
|
CurrentBSDF.LuminanceWeightV *= bBSDFComesFromTopLayer ? 1.0f : saturate(1.0f - Op.VerticalTop_Coverage) + saturate(Op.VerticalTop_Coverage) * Op.VerticalTop_ThroughputAlongV;
|
|
|
|
// Now we are going to combine the coverage/transmittance above this slab into a new one according to the vertical operator we traverse now, if PreviousIsInputA > 0
|
|
const float BotCover = CurrentBSDF.CoverageAboveAlongN;
|
|
const float TopCover = Op.VerticalTop_Coverage;
|
|
const float3 BotTrans = CurrentBSDF.TransmittanceAboveAlongN;
|
|
const float3 TopTrans = Op.VerticalTop_TransmittanceAlongN;
|
|
FVerticalLayeringInfo Info = GetVerticalLayeringInfo(Op.VerticalTop_Coverage, CurrentBSDF.CoverageAboveAlongN);
|
|
// Pre coverage transmittance is the surface transmitance as if coverage=1, so the final combined transmittance is renormalized accordingly.
|
|
const float3 PreCoverageTransmittance = saturate((Info.TransmittanceOnlyTop * TopTrans + Info.TransmittanceOnlyBottom * BotTrans + Info.TransmittanceTopAndBottom * (TopTrans * BotTrans)) / max(SUBSTRATE_EPSILON, Info.Coverage));
|
|
CurrentBSDF.CoverageAboveAlongN = bBSDFComesFromTopLayer ? CurrentBSDF.CoverageAboveAlongN : Info.Coverage; // Info.Coverage combines the incoming operator Top part coverage and the BSDF current CoverageAboveAlongN
|
|
CurrentBSDF.TransmittanceAboveAlongN = bBSDFComesFromTopLayer ? CurrentBSDF.TransmittanceAboveAlongN : PreCoverageTransmittance; // PreCoverageTransmittance is the combined trnasmittance for the BSDF TransmittanceAboveAlongN computed so far and the top layer of this new vertical operator.
|
|
|
|
// If the BSDF is from the bottom, we reduce its contribution according to the top layer coverage only.
|
|
CurrentBSDF.TopLayerDataWeight *= bBSDFComesFromTopLayer ? 1.0 : saturate(1.0f - Op.VerticalTop_Coverage);
|
|
|
|
#if SUBSTRATE_MATERIAL_ROUGHNESS_TRACKING
|
|
if (!bBSDFComesFromTopLayer) // 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.
|
|
// SUBSTRATE_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;
|
|
FSubstrateLobeStatistic ReflectedLobe = SubstrateGetReflectedLobe(SubstrateGetDiracLobe(DummyWi), DummyInterfaceDFG, SubstrateGetBSDFRoughness(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.
|
|
// However, light should only refract and spread from roughness according to the amount of coverage, and only if transmittance is > 0.
|
|
const float DiracLobeSigma = 0.0f;
|
|
const float TopLayerSigma = lerp(DiracLobeSigma, Op.VerticalTop_BottomUpRefractionLobeStat.Sigma, Op.VerticalTop_Coverage * dot(Op.VerticalTop_TransmittanceAlongN, (1.0f/3.0f).xxx));
|
|
const bool bIsTop = Op.LayerDepth == 0;
|
|
const float BottomUpInterfaceEta = bIsTop ? WATER_IOR / AIR_IOR : 1.0f;
|
|
FSubstrateLobeStatistic RefractedLobe = SubstrateGetRefractedLobe(SubstrateOppositeLobe(ReflectedLobe), DummyInterfaceDFG, SubstrateLobeVarianceToRoughness(TopLayerSigma), BottomUpInterfaceEta);
|
|
|
|
CurrentBSDF.SubstrateSetBSDFRoughness(SubstrateLobeVarianceToRoughness(RefractedLobe.Sigma));
|
|
}
|
|
#endif // SUBSTRATE_MATERIAL_ROUGHNESS_TRACKING
|
|
|
|
#endif // SUBSTRATE_INLINE_SHADING
|
|
}
|
|
|
|
#undef Op
|
|
#undef CurrentBSDF
|
|
|
|
|