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

430 lines
17 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#define MOBILE_DEFERRED_LIGHTING 1
#if SUBSTRATE_ENABLED
// This is needed when lights are rendered deferred on mobile with Substrate because we use the legacy GBuffer representation and code process.
#define ALLOW_LOCAL_LIGHT_DISTANCE_ATTENUATION 1
#endif
#include "Common.ush"
#include "SHCommon.ush"
// Reroute MobileSceneTextures uniform buffer references to the base pass uniform buffer
#define MobileSceneTextures MobileBasePass.SceneTextures
#define ForwardLightStruct MobileBasePass.Forward
#define PlanarReflectionStruct MobileBasePass.PlanarReflection
#define ReflectionStruct MobileBasePass.ReflectionsParameters
#define PreIntegratedGF ReflectionStruct.PreIntegratedGF
#define PreIntegratedGFSampler ReflectionStruct.PreIntegratedGFSampler
#if MATERIAL_SHADER
#include "/Engine/Generated/Material.ush"
#endif
#include "MobileLightingCommon.ush"
#if MATERIAL_SHADER
#include "LightFunctionCommon.ush"
#endif
#include "LightDataUniforms.ush"
#include "LightShaderParameters.ush"
#ifndef USE_HAIR_COMPLEX_TRANSMITTANCE
#define USE_HAIR_COMPLEX_TRANSMITTANCE 0
#endif
#include "HairStrands/HairStrandsEnvironmentLightingCommon.ush"
#if APPLY_SKY_SHADOWING
#include "SkyLightingDiffuseShared.ush"
#include "DistanceFieldAOShared.ush"
#endif
float4x4 TranslatedWorldToLight;
float2 LightFunctionParameters2;
half ComputeLightFunctionMultiplier(float3 TranslatedWorldPosition)
{
#if USE_LIGHT_FUNCTION
float4 LightVector = mul(float4(TranslatedWorldPosition, 1.0), TranslatedWorldToLight);
LightVector.xyz /= LightVector.w;
half3 LightFunction = GetLightFunctionColor(LightVector.xyz, TranslatedWorldPosition);
half GreyScale = dot(LightFunction, .3333f);
// Calculate radial view distance for stable fading
float ViewDistance = length(PrimaryView.TranslatedWorldCameraOrigin - TranslatedWorldPosition);
half DistanceFadeAlpha = saturate((LightFunctionParameters2.x - ViewDistance) / (LightFunctionParameters2.x * .2f));
// Fade to disabled based on LightFunctionFadeDistance
GreyScale = lerp(LightFunctionParameters2.y, GreyScale, DistanceFadeAlpha);
// Fade to disabled based on ShadowFadeFraction
GreyScale = lerp(LightFunctionParameters2.y, GreyScale, LightFunctionParameters.y);
return GreyScale;
#else
return 1.0;
#endif
}
half3 SkyLightDiffuseMobile(FGBufferData GBuffer, half3 DiffuseColor, half3 V, inout half IndirectIrradiance)
{
half3 Lighting = 0;
half3 SkyDiffuseLookUpNormal = GBuffer.WorldNormal;
if (GBuffer.ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE)
{
half3 SubsurfaceLookup = GetSkySHDiffuseSimple(-GBuffer.WorldNormal) * View.SkyLightColor.rgb;
half3 SubsurfaceColor = ExtractSubsurfaceColor(GBuffer);
Lighting += SubsurfaceLookup * SubsurfaceColor;
}
if (GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE || GBuffer.ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN)
{
half3 SubsurfaceColor = ExtractSubsurfaceColor(GBuffer);
// Add subsurface energy to diffuse
DiffuseColor += SubsurfaceColor;
}
if (GBuffer.ShadingModelID == SHADINGMODELID_HAIR)
{
const half3 N = GBuffer.WorldNormal;
DiffuseColor = EvaluateEnvHair(GBuffer, V, N, SkyDiffuseLookUpNormal);
}
if (GBuffer.ShadingModelID == SHADINGMODELID_CLOTH)
{
half3 ClothFuzz = ExtractSubsurfaceColor(GBuffer);
DiffuseColor += ClothFuzz * GBuffer.CustomData.a;
}
// Compute the preconvolved incoming lighting with the bent normal direction
half3 DiffuseLookup = GetSkySHDiffuseSimple(SkyDiffuseLookUpNormal) * View.SkyLightColor.rgb;
// And accumulate the lighting
Lighting += DiffuseLookup * DiffuseColor * GBuffer.GBufferAO;
#if ALLOW_STATIC_LIGHTING
IndirectIrradiance += Luminance(DiffuseLookup);
#endif
return Lighting;
}
void ReflectionEnvironmentSkyLighting(
FGBufferData GBuffer,
half4 SvPosition,
half3 CameraVector,
float3 TranslatedWorldPosition,
half3 ReflectionVector,
uint GridIndex,
half GatheredAmbientOcclusion,
inout FLightAccumulator DirectLighting)
{
half IndirectIrradiance = GBuffer.IndirectIrradiance;
#if ENABLE_SKY_LIGHT
half3 N = GBuffer.WorldNormal;
half3 V = -CameraVector;
half NoV = saturate(abs(dot(N, V)) + 1e-5);
#if APPLY_SKY_SHADOWING
half3 BentNormal = GBuffer.WorldNormal;
const float2 BentNormalUV = (SvPosition.xy - View.ViewRectMin.xy) * View.BufferSizeAndInvSize.zw;
BentNormal = UpsampleDFAO(BentNormalUV, GBuffer.Depth, GBuffer.WorldNormal);
#endif
half3 DiffuseColor = GBuffer.DiffuseColor;
half3 SpecularColor = GBuffer.SpecularColor;
RemapClearCoatDiffuseAndSpecularColor(GBuffer, NoV, DiffuseColor, SpecularColor);
#if APPLY_SKY_SHADOWING
//const float3 DiffuseColor = GBuffer.BaseColor; // not used
const float2 BufferUV = SvPosition * View.BufferSizeAndInvSize.zw; // not used
float2 ScreenPosition = SvPositionToScreenPosition(SvPosition).xy;
// Sky lighting (already pre-exposed)
half3 SkyLighting = SkyLightDiffuse(GBuffer, GatheredAmbientOcclusion, BufferUV, ScreenPosition, BentNormal, DiffuseColor);
#else
half3 SkyLighting = SkyLightDiffuseMobile(GBuffer, DiffuseColor, V, IndirectIrradiance);
#endif
const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID);
LightAccumulator_Add(DirectLighting, SkyLighting, SkyLighting, 1.0f, bNeedsSeparateSubsurfaceLightAccumulation);
#endif // ENABLE_SKY_LIGHT
// IBL
AccumulateReflection(GBuffer
, SvPosition
, CameraVector
, TranslatedWorldPosition
, ReflectionVector
, IndirectIrradiance
, GridIndex
, DirectLighting);
}
void MobileDirectionalLightPS(
noperspective float4 UVAndScreenPos : TEXCOORD0,
#if INSTANCED_STEREO && MOBILE_MULTI_VIEW
in nointerpolation uint EyeIndex : VIEW_ID,
#elif MOBILE_MULTI_VIEW
in nointerpolation uint ViewId : SV_ViewID,
#endif
float4 SvPosition : SV_POSITION,
#if USE_GLES_FBF_DEFERRED
out HALF4_TYPE OutProxyAdditive : SV_Target0,
out HALF4_TYPE OutGBufferA : SV_Target1,
out HALF4_TYPE OutGBufferB : SV_Target2,
out HALF4_TYPE OutGBufferC : SV_Target3
#else
out HALF4_TYPE OutColor : SV_Target0
#endif
)
{
#if INSTANCED_STEREO && MOBILE_MULTI_VIEW
ResolvedView = ResolveView(EyeIndex);
#elif MOBILE_MULTI_VIEW
ResolvedView = ResolveView(ViewId);
const uint EyeIndex = ViewId;
#else
ResolvedView = ResolveView();
const uint EyeIndex = 0;
#endif
FGBufferData GBuffer = MobileFetchAndDecodeGBuffer(UVAndScreenPos.xy, UVAndScreenPos.zw);
float2 ScreenPos = UVAndScreenPos.zw;
float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPos, GBuffer.Depth), GBuffer.Depth, 1), PrimaryView.ScreenToTranslatedWorld).xyz;
half3 CameraVector = normalize(TranslatedWorldPosition);
half3 V = -CameraVector;
half NoV = dot(GBuffer.WorldNormal, V);
half3 ReflectionVector = GBuffer.WorldNormal * (NoV * 2.0) - V;
FLightAccumulator DirectLighting = (FLightAccumulator)0;
float4 ScreenPosition = SvPositionToScreenPosition(float4(SvPosition.xyz, GBuffer.Depth));
half GatheredAmbientOcclusion = 1;
#if ENABLE_AMBIENT_OCCLUSION
GatheredAmbientOcclusion = Texture2DSample(MobileBasePass.AmbientOcclusionTexture, MobileBasePass.AmbientOcclusionSampler, UVAndScreenPos.xy).r;
GatheredAmbientOcclusion = lerp(1.0, GatheredAmbientOcclusion, MobileBasePass.AmbientOcclusionStaticFraction);
GBuffer.GBufferAO *= GatheredAmbientOcclusion;
#endif
// Directional light
half4 DynamicShadowFactors = 1.0f;
float DirectionalLightShadow = 1.0f;
AccumulateDirectionalLighting(GBuffer, TranslatedWorldPosition, CameraVector, ScreenPosition, SvPosition, DynamicShadowFactors, DirectionalLightShadow, DirectLighting, 0);
// LightFunction should only affect direct lighting result
DirectLighting.TotalLight *= ComputeLightFunctionMultiplier(TranslatedWorldPosition);
float2 LocalPosition = SvPosition.xy - View.ViewRectMin.xy;
uint GridIndex = ComputeLightGridCellIndex(uint2(LocalPosition.x, LocalPosition.y), GBuffer.Depth, EyeIndex);
// Local lights
#if ENABLE_CLUSTERED_LIGHTS
{
const FCulledLightsGridHeader CulledLightGridHeader = GetCulledLightsGridHeader(GridIndex);
half4 LocalLightDynamicShadowFactors = 1.0f;
AccumulateLightGridLocalLighting(CulledLightGridHeader, GBuffer, TranslatedWorldPosition, CameraVector, EyeIndex, 0, LocalLightDynamicShadowFactors, DirectLighting);
}
#endif
#if ENABLE_CLUSTERED_REFLECTION || ENABLE_SKY_LIGHT || ENABLE_PLANAR_REFLECTION || MOBILE_SSR_ENABLED
// If we have a single directional light, apply relfection and sky contrubution here
ReflectionEnvironmentSkyLighting(GBuffer, SvPosition, CameraVector, TranslatedWorldPosition, ReflectionVector, GridIndex, GatheredAmbientOcclusion, DirectLighting);
#endif
half3 Color = DirectLighting.TotalLight;
// MobileHDR applies PreExposure in tonemapper
Color *= View.PreExposure;
Color = SafeGetOutColor(Color);
#if USE_GLES_FBF_DEFERRED
OutProxyAdditive.rgb = Color;
#else
OutColor.rgb = Color;
// Blend is enabled only for static skylights
OutColor.a = GatheredAmbientOcclusion;
#endif
}
#if SUPPORT_SPOTLIGHTS_SHADOW
float4 SpotLightShadowSharpenAndFadeFractionAndReceiverDepthBiasAndSoftTransitionScale;
float4 SpotLightShadowmapMinMax;
float4x4 SpotLightShadowWorldToShadowMatrix;
Texture2D LocalLightShadowTexture;
SamplerState LocalLightShadowSampler;
float4 LocalLightShadowBufferSize;
#endif
FDeferredLightData InitDeferredLightFromLightParameters(FLightShaderParameters In, uint InLightType)
{
FDeferredLightData Out = (FDeferredLightData)0;
Out.TranslatedWorldPosition = In.TranslatedWorldPosition;
Out.InvRadius = In.InvRadius;
Out.Color = In.Color;
Out.FalloffExponent = In.FalloffExponent;
Out.Direction = In.Direction;
Out.Tangent = In.Tangent;
Out.SpotAngles = In.SpotAngles;
Out.SpecularScale = In.SpecularScale;
Out.DiffuseScale = In.DiffuseScale;
Out.SourceRadius = In.SourceRadius;
Out.SoftSourceRadius = In.SoftSourceRadius;
Out.SourceLength = In.SourceLength;
Out.RectLightData.BarnCosAngle = In.RectLightBarnCosAngle;
Out.RectLightData.BarnLength = In.RectLightBarnLength;
Out.RectLightData.AtlasData.AtlasUVOffset = In.RectLightAtlasUVOffset;
Out.RectLightData.AtlasData.AtlasUVScale = In.RectLightAtlasUVScale;
Out.RectLightData.AtlasData.AtlasMaxLevel = In.RectLightAtlasMaxLevel;
Out.IESAtlasIndex = In.IESAtlasIndex;
Out.LightFunctionAtlasLightIndex = In.LightFunctionAtlasLightIndex;
Out.bAffectsTranslucentLighting = In.bAffectsTranslucentLighting;
Out.bInverseSquared = In.FalloffExponent == 0;
Out.bRadialLight = true;
Out.bSpotLight = InLightType == LIGHT_TYPE_SPOT;
Out.bRectLight = InLightType == LIGHT_TYPE_RECT;
Out.HairTransmittance = InitHairTransmittanceData();
#if SUPPORT_SPOTLIGHTS_SHADOW
Out.ShadowedBits = 1;
Out.ShadowMapChannelMask = 1.f;
#endif
return Out;
}
void MobileRadialLightPS(
float4 InScreenPosition : TEXCOORD0,
float4 SVPos : SV_POSITION,
#if USE_GLES_FBF_DEFERRED
out HALF4_TYPE OutProxyAdditive : SV_Target0,
out HALF4_TYPE OutGBufferA : SV_Target1,
out HALF4_TYPE OutGBufferB : SV_Target2,
out HALF4_TYPE OutGBufferC : SV_Target3
#else
out HALF4_TYPE OutColor : SV_Target0
#endif
)
{
ResolvedView = ResolveView();
float2 ScreenUV = InScreenPosition.xy / InScreenPosition.w * View.ScreenPositionScaleBias.xy + View.ScreenPositionScaleBias.wz;
FGBufferData GBuffer = MobileFetchAndDecodeGBuffer(ScreenUV, SVPos.xy);
// With a perspective projection, the clip space position is NDC * Clip.w
// With an orthographic projection, clip space is the same as NDC
float2 ClipPosition = GetScreenPositionForProjectionType(InScreenPosition.xy / InScreenPosition.w, GBuffer.Depth);
float3 TranslatedWorldPosition = mul(float4(ClipPosition, GBuffer.Depth, 1), PrimaryView.ScreenToTranslatedWorld).xyz;
half3 CameraVector = normalize(TranslatedWorldPosition);
FLightAccumulator DirectLighting = (FLightAccumulator)0;
FDeferredLightData LightData = InitDeferredLightFromLightParameters(GetRootLightShaderParameters(), RADIAL_LIGHT_TYPE);
// Affect the light color by any other shadow/transmittance before the lighting is evaluated
{
half Attenuation = ComputeLightProfileMultiplier(TranslatedWorldPosition, LightData.TranslatedWorldPosition, -LightData.Direction, LightData.Tangent, LightData.IESAtlasIndex);
LightData.Color *= Attenuation;
}
half Shadow = 1.0;
#if SUPPORT_SPOTLIGHTS_SHADOW
float3 ToLight = LightData.TranslatedWorldPosition - TranslatedWorldPosition;
float3 LocalPosition = -ToLight;
float DistanceSqr = dot(ToLight, ToLight);
half3 L = ToLight * rsqrt(DistanceSqr);
half3 N = GBuffer.WorldNormal;
half NoL = saturate(dot(N, L));
FPCFSamplerSettings Settings;
Settings.ShadowDepthTexture = LocalLightShadowTexture;
Settings.ShadowDepthTextureSampler = LocalLightShadowSampler;
Settings.ShadowBufferSize = LocalLightShadowBufferSize;
Settings.bSubsurface = false;
Settings.bTreatMaxDepthUnshadowed = false;
Settings.DensityMulConstant = 0;
Settings.ProjectionDepthBiasParameters = 0;
float SpotLightShadowSharpen = SpotLightShadowSharpenAndFadeFractionAndReceiverDepthBiasAndSoftTransitionScale.x;
float SpotLightFadeFraction = SpotLightShadowSharpenAndFadeFractionAndReceiverDepthBiasAndSoftTransitionScale.y;
float SpotLightReceiverDepthBias = SpotLightShadowSharpenAndFadeFractionAndReceiverDepthBiasAndSoftTransitionScale.z;
float SpotLightSoftTransitionScale = SpotLightShadowSharpenAndFadeFractionAndReceiverDepthBiasAndSoftTransitionScale.w;
float4 HomogeneousShadowPosition = mul(float4(LocalPosition, 1), SpotLightShadowWorldToShadowMatrix);
float2 ShadowUVs = HomogeneousShadowPosition.xy / HomogeneousShadowPosition.w;
if (all(ShadowUVs >= SpotLightShadowmapMinMax.xy && ShadowUVs <= SpotLightShadowmapMinMax.zw))
{
// Clamp pixel depth in light space for shadowing opaque, because areas of the shadow depth buffer that weren't rendered to will have been cleared to 1
// We want to force the shadow comparison to result in 'unshadowed' in that case, regardless of whether the pixel being shaded is in front or behind that plane
// Invert ShadowZ as the shadow space has been changed (but not yet the filtering code)
float ShadowZ = 1.0f - HomogeneousShadowPosition.z;
float LightSpacePixelDepthForOpaque = min(ShadowZ, 0.99999f);
Settings.SceneDepth = LightSpacePixelDepthForOpaque;
Settings.TransitionScale = SpotLightSoftTransitionScale * lerp(SpotLightReceiverDepthBias, 1.0, NoL);
Shadow = MobileShadowPCF(ShadowUVs, Settings);
Shadow = saturate((Shadow - 0.5) * SpotLightShadowSharpen + 0.5f);
Shadow = lerp(1.0f, Square(Shadow), SpotLightFadeFraction);
}
#endif
half4 LightAttenuation = half4(1, 1, Shadow, Shadow);
float SurfaceShadow = 0;
DirectLighting = AccumulateDynamicLighting(TranslatedWorldPosition, CameraVector, GBuffer, 1, LightData, LightAttenuation, 0, uint2(0, 0), SurfaceShadow);
half3 Color = DirectLighting.TotalLight * ComputeLightFunctionMultiplier(TranslatedWorldPosition);
// MobileHDR applies PreExposure in tonemapper
Color *= View.PreExposure;
Color = SafeGetOutColor(Color);
#if USE_GLES_FBF_DEFERRED
OutProxyAdditive.rgb = Color;
#else
OutColor.rgb = Color;
OutColor.a = 1;
#endif
}
void MobileReflectionEnvironmentSkyLightingPS(
noperspective float4 UVAndScreenPos : TEXCOORD0
, float4 SvPosition : SV_POSITION
#if USE_GLES_FBF_DEFERRED
, out HALF4_TYPE OutProxyAdditive : SV_Target0
, out HALF4_TYPE OutGBufferA : SV_Target1
, out HALF4_TYPE OutGBufferB : SV_Target2
, out HALF4_TYPE OutGBufferC : SV_Target3
#else
, out HALF4_TYPE OutColor : SV_Target0
#endif
)
{
ResolvedView = ResolveView();
FGBufferData GBuffer = MobileFetchAndDecodeGBuffer(UVAndScreenPos.xy, UVAndScreenPos.zw);
float2 ScreenPos = UVAndScreenPos.zw;
float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPos, GBuffer.Depth), GBuffer.Depth, 1), PrimaryView.ScreenToTranslatedWorld).xyz;
half3 CameraVector = normalize(TranslatedWorldPosition);
half3 V = -CameraVector;
half NoV = max(0, dot(GBuffer.WorldNormal, V));
half3 ReflectionVector = GBuffer.WorldNormal * (NoV * 2.0) - V;
const uint EyeIndex = 0;
float2 LocalPosition = SvPosition.xy - View.ViewRectMin.xy;
uint GridIndex = ComputeLightGridCellIndex(uint2(LocalPosition.x, LocalPosition.y), GBuffer.Depth, EyeIndex);
half GatheredAmbientOcclusion = 1;
#if ENABLE_AMBIENT_OCCLUSION
GatheredAmbientOcclusion = Texture2DSample(MobileBasePass.AmbientOcclusionTexture, MobileBasePass.AmbientOcclusionSampler, UVAndScreenPos.xy).r;
GatheredAmbientOcclusion = lerp(1.0, GatheredAmbientOcclusion, MobileBasePass.AmbientOcclusionStaticFraction);
GBuffer.GBufferAO *= GatheredAmbientOcclusion;
#endif
FLightAccumulator DirectLighting = (FLightAccumulator)0;
ReflectionEnvironmentSkyLighting(GBuffer, SvPosition, CameraVector, TranslatedWorldPosition, ReflectionVector, GridIndex, GatheredAmbientOcclusion, DirectLighting);
half3 ReflectionAndSky = DirectLighting.TotalLight;
ReflectionAndSky *= View.PreExposure;
#if USE_GLES_FBF_DEFERRED
OutProxyAdditive.rgb = ReflectionAndSky.rgb;
#else
OutColor.rgb = ReflectionAndSky.rgb;
// Blend is enabled only for static skylights
OutColor.a = GatheredAmbientOcclusion;
#endif
}