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

226 lines
8.4 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LightAccumulator.usf: FLightAccumulator "class" and it's methods, useful for screen space subsurface scattering
=============================================================================*/
#pragma once
#include "Common.ush"
// set by c++, not set for LPV
// 0 / 1
#ifndef VISUALIZE_LIGHT_CULLING
#define VISUALIZE_LIGHT_CULLING 0
#endif
// for ScreenSpaceSubsurfaceScattering
// 0 : fastest (speculars leak in SSSSS). Forces checkerboard rendering for subsurface profile
// 1 : luminance specular - works well (needs 64bit SceneColor, stores luminance of specular in alpha, can be optimized further in LightAccumulator_Add() )
// NOTE: Dependent on View.bCheckerboardSubsurfaceProfileRendering - if this is off, we fall back to mode 0
// 2 : colored specular works well (best, requires another RT for light accumulation)
#define SUBSURFACE_CHANNEL_MODE 1
struct FLightAccumulator
{
float3 TotalLight;
float TotalLightLuminance;
// only actually used SUBSURFACE_CHANNEL_MODE == 1
// assumed to be compiled out otherwise (not compiled out with #if so we can use if() instead of #if for better readability and compiler error checking)
// input for ScreenSpaceSubsurfaceScattering
float ScatterableLightLuma;
// only actually used SUBSURFACE_CHANNEL_MODE == 2
// assumed to be compiled out otherwise (not compiled out with #if so we can use if() instead of #if for better readability and compiler error checking)
// input for ScreenSpaceSubsurfaceScattering
float3 ScatterableLight;
// only used for development (not compiled out with #if so we can use if() instead of #if for better readability and compiler error checking)
// assumed to be compiled out otherwise
float EstimatedCost;
// only used for alpha, which needs to keep specular and alpha separate since specular needs to multiply by 1/opacity to compensate for alpha blending
// assumed to be compiled out otherwise
float3 TotalLightDiffuse;
float3 TotalLightSpecular;
};
struct FDeferredLightingSplit
{
float4 DiffuseLighting;
float4 SpecularLighting;
float LightingLuminance;
};
// accumulate light, can be called multiple times
void LightAccumulator_AddSplit(inout FLightAccumulator In, float3 DiffuseTotalLight, float3 SpecularTotalLight, float3 ScatterableLight, float3 CommonMultiplier, const bool bNeedsSeparateSubsurfaceLightAccumulation)
{
// 3 mad
In.TotalLight += (DiffuseTotalLight + SpecularTotalLight) * CommonMultiplier;
In.TotalLightLuminance += Luminance((DiffuseTotalLight + SpecularTotalLight) * CommonMultiplier);
// This should ideally be evaluated statically outside of this function to avoid the branch
if (bNeedsSeparateSubsurfaceLightAccumulation)
{
if (SUBSURFACE_CHANNEL_MODE == 1)
{
if (View.bCheckerboardSubsurfaceProfileRendering == 0)
{
In.ScatterableLightLuma += Luminance(ScatterableLight * CommonMultiplier);
}
}
else if (SUBSURFACE_CHANNEL_MODE == 2)
{
// 3 mad
In.ScatterableLight += ScatterableLight * CommonMultiplier;
}
}
In.TotalLightDiffuse += DiffuseTotalLight * CommonMultiplier;
In.TotalLightSpecular += SpecularTotalLight * CommonMultiplier;
}
void LightAccumulator_Add(inout FLightAccumulator In, float3 TotalLight, float3 ScatterableLight, float3 CommonMultiplier, const bool bNeedsSeparateSubsurfaceLightAccumulation)
{
LightAccumulator_AddSplit(In, TotalLight, 0.0f, ScatterableLight, CommonMultiplier, bNeedsSeparateSubsurfaceLightAccumulation);
}
float4 ConvertEstimatedCostToColor(float EstimatedCost)
{
return 0.1f * float4(1.0f, 0.25f, 0.075f, 0) * EstimatedCost;
}
//
// compute final value to store in the MRT0
// @retrun RGB:SceneColor Specular and Diffuse, A:Non Specular SceneColor Luminance
float4 LightAccumulator_GetResult(FLightAccumulator In)
{
float4 Ret;
if (VISUALIZE_LIGHT_CULLING == 1)
{
// a soft gradient from dark red to bright white, can be changed to be different
Ret = ConvertEstimatedCostToColor(In.EstimatedCost);
}
else
{
Ret = float4(In.TotalLight, 0);
if (SUBSURFACE_CHANNEL_MODE == 1 )
{
// bSubsurfacePostprocessEnabled bCheckerboardSubsurfaceProfileRendering OutputLuma
// 0 0 0
// 0 1 0
// 1 0 1
// 1 1 0
// The alpha channel will not be used in the post process subsurface pass if View.bSubsurfacePostprocessEnabled
// is not enabled. Add this gate (View.bSubsurfacePostprocessEnabled) so that when subsurface is disabled, scene
// captures will not pick up ScatterableLightLuma to pollute the alpha channel.
if (View.bCheckerboardSubsurfaceProfileRendering == 0 && View.bSubsurfacePostprocessEnabled)
{
// RGB accumulated RGB HDR color, A: specular luminance for screenspace subsurface scattering
Ret.a = In.ScatterableLightLuma;
}
}
else if (SUBSURFACE_CHANNEL_MODE == 2)
{
// RGB accumulated RGB HDR color, A: view independent (diffuse) luminance for screenspace subsurface scattering
// 3 add, 1 mul, 2 mad, can be optimized to use 2 less temporary during accumulation and remove the 3 add
Ret.a = Luminance(In.ScatterableLight);
// todo, need second MRT for SUBSURFACE_CHANNEL_MODE==2
}
}
return Ret;
}
FDeferredLightingSplit LightAccumulator_GetResultSplit(FLightAccumulator In)
{
float4 RetDiffuse;
float4 RetSpecular;
if (VISUALIZE_LIGHT_CULLING == 1)
{
// a soft gradient from dark red to bright white, can be changed to be different
RetDiffuse = ConvertEstimatedCostToColor(In.EstimatedCost);
RetSpecular = RetDiffuse;
}
else
{
RetDiffuse = float4(In.TotalLightDiffuse, 0);
RetSpecular = float4(In.TotalLightSpecular, 0);
if (SUBSURFACE_CHANNEL_MODE == 1 )
{
if (View.bCheckerboardSubsurfaceProfileRendering == 0 && View.bSubsurfacePostprocessEnabled)
{
// RGB accumulated RGB HDR color, A: specular luminance for screenspace subsurface scattering
RetDiffuse.a = In.ScatterableLightLuma;
}
}
else if (SUBSURFACE_CHANNEL_MODE == 2)
{
// RGB accumulated RGB HDR color, A: view independent (diffuse) luminance for screenspace subsurface scattering
// 3 add, 1 mul, 2 mad, can be optimized to use 2 less temporary during accumulation and remove the 3 add
RetDiffuse.a = Luminance(In.ScatterableLight);
// todo, need second MRT for SUBSURFACE_CHANNEL_MODE==2
}
}
FDeferredLightingSplit Ret;
Ret.DiffuseLighting = RetDiffuse;
Ret.SpecularLighting = RetSpecular;
Ret.LightingLuminance = In.TotalLightLuminance;
return Ret;
}
struct FSubstrateDeferredLighting
{
float4 SceneColor; // Untouched scene luminance, alpha channel may contain scatterable light luminance.
#if SUBSTRATE_OPAQUE_ROUGH_REFRACTION_ENABLED
float3 OpaqueRoughRefractionSceneColor; // Luminance that is going to be blurier by the top layer roughness
float3 SubSurfaceSceneColor; // Scene color that should go through the sub surface post process. There is not alpha: not needed because it is separated from specular already.
#endif
// only used for development (not compiled out with #if so we can use if() instead of #if for better readability and compiler error checking)
// assumed to be compiled out otherwise
float EstimatedCost;
float3 TotalDiffuseLighting;
float3 TotalSpecularLighting;
};
FSubstrateDeferredLighting GetInitialisedSubstrateDeferredLighting()
{
FSubstrateDeferredLighting Result = (FSubstrateDeferredLighting)0;
return Result;
}
void AccumulateSubstrateDeferredLighting(inout FSubstrateDeferredLighting SubstrateLighting, FLightAccumulator In, bool bDiffuseIsSubsurface, bool bIsToplayer)
{
FDeferredLightingSplit DiffSpec = LightAccumulator_GetResultSplit(In);
#if SUBSTRATE_OPAQUE_ROUGH_REFRACTION_ENABLED
if (bIsToplayer)
{
SubstrateLighting.SceneColor += DiffSpec.SpecularLighting + (bDiffuseIsSubsurface ? 0 : DiffSpec.DiffuseLighting);
SubstrateLighting.OpaqueRoughRefractionSceneColor += 0.0;
SubstrateLighting.SubSurfaceSceneColor +=(bDiffuseIsSubsurface ? DiffSpec.DiffuseLighting : 0).rgb;
}
else // !bIsToplayer
{
SubstrateLighting.SceneColor += 0.0;
SubstrateLighting.OpaqueRoughRefractionSceneColor +=(DiffSpec.SpecularLighting +(bDiffuseIsSubsurface ? 0 : DiffSpec.DiffuseLighting)).rgb;
SubstrateLighting.SubSurfaceSceneColor +=(bDiffuseIsSubsurface ? DiffSpec.DiffuseLighting : 0).rgb;
}
#else
SubstrateLighting.SceneColor += DiffSpec.DiffuseLighting + DiffSpec.SpecularLighting;
#endif
SubstrateLighting.EstimatedCost += In.EstimatedCost;
}