Files
UnrealEngine/Engine/Shaders/Private/PathTracing/Material/PathTracingMaterialSampling.ush
2025-05-18 13:04:45 +08:00

685 lines
26 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================================
PathTracingMaterialSampling.usf: Material sampling functions
===============================================================================================*/
#pragma once
#include "PathTracingMaterialCommon.ush"
#include "PathTracingFresnel.ush"
#include "PathTracingLambert.ush"
#if !SIMPLIFIED_MATERIAL_SHADER
#include "PathTracingGlossy.ush"
#if SUBSTRATE_ENABLED
#include "PathTracingSubstrate.ush"
#include "PathTracingSubstrateFoliage.ush"
#include "PathTracingSubstrateSolidGlass.ush"
#include "PathTracingSubstrateThinGlass.ush"
#else
#include "PathTracingSolidGlass.ush"
#include "PathTracingThinGlass.ush"
#include "PathTracingDefaultLit.ush"
#include "PathTracingClearCoat.ush"
#include "PathTracingTwoSidedFoliage.ush"
#include "PathTracingCloth.ush"
#include "PathTracingSubsurfaceProfile.ush"
#endif // SUBSTRATE_ENABLED
#include "PathTracingEye.ush"
#include "PathTracingHair.ush"
#include "PathTracingMedium.ush"
#endif // !SIMPLIFIED_MATERIAL_SHADER
float3 EstimateMaterialAlbedo(FPathTracingPayload Payload)
{
// TODO: make this more accurate by estimating the directional albedo?
float3 Albedo = 0;
switch (Payload.ShadingModelID)
{
#if SIMPLIFIED_MATERIAL_SHADER
case SHADINGMODELID_TWOSIDED_FOLIAGE:
Albedo = Payload.BaseColor + Payload.SubsurfaceColor;
break;
#elif PATHTRACING_SUBSTRATE_PAYLOAD
case SHADINGMODELID_SUBSTRATE:
{
Albedo = Payload.DiffuseColor + Payload.SubsurfaceColor + Payload.SpecularColor + Payload.FuzzAmount * Payload.FuzzColor;
Albedo *= Payload.GetMaxLobeWeight();
break;
}
case SHADINGMODELID_SOLID_GLASS:
{
Albedo = 1.0 + Payload.FuzzAmount * Payload.FuzzColor;
Albedo *= Payload.GetMaxLobeWeight();
break;
}
case SHADINGMODELID_TWOSIDED_FOLIAGE:
{
Albedo = Payload.DiffuseColor + Payload.GetFoliageTransmissionColor() + Payload.SpecularColor + Payload.FuzzAmount * Payload.FuzzColor;
Albedo *= Payload.GetMaxLobeWeight();
break;
}
case SHADINGMODELID_THIN_TRANSLUCENT:
{
const float F0 = F0RGBToF0(Payload.SpecularColor);
Albedo = Payload.HasRefraction() ? lerp(Payload.GetTransmittanceColor(), 1.0, F0) : F0;
Albedo += Payload.DiffuseColor + Payload.FuzzAmount * Payload.FuzzColor;
Albedo *= Payload.GetMaxLobeWeight();
break;
}
case SHADINGMODELID_CLEAR_COAT:
{
Albedo = Payload.DiffuseColor + Payload.SubsurfaceColor + Payload.SpecularColor + Payload.RoughnessData.z * CLEAR_COAT_F0 + Payload.FuzzAmount * Payload.FuzzColor;
Albedo *= Payload.GetMaxLobeWeight();
break;
}
case SHADINGMODELID_EYE:
Albedo = Payload.DiffuseColor + Payload.SubsurfaceColor + Payload.SpecularColor;
break;
case SHADINGMODELID_HAIR:
// SUBSTRATE_TODO: Add more cases here?
#else
case SHADINGMODELID_TWOSIDED_FOLIAGE:
case SHADINGMODELID_SUBSURFACE:
case SHADINGMODELID_PREINTEGRATED_SKIN:
case SHADINGMODELID_SUBSURFACE_PROFILE:
case SHADINGMODELID_EYE:
Albedo = Payload.DiffuseColor + Payload.SpecularColor + Payload.SubsurfaceColor;
break;
case SHADINGMODELID_DEFAULT_LIT:
Albedo = Payload.DiffuseColor + Payload.SpecularColor;
break;
case SHADINGMODELID_CLEAR_COAT:
Albedo = Payload.DiffuseColor + Payload.SpecularColor + Payload.GetClearCoat() * CLEAR_COAT_F0;
break;
case SHADINGMODELID_THIN_TRANSLUCENT:
{
// glass portion is a mixture of pure specular and tinted transmission (when refracting)
// or glass portion is just the specular highlight with transmission accounted for by transparency
const float F0 = F0RGBToF0(Payload.SpecularColor);
Albedo = Payload.HasRefraction() ? lerp(Payload.GetTransmittanceColor(), 1.0, F0) : F0;
break;
}
case SHADINGMODELID_SOLID_GLASS:
{
Albedo = 1.0; // all energy is either reflected or transmitted
break;
}
case SHADINGMODELID_CLOTH:
Albedo = (Payload.DiffuseColor + lerp(Payload.SpecularColor, Payload.GetClothColor(), Payload.GetClothAmount()));
break;
case SHADINGMODELID_HAIR:
#endif
default:
Albedo = Payload.GetBaseColor();
break;
}
Albedo *= Payload.BSDFOpacity;
return Albedo;
}
#if !SIMPLIFIED_MATERIAL_SHADER
#define ROUGHNESS_WEIGHT(r) DenoiserRoughnessWeight(r, PathRoughness)
void AccumulateAlbedoNormal(inout FPathTracingPayload Payload, float3 PathThroughput, float PathRoughness, inout float3 Albedo, inout float3 Normal)
{
float AOVWeight = DenoiserAOVWeight(PathRoughness);
if (AOVWeight == 0)
{
// no contribution
return;
}
PathThroughput *= AOVWeight;
#if PATHTRACING_SUBSTRATE_PAYLOAD
PathThroughput *= Payload.WeightV * Payload.BSDFOpacity * lerp(1.0, Payload.TransmittanceN, Payload.CoverageAboveAlongN);
#endif
float NormThroughput = Luminance(PathThroughput);
// figure out how much the specular lobes should contribute to the total (relative to the current PathRoughness)
// if we are above the MinRoughness, use the PathRoughness as are lower bound instead so that multiple bounces don't accumulate too much albedo
#if PATHTRACING_SUBSTRATE_PAYLOAD
switch (Payload.ShadingModelID)
{
case SHADINGMODELID_SUBSTRATE:
{
const float Roughness0 = Payload.RoughnessData.x;
const float Roughness1 = Payload.RoughnessData.y;
const float SpecBlend = Payload.RoughnessData.z;
const float RoughnessWeight = lerp(ROUGHNESS_WEIGHT(Roughness0), ROUGHNESS_WEIGHT(Roughness1), SpecBlend);
Albedo += PathThroughput * (Payload.DiffuseColor + Payload.SubsurfaceColor + Payload.SpecularColor * RoughnessWeight + Payload.FuzzAmount * Payload.FuzzColor);
Normal += NormThroughput * Payload.WorldNormal;
break;
}
case SHADINGMODELID_SOLID_GLASS:
{
const float Roughness0 = Payload.RoughnessData.x;
const float Roughness1 = Payload.RoughnessData.y;
const float SpecBlend = Payload.RoughnessData.z;
const float RoughnessWeight = lerp(ROUGHNESS_WEIGHT(Roughness0), ROUGHNESS_WEIGHT(Roughness1), SpecBlend);
float GlassAlbedo = 1.0;
Albedo += PathThroughput * (GlassAlbedo * RoughnessWeight + Payload.FuzzAmount * Payload.FuzzColor);
Normal += NormThroughput * Payload.WorldNormal;
break;
}
case SHADINGMODELID_TWOSIDED_FOLIAGE:
{
const float Roughness0 = Payload.RoughnessData.x;
const float Roughness1 = Payload.RoughnessData.y;
const float SpecBlend = Payload.RoughnessData.z;
const float RoughnessWeight = lerp(ROUGHNESS_WEIGHT(Roughness0), ROUGHNESS_WEIGHT(Roughness1), SpecBlend);
Albedo += PathThroughput * (Payload.DiffuseColor + Payload.GetFoliageTransmissionColor() + Payload.SpecularColor * RoughnessWeight + Payload.FuzzAmount * Payload.FuzzColor);
Normal += NormThroughput * Payload.WorldNormal;
break;
}
case SHADINGMODELID_THIN_TRANSLUCENT:
{
const float F0 = F0RGBToF0(Payload.SpecularColor);
const float3 GlassAlbedo = Payload.HasRefraction() ? lerp(Payload.GetTransmittanceColor(), 1.0, F0) : F0;
const float Roughness0 = Payload.RoughnessData.x;
const float Roughness1 = Payload.RoughnessData.y;
const float SpecBlend = Payload.RoughnessData.z;
const float RoughnessWeight = lerp(ROUGHNESS_WEIGHT(Roughness0), ROUGHNESS_WEIGHT(Roughness1), SpecBlend);
Albedo += PathThroughput * (RoughnessWeight * GlassAlbedo + Payload.DiffuseColor + Payload.FuzzAmount * Payload.FuzzColor);
Normal += NormThroughput * RoughnessWeight * Payload.WorldNormal;
}
case SHADINGMODELID_CLEAR_COAT:
{
const float Roughness0 = Payload.RoughnessData.x;
const float Roughness1 = Payload.RoughnessData.y;
const float SpecBlend = Payload.RoughnessData.z;
const float Roughness0Weight = ROUGHNESS_WEIGHT(Roughness0);
const float Roughness1Weight = SpecBlend * ROUGHNESS_WEIGHT(Roughness1);
Albedo += PathThroughput * (Payload.DiffuseColor + Roughness0Weight * Payload.SpecularColor + Roughness1Weight * CLEAR_COAT_F0 + Payload.FuzzAmount * Payload.FuzzColor);
Normal += NormThroughput * Payload.WorldNormal;
break;
}
case SHADINGMODELID_EYE:
{
const float RoughnessWeight = ROUGHNESS_WEIGHT(Payload.GetEyeRoughness());
Albedo += PathThroughput * (Payload.DiffuseColor + RoughnessWeight * Payload.SpecularColor + Payload.SubsurfaceColor);
Normal += NormThroughput * Payload.WorldNormal;
break;
}
default:
{
Albedo += PathThroughput * Payload.GetBaseColor();
Normal += NormThroughput * Payload.WorldNormal;
break;
}
}
#else
const float RoughnessWeight = ROUGHNESS_WEIGHT(Payload.Roughness);
const float BrdfWeight = Payload.BSDFOpacity;
switch (Payload.ShadingModelID)
{
case SHADINGMODELID_TWOSIDED_FOLIAGE:
case SHADINGMODELID_SUBSURFACE:
case SHADINGMODELID_PREINTEGRATED_SKIN:
case SHADINGMODELID_SUBSURFACE_PROFILE:
case SHADINGMODELID_EYE:
Albedo += BrdfWeight * PathThroughput * (Payload.DiffuseColor + RoughnessWeight * Payload.SpecularColor + Payload.SubsurfaceColor);
Normal += BrdfWeight * NormThroughput * Payload.WorldNormal;
break;
case SHADINGMODELID_DEFAULT_LIT:
case SHADINGMODELID_CLEAR_COAT: // TODO: can we be more precise with clearcoat?
Albedo += BrdfWeight * PathThroughput * (Payload.DiffuseColor + RoughnessWeight * Payload.SpecularColor);
Normal += BrdfWeight * NormThroughput * Payload.WorldNormal;
break;
case SHADINGMODELID_CLOTH:
Albedo += BrdfWeight * PathThroughput * (Payload.DiffuseColor + lerp(RoughnessWeight * Payload.SpecularColor, Payload.GetClothColor(), Payload.GetClothAmount()));
Normal += BrdfWeight * NormThroughput * Payload.WorldNormal;
break;
case SHADINGMODELID_THIN_TRANSLUCENT:
{
const float F0 = F0RGBToF0(Payload.SpecularColor);
const float3 GlassAlbedo = Payload.HasRefraction() ? lerp(Payload.GetTransmittanceColor(), 1.0, F0) : F0;
Albedo += BrdfWeight * RoughnessWeight * PathThroughput * GlassAlbedo;
Normal += BrdfWeight * RoughnessWeight * NormThroughput * Payload.WorldNormal;
break;
}
case SHADINGMODELID_SOLID_GLASS:
{
const float GlassAlbedo = 1.0; // solid glass has unit albedo
Albedo += BrdfWeight * RoughnessWeight * PathThroughput * GlassAlbedo;
Normal += BrdfWeight * RoughnessWeight * NormThroughput * Payload.WorldNormal;
break;
}
default:
Albedo += BrdfWeight * PathThroughput * Payload.BaseColor;
Normal += BrdfWeight * NormThroughput * Payload.WorldNormal;
break;
}
#endif // PATHTRACING_SUBSTRATE_PAYLOAD
}
#undef ROUGHNESS_WEIGHT
void AccumulateAlbedo(float3 SigmaS, float3 PathThroughput, float PathRoughness, inout float3 Albedo)
{
PathThroughput *= DenoiserAOVWeight(PathRoughness);
// Volume acts as diffuse scattering, so make it contribute fully (like DiffuseColor above)
Albedo += SigmaS * PathThroughput;
}
#endif // SIMPLIFIED_MATERIAL_SHADER
// returns an average roughness for the whole material (for the path roughness of shadow rays from this material)
float GetAverageRoughness(FPathTracingPayload Payload)
{
#if SIMPLIFIED_MATERIAL_SHADER
return 1.0;
#elif PATHTRACING_SUBSTRATE_PAYLOAD
float AvgRoughness = Payload.RoughnessData.x;
switch (Payload.ShadingModelID)
{
case SHADINGMODELID_SUBSTRATE:
{
AvgRoughness = max(AvgRoughness, Payload.RoughnessData.y);
// Only non-glass slabs contain diffuse
float DiffuseFraction = Payload.HasRefraction() ? 1.0 : F0RGBToMetallic(Payload.SpecularColor);
AvgRoughness = lerp(1.0, AvgRoughness, DiffuseFraction);
break;
}
case SHADINGMODELID_SOLID_GLASS:
{
// NOTE: this case does not contain a diffuse lobe yet
AvgRoughness = max(AvgRoughness, Payload.RoughnessData.y);
break;
}
case SHADINGMODELID_THIN_TRANSLUCENT:
{
AvgRoughness = max(AvgRoughness, Payload.RoughnessData.y);
float DiffuseFraction = LobeSelectionProb(Payload.DiffuseColor + Payload.FuzzAmount * Payload.FuzzColor, Payload.SpecularColor);
AvgRoughness = lerp(1.0, AvgRoughness, DiffuseFraction);
break;
}
case SHADINGMODELID_CLEAR_COAT:
{
AvgRoughness = max(AvgRoughness, Payload.RoughnessData.y);
float DiffuseFraction = F0RGBToMetallic(Payload.SpecularColor + Payload.RoughnessData.z * CLEAR_COAT_F0);
AvgRoughness = lerp(1.0, AvgRoughness, DiffuseFraction);
break;
}
case SHADINGMODELID_EYE:
{
float DiffuseFraction = F0RGBToMetallic(Payload.SpecularColor);
AvgRoughness = lerp(1.0, AvgRoughness, DiffuseFraction);
break;
}
// SUBSTRATE_TODO: do we need special cases for the other slab types?
}
return AvgRoughness;
#else
float Roughness = Payload.Roughness;
switch (Payload.ShadingModelID)
{
case SHADINGMODELID_CLEAR_COAT:
Roughness = max(Roughness, Payload.GetClearCoatRoughness());
break;
case SHADINGMODELID_SUBSURFACE_PROFILE:
{
float3 DualRoughnessData = Payload.GetDualRoughnessSpecular();
Roughness = max(DualRoughnessData.x, DualRoughnessData.y);
break;
}
case SHADINGMODELID_THIN_TRANSLUCENT:
case SHADINGMODELID_SOLID_GLASS:
return Roughness; // both cases are purely specular, no diffuse
}
float AvgRoughness = lerp(1.0 /* diffuse lobe */, Roughness, Payload.Metallic);
return AvgRoughness;
#endif
}
void AdjustDiffuseSSSContribution(inout float3 DiffColor, inout float3 SSSColor, inout float3 Radius)
{
// Below this radius, lerp to diffuse so we can avoid numerical issues in the random walk itself which lead to darkening
const float MinRadius = 0.02;
float3 Blend = saturate(Radius / MinRadius);
DiffColor += SSSColor * (1 - Blend);
SSSColor *= Blend;
Radius = max(Radius, MinRadius);
}
#if !SIMPLIFIED_MATERIAL_SHADER
void AdjustPayloadAfterUnpack(inout FPathTracingPayload HitPayload, bool bApplyOverrides)
{
// This function can also do a bit of per-material post-processing to simplify the rest of the code
if (bApplyOverrides)
{
HitPayload.DiffuseColor = HitPayload.DiffuseColor * View.DiffuseOverrideParameter.w + View.DiffuseOverrideParameter.xyz;
HitPayload.SpecularColor = HitPayload.SpecularColor * View.SpecularOverrideParameter.w + View.SpecularOverrideParameter.xyz;
}
#if PATHTRACING_SUBSTRATE_PAYLOAD
switch (HitPayload.ShadingModelID)
{
case SHADINGMODELID_SUBSTRATE:
{
// NOTE: if radius is 0 the sss color will get flipped back into diffuse
HitPayload.SubsurfaceColor = HitPayload.DiffuseColor;
HitPayload.DiffuseColor = 0;
AdjustDiffuseSSSContribution(HitPayload.DiffuseColor, HitPayload.SubsurfaceColor, HitPayload.MeanFreePath);
break;
}
case SHADINGMODELID_TWOSIDED_FOLIAGE:
{
if (bApplyOverrides)
{
HitPayload.SetFoliageTransmissionColor(HitPayload.GetFoliageTransmissionColor() * View.DiffuseOverrideParameter.w + View.DiffuseOverrideParameter.xyz);
}
break;
}
case SHADINGMODELID_CLEAR_COAT:
{
if (bApplyOverrides)
{
HitPayload.RoughnessData.z = HitPayload.RoughnessData.z * View.SpecularOverrideParameter.w + View.SpecularOverrideParameter.x;
}
break;
}
case SHADINGMODELID_EYE:
{
// eye still needs a diffuse component for the iris center
float IrisMask = HitPayload.GetEyeIrisMask();
HitPayload.SubsurfaceColor = HitPayload.DiffuseColor - IrisMask * HitPayload.DiffuseColor;
HitPayload.DiffuseColor *= IrisMask;
break;
}
}
#else
switch (HitPayload.ShadingModelID)
{
case SHADINGMODELID_UNLIT:
{
if (bApplyOverrides)
{
HitPayload.BaseColor = HitPayload.BaseColor * View.DiffuseOverrideParameter.w + View.DiffuseOverrideParameter.xyz;
}
break;
}
case SHADINGMODELID_SUBSURFACE:
case SHADINGMODELID_PREINTEGRATED_SKIN:
{
HitPayload.SubsurfaceRadius = HitPayload.GetSubsurfaceRadius();
// fallthrough to case below to avoid over-bright SSS
}
case SHADINGMODELID_TWOSIDED_FOLIAGE:
{
HitPayload.SubsurfaceColor = HitPayload.GetSubsurfaceColor();
float3 Front = HitPayload.DiffuseColor;
float3 Back = HitPayload.SubsurfaceColor;
float3 Sum = Front + Back;
float S = max(Sum.x, max(Sum.y, Sum.z));
if (S > 1)
{
// nudge the material back to something physically plausible
// NOTE: we ignore spec here since it should be accounted for by the brdf model itself
HitPayload.DiffuseColor = Front / S;
HitPayload.SubsurfaceColor = Back / S;
}
if (HitPayload.ShadingModelID != SHADINGMODELID_TWOSIDED_FOLIAGE)
{
AdjustDiffuseSSSContribution(HitPayload.DiffuseColor, HitPayload.SubsurfaceColor, HitPayload.SubsurfaceRadius);
}
if (bApplyOverrides)
{
HitPayload.SubsurfaceColor = HitPayload.SubsurfaceColor * View.DiffuseOverrideParameter.w + View.DiffuseOverrideParameter.xyz;
}
break;
}
case SHADINGMODELID_SUBSURFACE_PROFILE:
{
HitPayload.SubsurfaceRadius = HitPayload.GetSubsurfaceRadius();
// NOTE: if radius is 0 the sss color will get flipped back into diffuse
HitPayload.SubsurfaceColor = HitPayload.DiffuseColor;
HitPayload.DiffuseColor = 0;
AdjustDiffuseSSSContribution(HitPayload.DiffuseColor, HitPayload.SubsurfaceColor, HitPayload.SubsurfaceRadius);
break;
}
case SHADINGMODELID_EYE:
{
HitPayload.SubsurfaceRadius = HitPayload.GetSubsurfaceRadius();
// eye still needs a diffuse component for the iris center
float IrisMask = HitPayload.GetEyeIrisMask();
HitPayload.SubsurfaceColor = HitPayload.DiffuseColor - IrisMask * HitPayload.DiffuseColor;
HitPayload.DiffuseColor *= IrisMask;
AdjustDiffuseSSSContribution(HitPayload.DiffuseColor, HitPayload.SubsurfaceColor, HitPayload.SubsurfaceRadius);
break;
}
case SHADINGMODELID_HAIR:
{
if (bApplyOverrides)
{
HitPayload.BaseColor = HitPayload.BaseColor * View.SpecularOverrideParameter.w + View.SpecularOverrideParameter.xyz;
HitPayload.Specular = HitPayload.Specular * View.SpecularOverrideParameter.w + View.SpecularOverrideParameter.x;
}
break;
}
case SHADINGMODELID_CLEAR_COAT:
{
if (bApplyOverrides)
{
HitPayload.SetClearCoat(HitPayload.GetClearCoat() * View.SpecularOverrideParameter.w + View.SpecularOverrideParameter.x);
}
break;
}
case SHADINGMODELID_CLOTH:
{
if (bApplyOverrides)
{
HitPayload.SetClothColor(HitPayload.GetClothColor() * View.SpecularOverrideParameter.w + View.SpecularOverrideParameter.xyz);
}
break;
}
}
#endif // PATHTRACING_SUBSTRATE_PAYLOAD
}
struct FSSSRandomWalkInfo
{
float3 Color;
float3 Radius;
float3 Weight; // multiplier for the path throughput to take into account energy conservation from specular above the SSS
float Prob; // probability of choosing to perform the random walk
float G;
};
FSSSRandomWalkInfo GetMaterialSSSInfo(FPathTracingPayload Payload, float3 V_World)
{
FSSSRandomWalkInfo Result = (FSSSRandomWalkInfo)0;
#if PATHTRACING_SUBSTRATE_PAYLOAD
if (any(Payload.MeanFreePath > 0.0))
{
Result.Color = Payload.SubsurfaceColor;
Result.Radius = Payload.MeanFreePath;
Result.G = Payload.PhaseG;
if (Payload.ShadingModelID == SHADINGMODELID_SUBSTRATE)
{
FSubstrateSingleSlabData Data = (FSubstrateSingleSlabData)0;
Data.PrepareBSDF(V_World, Payload, true);
Result.Weight = Payload.WeightV * Payload.BSDFOpacity * Data.DiffuseWeight;
Result.Prob = Data.LobePdf.x;
}
else if (Payload.ShadingModelID == SHADINGMODELID_CLEAR_COAT)
{
FSubstrateClearCoatData Data = (FSubstrateClearCoatData)0;
Data.PrepareBSDF(V_World, Payload, true);
Result.Weight = Payload.WeightV * Payload.BSDFOpacity * Data.DiffuseWeight;
Result.Prob = Data.LobePdf.x;
}
else if (Payload.ShadingModelID == SHADINGMODELID_EYE)
{
const float NoV = saturate(dot(Payload.WorldNormal, V_World));
const float3 SpecE = ComputeGGXSpecEnergyTermsRGB(Payload.GetEyeRoughness(), NoV, Payload.SpecularColor).E;
const float DiffWeight = (1 - Luminance(SpecE));
const float3 SSSAlbedo = DiffWeight * Payload.SubsurfaceColor;
const float3 DiffAlbedo = DiffWeight * Payload.DiffuseColor;
const float3 SpecAlbedo = SpecE;
Result.Prob = LobeSelectionProb(SSSAlbedo, DiffAlbedo + SpecAlbedo);
Result.Weight = Payload.BSDFOpacity * DiffWeight;
}
}
#else
Result.Color = Payload.SubsurfaceColor;
Result.Radius = Payload.SubsurfaceRadius;
Result.Weight = 0;
Result.Prob = 0;
Result.G = Payload.GetSubsurfacePhaseFunction();
if (Payload.IsSubsurfaceMaterial())
{
const float NoV = saturate(dot(Payload.WorldNormal, V_World));
float3 SpecE = 0;
if (Payload.ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE)
{
// dual spec
const float3 DualRoughnessData = Payload.GetDualRoughnessSpecular();
const float3 SpecE0 = ComputeGGXSpecEnergyTermsRGB(DualRoughnessData.x, NoV, Payload.SpecularColor).E;
const float3 SpecE1 = ComputeGGXSpecEnergyTermsRGB(DualRoughnessData.y, NoV, Payload.SpecularColor).E;
SpecE = lerp(SpecE0, SpecE1, DualRoughnessData.z);
}
else
{
// single spec
SpecE = ComputeGGXSpecEnergyTermsRGB(Payload.Roughness, NoV, Payload.SpecularColor).E;
}
const float3 SSSLobeAlbedo = (1 - SpecE) * Payload.SubsurfaceColor;
const float3 DiffLobeAlbedo = (1 - SpecE) * Payload.DiffuseColor;
const float3 SpecLobeAlbedo = SpecE;
Result.Prob = LobeSelectionProb(SSSLobeAlbedo, DiffLobeAlbedo + SpecLobeAlbedo);
Result.Weight = Payload.BSDFOpacity * (1 - SpecE);
}
#endif // PATHTRACING_SUBSTRATE_PAYLOAD
return Result;
}
void RemoveMaterialSSS(inout FPathTracingPayload Payload)
{
// Tweak the payload to remove subsurface scattering without chaning the overall color
// This function is free to assume this material contains SSS
Payload.DiffuseColor += Payload.SubsurfaceColor;
Payload.DiffuseColor = saturate(Payload.DiffuseColor);
Payload.SubsurfaceColor = 0.0;
}
#endif // SIMPLIFIED_MATERIAL_SHADER
FMaterialSample SampleMaterial(
float3 V_World,
FPathTracingPayload Payload,
float3 RandSample
)
{
#if SIMPLIFIED_MATERIAL_SHADER
return Simplified_SampleMaterial(Payload, RandSample);
//return Lambert_SampleMaterial(Payload, RandSample);
#else
switch (Payload.ShadingModelID)
{
case SHADINGMODELID_UNLIT:
return Volumetric_SampleMaterial(V_World, Payload, RandSample);
#if PATHTRACING_SUBSTRATE_PAYLOAD
case SHADINGMODELID_SUBSTRATE:
return Substrate_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_SOLID_GLASS:
return SubstrateSolidGlass_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_TWOSIDED_FOLIAGE:
return SubstrateFoliage_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_THIN_TRANSLUCENT:
return SubstrateThinGlass_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_CLEAR_COAT:
return SubstrateClearCoat_SampleMaterial(V_World, Payload, RandSample);
#else
case SHADINGMODELID_DEFAULT_LIT:
case SHADINGMODELID_SUBSURFACE:
case SHADINGMODELID_PREINTEGRATED_SKIN:
return DefaultLit_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_THIN_TRANSLUCENT:
return RoughThinGlass_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_SOLID_GLASS:
return RoughGlass_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_SUBSURFACE_PROFILE:
return SubsurfaceProfile_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_CLOTH:
return Cloth_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_CLEAR_COAT:
return ClearCoat_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_TWOSIDED_FOLIAGE:
return TwoSidedFoliage_SampleMaterial(V_World, Payload, RandSample);
#endif // PATHTRACING_SUBSTRATE_PAYLOAD
case SHADINGMODELID_EYE:
return Eye_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_HAIR:
return Hair_SampleMaterial(V_World, Payload, RandSample);
case SHADINGMODELID_MEDIUM:
return Medium_SampleMaterial(V_World, Payload, RandSample);
default:
return Lambert_SampleMaterial(Payload, RandSample);
}
return NullMaterialSample();
#endif // SIMPLIFIED_MATERIAL_SHADER
}
FMaterialEval EvalMaterial(
float3 V_World,
float3 L_World,
FPathTracingPayload Payload,
float2 DiffuseSpecularScale
)
{
#if SIMPLIFIED_MATERIAL_SHADER
return Simplified_EvalMaterial(L_World, Payload);
//return Lambert_EvalMaterial(L_World, Payload);
#else
switch (Payload.ShadingModelID)
{
case SHADINGMODELID_UNLIT:
return Volumetric_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
#if PATHTRACING_SUBSTRATE_PAYLOAD
case SHADINGMODELID_SUBSTRATE:
return Substrate_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_SOLID_GLASS:
return SubstrateSolidGlass_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_TWOSIDED_FOLIAGE:
return SubstrateFoliage_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_THIN_TRANSLUCENT:
return SubstrateThinGlass_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_CLEAR_COAT:
return SubstrateClearCoat_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
#else
case SHADINGMODELID_DEFAULT_LIT:
case SHADINGMODELID_SUBSURFACE:
case SHADINGMODELID_PREINTEGRATED_SKIN:
return DefaultLit_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_THIN_TRANSLUCENT:
return RoughThinGlass_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_SOLID_GLASS:
return RoughGlass_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_SUBSURFACE_PROFILE:
return SubsurfaceProfile_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_CLOTH:
return Cloth_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_CLEAR_COAT:
return ClearCoat_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_TWOSIDED_FOLIAGE:
return TwoSidedFoliage_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
#endif // PATHTRACING_SUBSTRATE_PAYLOAD
case SHADINGMODELID_EYE:
return Eye_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_HAIR:
return Hair_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
case SHADINGMODELID_MEDIUM:
return Medium_EvalMaterial(V_World, L_World, Payload, DiffuseSpecularScale);
default:
return Lambert_EvalMaterial(L_World, Payload, DiffuseSpecularScale);
}
return NullMaterialEval();
#endif // SIMPLIFIED_MATERIAL_SHADER
}