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

145 lines
4.5 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#ifndef BXDF_ENERGY_TYPE
#error BXDF_ENERGY_TYPE needs to be defined
#endif
#ifndef BXDF_ENERGY_SUFFIX
#error BXDF_ENERGY_SUFFIX needs to be defined
#endif
#ifndef IS_BXDF_ENERGY_TYPE_ACHROMATIC
#error IS_BXDF_ENERGY_TYPE_ACHROMATIC needs to be defined
#endif
#include "ShadingCommon.ush"
///////////////////////////////////////////////////////////////////////////////////////////////////
// Energy terms
BXDF_ENERGY_TYPE BXDF_ENERGY_SUFFIX(GetF0F90)(float3 InF0)
{
#if IS_BXDF_ENERGY_TYPE_ACHROMATIC
return max3(InF0.x, InF0.y, InF0.z);
#else
return InF0;
#endif
}
float GetEnergyLuminance(BXDF_ENERGY_TYPE InE)
{
#if IS_BXDF_ENERGY_TYPE_ACHROMATIC
return InE;
#else
return Luminance(InE);
#endif
}
struct BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms)
{
BXDF_ENERGY_TYPE W; // overall weight to scale the lobe BxDF by to ensure energy conservation
BXDF_ENERGY_TYPE E; // Directional albedo of the lobe for energy preservation and lobe picking
};
// Given a split-sum approximation of directional albedo for a BxDF, compute multiple scattering weight and multiple scattering directional albedo
// while taking into account the fresnel term (assumed to be F_Schlick)
BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms) BXDF_ENERGY_SUFFIX(ComputeFresnelEnergyTerms)(float2 E, float3 InF0, float3 InF90)
{
BXDF_ENERGY_TYPE F0 = BXDF_ENERGY_SUFFIX(GetF0F90)(InF0);
BXDF_ENERGY_TYPE F90 = BXDF_ENERGY_SUFFIX(GetF0F90)(InF90);
BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms) Result;
// [2] Eq 16: this restores the missing energy of the bsdf, while also accounting for the fact that the fresnel term causes some energy to be absorbed
// NOTE: using F0 here is an approximation, but for schlick fresnel Favg is almost exactly equal to F0
#if USE_DEVELOPMENT_SHADERS
Result.W = View.bShadingEnergyConservation ? (1.0 + F0 * ((1 - E.x) / E.x)) : 1.0f;
#else
Result.W = 1.0 + F0 * ((1 - E.x) / E.x);
#endif
// Now estimate the amount of energy reflected off this specular lobe so that we can remove it from underlying BxDF layers (like diffuse)
// This relies on the split-sum approximation as in [3] Sec 4.
// This term can also be useful to compute the probability of choosing among lobes
Result.E = Result.W * (E.x * F0 + E.y * (F90 - F0));
return Result;
}
BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms) BXDF_ENERGY_SUFFIX(ComputeGGXSpecEnergyTerms)(float Roughness, float NoV, float3 F0, float3 F90)
{
BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms) Out;
#if USE_ENERGY_CONSERVATION > 0
{
Out = BXDF_ENERGY_SUFFIX(ComputeFresnelEnergyTerms)(GGXEnergyLookup(Roughness, NoV), F0, F90);
}
#else
{
Out.W = 1.0f;
Out.E = BXDF_ENERGY_SUFFIX(GetF0F90)(F0);
}
#endif
return Out;
}
BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms) BXDF_ENERGY_SUFFIX(ComputeGGXSpecEnergyTerms)(float Roughness, float NoV, float3 F0)
{
const float F90 = F0RGBToMicroOcclusion(F0);
return BXDF_ENERGY_SUFFIX(ComputeGGXSpecEnergyTerms)(Roughness, NoV, F0, F90);
}
BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms) BXDF_ENERGY_SUFFIX(ComputeClothEnergyTerms)(float Roughness, float NoV)
{
BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms) Out;
#if USE_ENERGY_CONSERVATION > 0
{
Out.W = 1.f;
Out.E = ClothEnergyLookup(Roughness, NoV).x;
}
#else
{
Out.W = 1.0f;
Out.E = 1.0f;
}
#endif
return Out;
}
BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms) BXDF_ENERGY_SUFFIX(ComputeDiffuseEnergyTerms)(float Roughness, float NoV)
{
BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms) Out;
#if USE_ENERGY_CONSERVATION > 0
{
Out.E = DiffuseEnergyLookup(Roughness, NoV);
}
#else
{
Out.E = 1.0f;
}
#endif
Out.W = 1.0f;
return Out;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Energy Preservation/Conservation
// Return the energy absorbed by upper layer (e.g., for the specular layer attenuation onto diffuse)
// Note: Use the directional albedo luminance to avoid color-shift due to metallic specular (for which the energy should be absorbed, not transmitted)
float ComputeEnergyPreservation(BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms) EnergyTerms)
{
#if USE_ENERGY_CONSERVATION > 0
#if USE_DEVELOPMENT_SHADERS
return View.bShadingEnergyPreservation ? (1 - GetEnergyLuminance(EnergyTerms.E)) : 1.0f;
#else
return 1 - GetEnergyLuminance(EnergyTerms.E);
#endif
#else
return 1.0f;
#endif
}
// Return the energy conservation weight factor for account energy loss in the BSDF model (i.e. due to micro-facet multiple scattering)
BXDF_ENERGY_TYPE ComputeEnergyConservation(BXDF_ENERGY_SUFFIX(FBxDFEnergyTerms) EnergyTerms)
{
return EnergyTerms.W;
}
#undef BXDF_ENERGY_SUFFIX