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

624 lines
24 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
//------------------------------------------------------- ENUM VALUES
#include "ScreenSpaceDenoise/SSDDefinitions.ush"
//------------------------------------------------------- CONFIGS
#define DIM_APPLY_DIFFUSE_INDIRECT_SSGI 1
#define DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER 2
#ifndef SSR_TILED_COMPOSITION
#define SSR_TILED_COMPOSITION 0
#endif
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SSGI
#define CONFIG_SIGNAL_PROCESSING SIGNAL_PROCESSING_SSGI
#define COMPILE_SIGNAL_COLOR 1
#define MAX_SIGNAL_BATCH_SIZE 1
#define SIGNAL_ARRAY_SIZE 1
#define CONFIG_SIGNAL_INPUT_LAYOUT SIGNAL_BUFFER_LAYOUT_SSGI_HISTORY_R11G11B10
#define CONFIG_INPUT_TEXTURE_COUNT 2
#elif DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
#define CONFIG_SIGNAL_PROCESSING SIGNAL_PROCESSING_SSGI
#define COMPILE_SIGNAL_COLOR_ARRAY 3
#define MAX_SIGNAL_BATCH_SIZE 1
#define SIGNAL_ARRAY_SIZE 1
#define CONFIG_SIGNAL_INPUT_LAYOUT SIGNAL_BUFFER_LAYOUT_SSGI_HISTORY_R11G11B10
#define CONFIG_INPUT_TEXTURE_COUNT 4
#else // !DIM_APPLY_DIFFUSE_INDIRECT
// NOP
#endif // !DIM_APPLY_DIFFUSE_INDIRECT
#define COMPILE_MOMENT1_ACCUMULATOR 1
#define COMPILE_BOX_KERNEL 1
#if SUBSTRATE_ENABLED && (DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SSGI || DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER)
#if SUBSTRATE_TILETYPE == 0
#define SUBSTRATE_FASTPATH 1
#elif SUBSTRATE_TILETYPE == 1
#define SUBSTRATE_SINGLEPATH 1
#elif SUBSTRATE_TILETYPE == 2
// COMPLEX PATH
#elif SUBSTRATE_TILETYPE == 3
// COMPLEX PATH
#define SUBSTRATE_COMPLEXSPECIALPATH 1
#else
#error Substrate tile type non-implemented
#endif
#endif
#if SUBTRATE_GBUFFER_FORMAT==1 && SUBSTRATE_OPAQUE_ROUGH_REFRACTION_ENABLED
RWTexture2D<float3> OutOpaqueRoughRefractionSceneColor;
RWTexture2D<float3> OutSubSurfaceSceneColor;
#endif
// Remove a branch of code that is unused where TransformSignal() should always early exit.
// SPIRV does not recognize that the code should be remove which adds an unnecessary parameter requirement.
#define FORCE_IDENTICAL_COLOR_SPACE 1
//------------------------------------------------------- INCLUDES
#include "Common.ush"
#include "SceneTextureParameters.ush"
#include "BRDF.ush"
#include "ShadingModels.ush"
#include "ClearCoatCommon.ush"
#include "FastMath.ush"
#include "Lumen/LumenMaterial.ush"
#include "Lumen/LumenReflectionsCombine.ush"
#include "Substrate/Substrate.ush"
#include "Substrate/SubstrateEvaluation.ush"
#include "HairStrands/HairStrandsEnvironmentLightingCommon.ush"
#if DIM_APPLY_DIFFUSE_INDIRECT
#include "ScreenSpaceDenoise/SSDSignalFramework.ush"
#include "ScreenSpaceDenoise/SSDSignalArray.ush"
#include "ScreenSpaceDenoise/SSDSpatialKernel.ush"
#include "Lumen/LumenScreenSpaceBentNormal.ush"
#endif
#if SSR_TILED_COMPOSITION
#include "ScreenSpaceReflectionTileCommons.ush"
#endif
//------------------------------------------------------- PARAMETERS
float AmbientOcclusionStaticFraction;
uint bVisualizeDiffuseIndirect;
Texture2D AmbientOcclusionTexture;
SamplerState AmbientOcclusionSampler;
// For Lumen we use explicit binding with texture arrays
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
Texture2DArray DiffuseIndirect_Lumen_0;
Texture2DArray DiffuseIndirect_Lumen_1;
Texture2DArray DiffuseIndirect_Lumen_2;
Texture2DArray DiffuseIndirect_Lumen_3;
#endif
Texture2D DiffuseIndirect_Textures_0;
#if CONFIG_INPUT_TEXTURE_COUNT > 1
Texture2D DiffuseIndirect_Textures_1;
#else
#define DiffuseIndirect_Textures_1 DiffuseIndirect_Textures_0
#endif
#if CONFIG_INPUT_TEXTURE_COUNT > 2
Texture2D DiffuseIndirect_Textures_2;
#else
#define DiffuseIndirect_Textures_2 DiffuseIndirect_Textures_0
#endif
#if CONFIG_INPUT_TEXTURE_COUNT > 3
Texture2D DiffuseIndirect_Textures_3;
#else
#define DiffuseIndirect_Textures_3 DiffuseIndirect_Textures_0
#endif
#if !ENABLE_DUAL_SRC_BLENDING
Texture2D SceneColorTexture;
SamplerState SceneColorSampler;
#endif
RWTexture2D<float4> PassDebugOutput;
uint bLumenSupportBackfaceDiffuse;
uint bLumenReflectionInputIsSSR;
float LumenFoliageOcclusionStrength;
float LumenMaxAOMultibounceAlbedo;
float LumenReflectionContrast;
float LumenReflectionSpecularScale;
#if SSR_TILED_COMPOSITION
StructuredBuffer<uint> SSRTileMaskBuffer;
uint2 SSRTiledViewRes;
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper functions
struct FShadingOcclusion
{
float SpecularOcclusion;
float3 DiffuseOcclusion;
};
#if DIM_SCREEN_BENT_NORMAL_MODE == 2
Texture2DArray<uint> ShortRangeAOTexture;
#else
Texture2DArray<UNORM float> ShortRangeAOTexture;
#endif
FShadingOcclusion GetShadingOcclusion(uint2 InPixelPos, uint InBSDFIndex, float3 V, float3 N, float Roughness, float3 DiffuseColor, float InAO)
{
FShadingOcclusion Out = (FShadingOcclusion)0;
Out.SpecularOcclusion = 1;
Out.DiffuseOcclusion = 1;
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
#if DIM_SCREEN_BENT_NORMAL_MODE == 2
const float3 BentNormal = UnpackScreenBentNormal(ShortRangeAOTexture[uint3(InPixelPos, InBSDFIndex)]);
const float AO = saturate(length(BentNormal));
Out.SpecularOcclusion = CalculateSpecularOcclusion(N, Roughness, AO, V, BentNormal);
Out.DiffuseOcclusion = DistantIlluminationRescale(min(DiffuseColor, LumenMaxAOMultibounceAlbedo), AO);
#elif DIM_SCREEN_BENT_NORMAL_MODE == 1
const float3 BentNormal = N;
const float AO = ShortRangeAOTexture[uint3(InPixelPos, InBSDFIndex)];
Out.SpecularOcclusion = CalculateSpecularOcclusion(N, Roughness, AO, V, BentNormal);
Out.DiffuseOcclusion = DistantIlluminationRescale(min(DiffuseColor, LumenMaxAOMultibounceAlbedo), AO);
#else
Out.SpecularOcclusion = InAO;
Out.DiffuseOcclusion = InAO;
#endif
#else
Out.DiffuseOcclusion = 1;
#endif
return Out;
}
float3 AddContrastAndSpecularScale(float3 In)
{
// Constrast adjustment with mid-grey as 'anchor' point so that overall exposure don't change
In = pow(In * (1.f / 0.18f), LumenReflectionContrast) * 0.18f;
return In * LumenReflectionSpecularScale;
}
float3 MixSpecularAndRoughReflections(float InRoughness, bool bHasBackfaceDiffuse, float4 SpecularReflections, float3 RoughReflections)
{
const float FadeAlpha = LumenCombineReflectionsAlpha(InRoughness, bHasBackfaceDiffuse);
float3 Lighting = bLumenReflectionInputIsSSR ? RoughReflections : RoughReflections * (1 - FadeAlpha);
if (bLumenReflectionInputIsSSR > 0)
{
// #lumen_todo: this incorrectly blends over ScreenProbeGather direct specular
Lighting = lerp(Lighting, SpecularReflections.rgb, SpecularReflections.a);
}
// Must branch as SpecularReflections can be uninitialized where not needed and contain NaN
else if (FadeAlpha > 0.0f)
{
Lighting += SpecularReflections.rgb * FadeAlpha;
}
return AddContrastAndSpecularScale(Lighting);
}
float3 CombineRoughSpecular(FGBufferData GBuffer, bool bHasBackfaceDiffuse, float NoV, float4 RayTracedReflections, float3 RoughReflections, float3 SpecularColor, float3 EnvBRDF)
{
float3 Lighting;
if (GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT)
{
Lighting = ClearCoatLayerCombine(GBuffer, NoV, AddContrastAndSpecularScale(RayTracedReflections.xyz), AddContrastAndSpecularScale(RoughReflections), SpecularColor);
}
else
{
Lighting = MixSpecularAndRoughReflections(GBuffer.Roughness, bHasBackfaceDiffuse, RayTracedReflections, RoughReflections) * EnvBRDF;
}
return Lighting;
}
float GetSSSCheckerboadSpecularScale(uint2 PixelPos, bool bNeedsSeparateLightAccumulation)
{
float SpecularScale = 1.0f;
if (bNeedsSeparateLightAccumulation &&
View.bSubsurfacePostprocessEnabled > 0 && View.bCheckerboardSubsurfaceProfileRendering > 0)
{
bool bChecker = CheckerFromPixelPos(PixelPos);
// Adjust for checkerboard. only apply non-diffuse lighting (including emissive)
// to the specular component, otherwise lighting is applied twice
SpecularScale = !bChecker;
}
return SpecularScale;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Lumen diffuse evaluation for Substrate material.
#if SUBTRATE_GBUFFER_FORMAT==1
FSubstrateDeferredLighting SubstrateIndirectLighting(
uint2 PixelPos,
FSubstrateMaterialContainer MaterialBuffer,
inout FSubstrateAddressing SubstrateAddressing,
FSubstratePixelHeader SubstratePixelHeader,
in float3 V,
in float InAO,
in float3 InputDiffuseLighting,
in float4 InputSpecularLighting)
{
FSubstrateDeferredLighting Out = GetInitialisedSubstrateDeferredLighting();
const FSubstrateIntegrationSettings Settings = InitSubstrateIntegrationSettings(false /*bForceFullyRough*/, Substrate.bRoughDiffuse, Substrate.PeelLayersAboveDepth, Substrate.bRoughnessTracking);
Substrate_for(uint ClosureIndex = 0, ClosureIndex < SubstratePixelHeader.ClosureCount, ++ClosureIndex)
{
const uint4 FetchCoord = uint4(PixelPos, ClosureIndex, 0);
// * Lumen integration output indirect lighting per BSDF
// * SSGI integration output a single indirect lighting value for all BSDF
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
const float3 InDiffuseLighting = DiffuseIndirect_Lumen_0.Load(FetchCoord).rgb;
const float3 InRoughSpecularLighting = DiffuseIndirect_Lumen_2.Load(FetchCoord).rgb;
const float4 InSpecularLighting = bLumenReflectionInputIsSSR ? InputSpecularLighting : DiffuseIndirect_Lumen_3.Load(FetchCoord).rgba;
#else
const float3 InDiffuseLighting = InputDiffuseLighting;
const float3 InRoughSpecularLighting = InputSpecularLighting.xyz;
const float4 InSpecularLighting = InputSpecularLighting;
#endif
FSubstrateBSDF BSDF = UnpackSubstrateBSDF(MaterialBuffer, SubstrateAddressing, SubstratePixelHeader);
if (bVisualizeDiffuseIndirect)
{
SubstrateSetBSDFDiffuseColor(BSDF, float3(0.18f, 0.18f, 0.18f));
}
// Create the BSDF context
FSubstrateBSDFContext SubstrateBSDFContext = SubstrateCreateBSDFContext(SubstratePixelHeader, BSDF, SubstrateAddressing, V);
const float3 BSDFThroughput = LuminanceWeight(SubstrateBSDFContext, BSDF);
// Evaluate environment lighting
FSubstrateEnvLightResult SubstrateEnvLight = SubstrateEvaluateForEnvLight(SubstrateBSDFContext, true /*bEnableSpecular*/, Settings);
const float3 DiffuseColor = SubstrateGetBSDFDiffuseColor(BSDF);
const float Roughness = SubstrateGetBSDFRoughness(BSDF);
const bool bHasBackfaceDiffuse = SubstrateGetBSDFType(BSDF) == SUBSTRATE_BSDF_TYPE_SLAB && BSDF.HasBackScattering();
const FShadingOcclusion Occlusion = GetShadingOcclusion(PixelPos, ClosureIndex, V, SubstrateBSDFContext.N, Roughness, DiffuseColor, InAO);
const float3 InGlossyLighting = MixSpecularAndRoughReflections(Roughness, bHasBackfaceDiffuse, InSpecularLighting, InRoughSpecularLighting * Occlusion.SpecularOcclusion);
FDirectLighting OutBSDF = (FDirectLighting)0;
OutBSDF.Diffuse += BSDFThroughput * InDiffuseLighting * SubstrateEnvLight.DiffuseWeight * Occlusion.DiffuseOcclusion;
OutBSDF.Specular += BSDFThroughput * InGlossyLighting * SubstrateEnvLight.SpecularWeight; // Note: Occlusion.SpecularOcclusion is only applied on the rough specular part, as in legacy path.
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
if (bHasBackfaceDiffuse && bLumenSupportBackfaceDiffuse > 0)
{
const float3 InBackfaceDiffuseLighting = DiffuseIndirect_Lumen_1.Load(FetchCoord).rgb;
OutBSDF.Diffuse += BSDFThroughput * InBackfaceDiffuseLighting * SubstrateEnvLight.DiffuseBackFaceWeight * PI * Occlusion.DiffuseOcclusion; // Multiplied by PI as is SubstrateEnvLight.DiffuseBackFaceWeight is divided PI
}
#endif
#if SUBSTRATE_FASTPATH==0
if (any(SubstrateEnvLight.SpecularHazeWeight > 0.0f))
{
const float3 InHazeGlossyLighting = MixSpecularAndRoughReflections(SubstrateEnvLight.SpecularHazeSafeRoughness, bHasBackfaceDiffuse, InSpecularLighting, InRoughSpecularLighting * Occlusion.SpecularOcclusion);
OutBSDF.Specular += BSDFThroughput * SubstrateEnvLight.SpecularHazeWeight * InHazeGlossyLighting; // Note: Occlusion.SpecularOcclusion is only applied on the rough specular part, as in legacy path.
}
#endif
// SSS Checkerboard
OutBSDF.Specular *= GetSSSCheckerboadSpecularScale(PixelPos, SubstrateEnvLight.bPostProcessSubsurface);
FLightAccumulator Accumulator = (FLightAccumulator)0;
LightAccumulator_AddSplit(Accumulator, OutBSDF.Diffuse, OutBSDF.Specular, OutBSDF.Diffuse, 1.f /*CommonMultiplier*/, SubstrateEnvLight.bPostProcessSubsurface);
AccumulateSubstrateDeferredLighting(Out, Accumulator, SubstrateEnvLight.bPostProcessSubsurface, BSDF_GETISTOPLAYER(BSDF));
}
return Out;
}
#endif // SUBTRATE_GBUFFER_FORMAT==1
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void MainPS(
float4 SvPosition : SV_POSITION
#if DIM_APPLY_DIFFUSE_INDIRECT
#if ENABLE_DUAL_SRC_BLENDING
, out float4 OutAddColor DUAL_SOURCE_BLENDING_SLOT(0) : SV_Target0
, out float4 OutMultiplyColor DUAL_SOURCE_BLENDING_SLOT(1) : SV_Target1
#else
, out float4 OutColor : SV_Target0
#endif
#else
, out float4 OutMultiplyColor : SV_Target0
#endif
)
{
const uint2 PixelPos = uint2(SvPosition.xy);
const float2 SceneBufferUV = SvPositionToBufferUV(SvPosition);
const float2 ScreenPosition = SvPositionToScreenPosition(SvPosition).xy;
#if !ENABLE_DUAL_SRC_BLENDING && DIM_APPLY_DIFFUSE_INDIRECT
float4 OutAddColor = float4(0.0, 0.0, 0.0, 0.0);
float4 OutMultiplyColor;
const float4 SceneColor = SceneColorTexture.SampleLevel(SceneColorSampler, SceneBufferUV, 0);
#endif
// Sample scene textures.
const FLumenMaterialData Material = ReadMaterialData(PixelPos, SceneBufferUV);
// Sample the ambient occlusion that is dynamically generated every frame.
const float DynamicAmbientOcclusion = AmbientOcclusionTexture.SampleLevel(AmbientOcclusionSampler, SceneBufferUV, 0).r;
// Compute the final ambient occlusion to be applied. Lumen handles material AO internally.
float FinalAmbientOcclusion = 1.0f;
#if DIM_APPLY_DIFFUSE_INDIRECT != DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
{
float AOMask = IsValid(Material);
FinalAmbientOcclusion = lerp(1.0f, Material.MaterialAO * DynamicAmbientOcclusion, AOMask * AmbientOcclusionStaticFraction);
}
#endif
const float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, Material.SceneDepth), Material.SceneDepth, 1), View.ScreenToTranslatedWorld).xyz;
const float3 N = Material.WorldNormal;
const float3 V = -GetCameraVectorFromTranslatedWorldPosition(TranslatedWorldPosition);
const float NoV = saturate(dot(N, V));
// Apply diffuse indirect.
#if DIM_APPLY_DIFFUSE_INDIRECT
OutAddColor = 0;
{
FDirectLighting IndirectLighting = (FDirectLighting)0;
if (IsValid(Material))
{
float3 DiffuseIndirectLighting = 0;
float3 RoughSpecularIndirectLighting = 0;
float4 SpecularIndirectLighting = 0;
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
DiffuseIndirectLighting = DiffuseIndirect_Lumen_0.SampleLevel(GlobalPointClampedSampler, float3(SceneBufferUV, 0), 0).rgb;
RoughSpecularIndirectLighting = DiffuseIndirect_Lumen_2.SampleLevel(GlobalPointClampedSampler, float3(SceneBufferUV, 0), 0).rgb;
if (bLumenReflectionInputIsSSR)
{
#if SSR_TILED_COMPOSITION
if (IsSSRTileUsed(PixelPos, SSRTiledViewRes, SSRTileMaskBuffer))
#endif
{
SpecularIndirectLighting = DiffuseIndirect_Textures_3.SampleLevel(GlobalPointClampedSampler, SceneBufferUV, 0).rgba;
}
}
else
{
SpecularIndirectLighting = DiffuseIndirect_Lumen_3.SampleLevel(GlobalPointClampedSampler, float3(SceneBufferUV, 0), 0).rgba;
}
#else
{
// Sample the output of the denoiser.
FSSDKernelConfig KernelConfig = CreateKernelConfig();
#if DEBUG_OUTPUT
{
KernelConfig.DebugPixelPosition = uint2(SvPosition.xy);
KernelConfig.DebugEventCounter = 0;
}
#endif
// Compile time.
KernelConfig.bSampleKernelCenter = true;
KernelConfig.BufferLayout = CONFIG_SIGNAL_INPUT_LAYOUT;
KernelConfig.bUnroll = true;
#if DIM_UPSCALE_DIFFUSE_INDIRECT
{
KernelConfig.SampleSet = SAMPLE_SET_2X2_BILINEAR;
KernelConfig.BilateralDistanceComputation = SIGNAL_WORLD_FREQUENCY_REF_METADATA_ONLY;
KernelConfig.WorldBluringDistanceMultiplier = 16.0;
KernelConfig.BilateralSettings[0] = BILATERAL_POSITION_BASED(3);
// SGPRs
KernelConfig.BufferSizeAndInvSize = View.BufferSizeAndInvSize * float4(0.5, 0.5, 2.0, 2.0);
KernelConfig.BufferBilinearUVMinMax = View.BufferBilinearUVMinMax;
}
#else
{
KernelConfig.SampleSet = SAMPLE_SET_1X1;
KernelConfig.bNormalizeSample = true;
// SGPRs
KernelConfig.BufferSizeAndInvSize = View.BufferSizeAndInvSize;
KernelConfig.BufferBilinearUVMinMax = View.BufferBilinearUVMinMax;
}
#endif
// VGPRs
KernelConfig.BufferUV = SceneBufferUV;
{
// SUBSTRATE_TODO: We use the top layer data, but we should resolve lighting for each BSDFs.
KernelConfig.CompressedRefSceneMetadata = MaterialToCompressedSceneMetadata(Material.SceneDepth, Material.WorldNormal, Material.Roughness, Material.ShadingID);
KernelConfig.RefBufferUV = SceneBufferUV;
KernelConfig.RefSceneMetadataLayout = METADATA_BUFFER_LAYOUT_DISABLED;
}
KernelConfig.HammersleySeed = Rand3DPCG16(int3(SvPosition.xy, View.StateFrameIndexMod8)).xy;
FSSDSignalAccumulatorArray UncompressedAccumulators = CreateSignalAccumulatorArray();
FSSDCompressedSignalAccumulatorArray CompressedAccumulators = CompressAccumulatorArray(
UncompressedAccumulators, CONFIG_ACCUMULATOR_VGPR_COMPRESSION);
AccumulateKernel(
KernelConfig,
DiffuseIndirect_Textures_0,
DiffuseIndirect_Textures_1,
DiffuseIndirect_Textures_2,
DiffuseIndirect_Textures_3,
/* inout */ UncompressedAccumulators,
/* inout */ CompressedAccumulators);
//PassDebugOutput[uint2(SvPosition.xy)] = float4(UncompressedAccumulators.Array[0].Moment1.SampleCount, 0, 0, 0);
FSSDSignalSample Sample;
#if DIM_UPSCALE_DIFFUSE_INDIRECT
Sample = NormalizeToOneSample(UncompressedAccumulators.Array[0].Moment1);
#else
Sample = UncompressedAccumulators.Array[0].Moment1;
#endif
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SSGI
{
DiffuseIndirectLighting = Sample.SceneColor.rgb;
}
#else
#error Unimplemented
#endif
}
#endif
#if SUBTRATE_GBUFFER_FORMAT==1
{
FSubstrateAddressing SubstrateAddressing = GetSubstratePixelDataByteOffset(PixelPos, uint2(View.BufferSizeAndInvSize.xy), Substrate.MaxBytesPerPixel);
FSubstratePixelHeader SubstratePixelHeader = UnpackSubstrateHeaderIn(Substrate.MaterialTextureArray, SubstrateAddressing, Substrate.TopLayerTexture);
if (SubstratePixelHeader.GetMaterialMode() > HEADER_MATERIALMODE_NONE)
{
const FSubstrateDeferredLighting IndirectLighting_Substrate = SubstrateIndirectLighting(
PixelPos,
Substrate.MaterialTextureArray,
SubstrateAddressing,
SubstratePixelHeader,
V,
DynamicAmbientOcclusion,
DiffuseIndirectLighting,
SpecularIndirectLighting);
OutAddColor = IndirectLighting_Substrate.SceneColor;
#if SUBSTRATE_OPAQUE_ROUGH_REFRACTION_ENABLED
const uint2 OutCoord = PixelPos;
OutOpaqueRoughRefractionSceneColor[OutCoord] += IndirectLighting_Substrate.OpaqueRoughRefractionSceneColor;
OutSubSurfaceSceneColor[OutCoord] += IndirectLighting_Substrate.SubSurfaceSceneColor;
#endif
}
}
#else // SUBTRATE_GBUFFER_FORMAT==1
{
FGBufferData GBuffer = Material.GBufferData;
float3 DiffuseColor = bVisualizeDiffuseIndirect ? float3(.18f, .18f, .18f) : GBuffer.DiffuseColor;
float3 SpecularColor = GBuffer.SpecularColor;
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
RemapClearCoatDiffuseAndSpecularColor(GBuffer, NoV, DiffuseColor, SpecularColor);
#endif
FShadingOcclusion Occlusion = GetShadingOcclusion(PixelPos, 0 /*BSDFIndex*/, V, N, GBuffer.Roughness, GBuffer.BaseColor, DynamicAmbientOcclusion);
if (GBuffer.ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE || GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE)
{
Occlusion.DiffuseOcclusion = lerp(1, Occlusion.DiffuseOcclusion, LumenFoliageOcclusionStrength);
}
if (GBuffer.ShadingModelID == SHADINGMODELID_HAIR)
{
float3 L = 0;
const float3 IndirectDiffuseColor = EvaluateEnvHair(GBuffer, V, N, L /*out*/);
IndirectLighting.Diffuse = DiffuseIndirectLighting * Occlusion.DiffuseOcclusion * IndirectDiffuseColor;
IndirectLighting.Specular = 0;
}
else
{
float3 BackfaceDiffuseIndirectLighting = 0;
if (GBuffer.ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE)
{
float3 SubsurfaceColor = ExtractSubsurfaceColor(GBuffer);
if (bLumenSupportBackfaceDiffuse > 0)
{
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
BackfaceDiffuseIndirectLighting += SubsurfaceColor * DiffuseIndirect_Lumen_1.SampleLevel(GlobalPointClampedSampler, float3(SceneBufferUV, 0), 0).rgb;
#else
BackfaceDiffuseIndirectLighting += SubsurfaceColor * DiffuseIndirect_Textures_1.SampleLevel(GlobalPointClampedSampler, SceneBufferUV, 0).rgb;
#endif
}
else
{
// Adding Subsurface energy to the diffuse lobe is a poor approximation when DiffuseColor is small and SubsurfaceColor is large
// Reduce the error by attenuating SubsurfaceColor, even though diffuse already has the 1/PI for Lambert.
const float PreintegratedTwoSidedBxDF = 1.0f / PI;
DiffuseColor += SubsurfaceColor * PreintegratedTwoSidedBxDF;
}
}
if (GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE || GBuffer.ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN)
{
float3 SubsurfaceColor = ExtractSubsurfaceColor(GBuffer);
// Add subsurface energy to diffuse
DiffuseColor += SubsurfaceColor;
}
if (GBuffer.ShadingModelID == SHADINGMODELID_CLOTH)
{
float3 ClothFuzz = ExtractSubsurfaceColor(GBuffer);
DiffuseColor += ClothFuzz * GBuffer.CustomData.a;
}
#if USE_ENERGY_CONSERVATION
FBxDFEnergyTerms SpecularEnergyTerms = ComputeGGXSpecEnergyTerms(GBuffer.Roughness, NoV, SpecularColor);
float3 EnvBRDFValue = SpecularEnergyTerms.E; // EnvBRDF accounting for multiple scattering when enabled
float EnergyPreservationFactor = ComputeEnergyPreservation(SpecularEnergyTerms);
#else
float3 EnvBRDFValue = EnvBRDF(SpecularColor, GBuffer.Roughness, NoV);
float EnergyPreservationFactor = 1.0;
#endif
IndirectLighting.Diffuse = (DiffuseIndirectLighting * DiffuseColor + BackfaceDiffuseIndirectLighting) * Occlusion.DiffuseOcclusion * EnergyPreservationFactor;
IndirectLighting.Transmission = 0;
#if DIM_APPLY_DIFFUSE_INDIRECT == DIM_APPLY_DIFFUSE_INDIRECT_SCREEN_PROBE_GATHER
RoughSpecularIndirectLighting *= Occlusion.SpecularOcclusion;
IndirectLighting.Specular = CombineRoughSpecular(GBuffer, HasBackfaceDiffuse(Material), NoV, SpecularIndirectLighting, RoughSpecularIndirectLighting, SpecularColor, EnvBRDFValue);
#else
IndirectLighting.Specular = AddContrastAndSpecularScale(SpecularIndirectLighting.xyz) * EnvBRDFValue;
#endif
}
}
#endif // SUBTRATE_GBUFFER_FORMAT==1
}
// Accumulate lighting into the final buffers
#if SUBTRATE_GBUFFER_FORMAT==0
IndirectLighting.Specular *= GetSSSCheckerboadSpecularScale(PixelPos, Material.bNeedsSeparateLightAccumulation);
FLightAccumulator LightAccumulator = (FLightAccumulator)0;
LightAccumulator_Add(
LightAccumulator,
IndirectLighting.Diffuse + IndirectLighting.Specular,
IndirectLighting.Diffuse,
1.0f,
Material.bNeedsSeparateLightAccumulation);
OutAddColor = LightAccumulator_GetResult(LightAccumulator);
#endif // SUBTRATE_GBUFFER_FORMAT==0
}
#endif
OutMultiplyColor = FinalAmbientOcclusion;
#if !ENABLE_DUAL_SRC_BLENDING && DIM_APPLY_DIFFUSE_INDIRECT
OutColor = SceneColor * OutMultiplyColor + OutAddColor;
#endif
}