583 lines
24 KiB
HLSL
583 lines
24 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
ForwardLightingCommon.ush
|
|
=============================================================================*/
|
|
|
|
#define NON_DIRECTIONAL_DIRECT_LIGHTING (TRANSLUCENCY_LIGHTING_VOLUMETRIC_NONDIRECTIONAL || TRANSLUCENCY_LIGHTING_VOLUMETRIC_PERVERTEX_NONDIRECTIONAL)
|
|
#define SUPPORT_CONTACT_SHADOWS (MATERIAL_CONTACT_SHADOWS && !FORWARD_SHADING)
|
|
|
|
#include "DeferredLightingCommon.ush"
|
|
#include "LightGridCommon.ush"
|
|
#include "LightData.ush"
|
|
|
|
#define FILTER_DIRECTIONAL_LIGHT_SHADOWING 1
|
|
#include "ForwardShadowingCommon.ush"
|
|
|
|
#ifndef DISABLE_FORWARD_DIRECTIONAL_LIGHT_SHADOW
|
|
#define DISABLE_FORWARD_DIRECTIONAL_LIGHT_SHADOW 0
|
|
#endif
|
|
|
|
#if USE_HAIR_COMPLEX_TRANSMITTANCE
|
|
#include "HairStrands/HairStrandsCommon.ush"
|
|
#include "HairStrands/HairStrandsDeepTransmittanceCommon.ush"
|
|
#include "HairStrands/HairStrandsDeepTransmittanceDualScattering.ush"
|
|
#endif
|
|
|
|
#ifndef VIRTUAL_SHADOW_MAP
|
|
#define VIRTUAL_SHADOW_MAP 0
|
|
#endif
|
|
|
|
#ifndef MATERIAL_ENABLE_TRANSLUCENT_LOCAL_LIGHT_SHADOW
|
|
#define MATERIAL_ENABLE_TRANSLUCENT_LOCAL_LIGHT_SHADOW 0
|
|
#endif
|
|
#ifndef MATERIAL_ENABLE_TRANSLUCENT_HIGH_QUALITY_LOCAL_LIGHT_SHADOW
|
|
#define MATERIAL_ENABLE_TRANSLUCENT_HIGH_QUALITY_LOCAL_LIGHT_SHADOW 0
|
|
#endif
|
|
#ifndef MATERIAL_ENABLE_TRANSLUCENT_HIGH_QUALITY_DIRECTIONAL_LIGHT_SHADOW
|
|
#define MATERIAL_ENABLE_TRANSLUCENT_HIGH_QUALITY_DIRECTIONAL_LIGHT_SHADOW 0
|
|
#endif
|
|
|
|
#if VIRTUAL_SHADOW_MAP
|
|
#include "VirtualShadowMaps/VirtualShadowMapProjectionCommon.ush"
|
|
#if SUPPORT_VSM_FOWARD_QUALITY > 0 || MATERIAL_ENABLE_TRANSLUCENT_HIGH_QUALITY_LOCAL_LIGHT_SHADOW || MATERIAL_ENABLE_TRANSLUCENT_HIGH_QUALITY_DIRECTIONAL_LIGHT_SHADOW
|
|
#include "VirtualShadowMaps/VirtualShadowMapProjectionFilter.ush"
|
|
#include "VirtualShadowMaps/VirtualShadowMapProjectionDirectional.ush"
|
|
#include "VirtualShadowMaps/VirtualShadowMapProjectionSpot.ush"
|
|
#endif // SUPPORT_VSM_FOWARD_QUALITY
|
|
#endif // VIRTUAL_SHADOW_MAP
|
|
|
|
#ifndef ENABLE_FORWARD_CLOUD_SHADOW
|
|
#define ENABLE_FORWARD_CLOUD_SHADOW 0
|
|
#endif
|
|
|
|
float PrevSceneColorPreExposureInv;
|
|
|
|
void UpdateNearestSample(float Z, float2 UV, float FullResZ, inout float MinDist, inout float2 NearestUV)
|
|
{
|
|
float DepthDelta = abs(Z - FullResZ);
|
|
|
|
FLATTEN
|
|
if (DepthDelta < MinDist)
|
|
{
|
|
MinDist = DepthDelta;
|
|
NearestUV = UV;
|
|
}
|
|
}
|
|
|
|
float2 CalculateNearestResolvedDepthScreenUV(float2 ScreenUV, float SceneDepth)
|
|
{
|
|
float2 EffectiveScreenUV = ScreenUV;
|
|
|
|
if (View.NumSceneColorMSAASamples > 1)
|
|
{
|
|
int2 IntScreenUV = int2(trunc(ScreenUV * View.BufferSizeAndInvSize.xy));
|
|
|
|
float DeferredShadowingDepth = ConvertFromDeviceZ(OpaqueBasePass.ResolvedSceneDepthTexture.Load(int3(IntScreenUV, 0)).r);
|
|
float RelativeDepthThreshold = .01f;
|
|
|
|
// Fragment depth doesn't match what we used for deferred shadowing, search neighbors in cross pattern
|
|
// Even with this nearest depth upsampling there can be edge artifacts from deferred shadowing,
|
|
// Since depth testing and stencil testing used during shadow projection are done per-sample and we're fetching from the resolved light attenuation
|
|
if (abs(DeferredShadowingDepth - SceneDepth) / SceneDepth > RelativeDepthThreshold)
|
|
{
|
|
float2 TexelSize = View.BufferSizeAndInvSize.zw;
|
|
float MinDist = 1.e8f;
|
|
|
|
float2 LeftUV = ScreenUV + float2(-TexelSize.x, 0);
|
|
float LeftDepth = ConvertFromDeviceZ(OpaqueBasePass.ResolvedSceneDepthTexture.Load(int3(IntScreenUV.x - 1, IntScreenUV.y, 0)).r);
|
|
UpdateNearestSample(LeftDepth, LeftUV, SceneDepth, MinDist, EffectiveScreenUV);
|
|
|
|
float2 UpUV = ScreenUV + float2(0, TexelSize.y);
|
|
float UpDepth = ConvertFromDeviceZ(OpaqueBasePass.ResolvedSceneDepthTexture.Load(int3(IntScreenUV.x, IntScreenUV.y + 1, 0)).r);
|
|
UpdateNearestSample(UpDepth, UpUV, SceneDepth, MinDist, EffectiveScreenUV);
|
|
|
|
float2 RightUV = ScreenUV + float2(TexelSize.x, 0);
|
|
float RightDepth = ConvertFromDeviceZ(OpaqueBasePass.ResolvedSceneDepthTexture.Load(int3(IntScreenUV.x + 1, IntScreenUV.y, 0)).r);
|
|
UpdateNearestSample(RightDepth, RightUV, SceneDepth, MinDist, EffectiveScreenUV);
|
|
|
|
float2 BottomUV = ScreenUV + float2(0, -TexelSize.y);
|
|
float BottomDepth = ConvertFromDeviceZ(OpaqueBasePass.ResolvedSceneDepthTexture.Load(int3(IntScreenUV.x, IntScreenUV.y - 1, 0)).r);
|
|
UpdateNearestSample(BottomDepth, BottomUV, SceneDepth, MinDist, EffectiveScreenUV);
|
|
}
|
|
}
|
|
|
|
return EffectiveScreenUV;
|
|
}
|
|
|
|
float4 GetForwardDynamicShadowFactors(float2 ScreenUV)
|
|
{
|
|
int2 IntScreenUV = int2(trunc(ScreenUV * View.BufferSizeAndInvSize.xy));
|
|
float4 Value = 1.0f;
|
|
// Avoid sampling the white texture fallback because Load returns 0 when out-of-bound.
|
|
BRANCH
|
|
if (OpaqueBasePass.UseForwardScreenSpaceShadowMask)
|
|
{
|
|
Value = OpaqueBasePass.ForwardScreenSpaceShadowMaskTexture.Load(int3(IntScreenUV, 0));
|
|
}
|
|
return DecodeLightAttenuation(Value);
|
|
}
|
|
|
|
float GetIndirectOcclusion(float2 ScreenUV, bool bHasDynamicIndirectShadowCasterRepresentation)
|
|
{
|
|
float IndirectOcclusion;
|
|
|
|
uint IndirectOcclusionWidth, IndirectOcclusionHeight;
|
|
OpaqueBasePass.IndirectOcclusionTexture.GetDimensions(IndirectOcclusionWidth, IndirectOcclusionHeight);
|
|
|
|
int2 IntScreenUV = int2(trunc(ScreenUV * float2(IndirectOcclusionWidth, IndirectOcclusionHeight)));
|
|
IndirectOcclusion = OpaqueBasePass.IndirectOcclusionTexture.Load(int3(IntScreenUV, 0)).x;
|
|
|
|
// Reduce self shadowing intensity on characters (reuse distance field bit, really should be HasCapsuleShadowRepresentation)
|
|
IndirectOcclusion = lerp(1, IndirectOcclusion, bHasDynamicIndirectShadowCasterRepresentation ? View.IndirectCapsuleSelfShadowingIntensity : 1);
|
|
|
|
return IndirectOcclusion;
|
|
}
|
|
|
|
FDeferredLightingSplit GetForwardDirectLightingSplit(
|
|
uint2 PixelPos,
|
|
uint GridIndex, float3 TranslatedWorldPosition, float3 CameraVector, FGBufferData GBufferData, float2 ScreenUV, uint PrimitiveId, uint EyeIndex, float Dither,
|
|
float InDirectionalLightCloudShadow, float3 InDirectionalLightAtmosphereTransmittance, inout float OutDirectionalLightShadow,
|
|
bool bSeparateMainDirLightLuminance, inout float3 SeparatedMainDirLightLuminance, bool bSkipDirLightVirtualShadowMapEvaluation)
|
|
{
|
|
float4 DynamicShadowFactors = 1;
|
|
|
|
#if MATERIALBLENDING_SOLID || MATERIALBLENDING_MASKED
|
|
DynamicShadowFactors = GetForwardDynamicShadowFactors(ScreenUV);
|
|
#endif
|
|
|
|
FDeferredLightingSplit DirectLighting;
|
|
DirectLighting.DiffuseLighting = 0;
|
|
DirectLighting.SpecularLighting = 0;
|
|
DirectLighting.LightingLuminance = 0;
|
|
|
|
float SpecularScale = 1;
|
|
#if TRANSLUCENCY_ANY_VOLUMETRIC
|
|
// No specular on volumetric translucency lighting modes
|
|
SpecularScale = 0;
|
|
#endif
|
|
|
|
uint LightingChannelMask = GetPrimitive_LightingChannelMask(PrimitiveId);
|
|
|
|
const FDirectionalLightData DirectionalLightData = GetDirectionalLightData();
|
|
|
|
BRANCH
|
|
if (DirectionalLightData.HasDirectionalLight
|
|
#if MATERIALBLENDING_ANY_TRANSLUCENT
|
|
&& DirectionalLightData.bAffectsTranslucentLighting > 0
|
|
#endif
|
|
)
|
|
{
|
|
half4 PreviewShadowMapChannelMask = 1;
|
|
uint DirLightingChannelMask = LIGHTING_CHANNEL_MASK;
|
|
FDeferredLightData LightData = ConvertToDeferredLight(DirectionalLightData, SpecularScale, PreviewShadowMapChannelMask, DirLightingChannelMask);
|
|
#if USE_HAIR_COMPLEX_TRANSMITTANCE
|
|
if (GBufferData.ShadingModelID == SHADINGMODELID_HAIR)
|
|
{
|
|
LightData.HairTransmittance = EvaluateDualScattering(GBufferData, -CameraVector, LightData.Direction);
|
|
}
|
|
#endif
|
|
|
|
// We want to force the directional light shadow when using water material to see shadow on the water. This could be an option later.
|
|
#if DISABLE_FORWARD_DIRECTIONAL_LIGHT_SHADOW
|
|
float4 LightAttenuation = float4(1, 1, 1, 1);
|
|
#elif ((MATERIALBLENDING_SOLID || MATERIALBLENDING_MASKED) && !MATERIAL_SHADINGMODEL_SINGLELAYERWATER)
|
|
float DynamicShadowing = dot(PreviewShadowMapChannelMask, DynamicShadowFactors);
|
|
|
|
// In the forward shading path we can't separate per-object shadows from CSM, since we only spend one light attenuation channel per light
|
|
// If CSM is enabled (distance fading to precomputed shadowing is active), treat all of our dynamic shadowing as whole scene shadows that will be faded out at the max CSM distance
|
|
// If CSM is not enabled, allow our dynamic shadowing to coexist with precomputed shadowing
|
|
float PerObjectShadowing = LightData.DistanceFadeMAD.y < 0.0f ? 1.0f : DynamicShadowing;
|
|
float WholeSceneShadowing = LightData.DistanceFadeMAD.y < 0.0f ? DynamicShadowing : 1.0f;
|
|
|
|
float4 LightAttenuation = float4(WholeSceneShadowing.xx, PerObjectShadowing.xx);
|
|
#else
|
|
LightData.ShadowedBits = 1;
|
|
LightData.ShadowMapChannelMask.x = 1;
|
|
#if TRANSLUCENCY_LIGHTING_SURFACE_FORWARDSHADING
|
|
GBufferData.PrecomputedShadowFactors.x = ComputeDirectionalLightStaticShadowing(TranslatedWorldPosition).x;
|
|
#else
|
|
GBufferData.PrecomputedShadowFactors.x = 1;
|
|
#endif
|
|
bool bUnused = false;
|
|
float DynamicShadowFactor = ComputeDirectionalLightDynamicShadowing(TranslatedWorldPosition, GBufferData.Depth, bUnused);
|
|
|
|
#if VIRTUAL_SHADOW_MAP
|
|
BRANCH
|
|
if ( !bSkipDirLightVirtualShadowMapEvaluation && ForwardLightStruct.DirectionalLightVSM != INDEX_NONE )
|
|
{
|
|
#if SUPPORT_VSM_FOWARD_QUALITY == 1 || MATERIAL_ENABLE_TRANSLUCENT_HIGH_QUALITY_DIRECTIONAL_LIGHT_SHADOW
|
|
{
|
|
const FLightShaderParameters DirectionalLight = ConvertToLightShaderParameters(LightData);
|
|
|
|
const float ScreenRayLengthWorld = VirtualShadowMap.ScreenRayLength * View.ClipToView[1][1] * GBufferData.Depth;
|
|
float SMRTRayOffset = ScreenRayLengthWorld;
|
|
|
|
FVirtualShadowMapSampleResult VirtualShadowMapSample = TraceDirectional(
|
|
ForwardLightStruct.DirectionalLightVSM,
|
|
DirectionalLight,
|
|
PixelPos,
|
|
GBufferData.Depth,
|
|
TranslatedWorldPosition,
|
|
SMRTRayOffset,
|
|
Dither,
|
|
GBufferData.WorldNormal);
|
|
|
|
FilterVirtualShadowMapSampleResult(PixelPos, VirtualShadowMapSample);
|
|
|
|
DynamicShadowFactor *= VirtualShadowMapSample.ShadowFactor;
|
|
}
|
|
#else
|
|
{
|
|
FVirtualShadowMapSampleResult VirtualShadowMapSample = SampleVirtualShadowMapDirectional( ForwardLightStruct.DirectionalLightVSM, TranslatedWorldPosition );
|
|
DynamicShadowFactor *= VirtualShadowMapSample.ShadowFactor;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
float4 LightAttenuation = float4(DynamicShadowFactor.x, DynamicShadowFactor.x, 1, 1);
|
|
#endif
|
|
|
|
FDeferredLightingSplit NewLighting = GetDynamicLightingSplit(TranslatedWorldPosition, -CameraVector, GBufferData, 1, LightData, LightAttenuation, Dither, uint2(0,0), OutDirectionalLightShadow);
|
|
|
|
FLATTEN
|
|
if (DirLightingChannelMask & LightingChannelMask)
|
|
{
|
|
#if ENABLE_FORWARD_CLOUD_SHADOW
|
|
// This cannot go into LightAttenuation due to some distance based attenuation math being applied.
|
|
NewLighting.DiffuseLighting *= InDirectionalLightCloudShadow;
|
|
NewLighting.SpecularLighting *= InDirectionalLightCloudShadow;
|
|
NewLighting.LightingLuminance *= InDirectionalLightCloudShadow;
|
|
#endif
|
|
NewLighting.DiffuseLighting.rgb *= InDirectionalLightAtmosphereTransmittance;
|
|
NewLighting.SpecularLighting.rgb *= InDirectionalLightAtmosphereTransmittance;
|
|
NewLighting.LightingLuminance *= Luminance(InDirectionalLightAtmosphereTransmittance);
|
|
|
|
if (bSeparateMainDirLightLuminance)
|
|
{
|
|
SeparatedMainDirLightLuminance += NewLighting.DiffuseLighting.rgb;
|
|
SeparatedMainDirLightLuminance += NewLighting.SpecularLighting.rgb;
|
|
}
|
|
else
|
|
{
|
|
DirectLighting.DiffuseLighting += NewLighting.DiffuseLighting;
|
|
DirectLighting.SpecularLighting += NewLighting.SpecularLighting;
|
|
DirectLighting.LightingLuminance += NewLighting.LightingLuminance;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !DISABLE_FORWARD_LOCAL_LIGHTS
|
|
|
|
const FCulledLightsGridHeader CulledLightsGridHeader = GetCulledLightsGridHeader(GridIndex);
|
|
|
|
|
|
// Limit max to ForwardLightStruct.NumLocalLights.
|
|
// This prevents GPU hangs when the PS tries to read from uninitialized NumCulledLightsGrid buffer
|
|
const uint NumLightsInGridCell = min(CulledLightsGridHeader.NumLights, GetMaxLightsPerCell());
|
|
|
|
LOOP
|
|
for (uint GridLightListIndex = 0; GridLightListIndex < NumLightsInGridCell; GridLightListIndex++)
|
|
{
|
|
half4 PreviewShadowMapChannelMask = 1;
|
|
uint LocalLightingChannelMask = LIGHTING_CHANNEL_MASK;
|
|
const FLocalLightData LocalLight = GetLocalLightDataFromGrid(CulledLightsGridHeader.DataStartIndex + GridLightListIndex, EyeIndex);
|
|
|
|
#if MATERIALBLENDING_ANY_TRANSLUCENT
|
|
if(UnpackAffectsTranslucentLighting(LocalLight) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
FDeferredLightData LightData = ConvertToDeferredLight(LocalLight, SpecularScale, PreviewShadowMapChannelMask, LocalLightingChannelMask);
|
|
|
|
// Rect light optionally supported in forward.
|
|
LightData.bRectLight = LightData.bRectLight && SUPPORT_RECTLIGHT_ON_FORWARD_LIT_TRANSLUCENT;
|
|
#if USE_HAIR_COMPLEX_TRANSMITTANCE
|
|
if (GBufferData.ShadingModelID == SHADINGMODELID_HAIR)
|
|
{
|
|
LightData.HairTransmittance = EvaluateDualScattering(GBufferData, -CameraVector, LightData.Direction);
|
|
}
|
|
#endif
|
|
#if USE_IES_PROFILE
|
|
LightData.Color *= ComputeLightProfileMultiplier(TranslatedWorldPosition, LightData.TranslatedWorldPosition, -LightData.Direction, LightData.Tangent, LightData.IESAtlasIndex);
|
|
#endif
|
|
|
|
float DynamicShadowing = dot(PreviewShadowMapChannelMask, DynamicShadowFactors);
|
|
float4 LightAttenuation = float4(1, 1, DynamicShadowing.x, DynamicShadowing.x);
|
|
|
|
#if VIRTUAL_SHADOW_MAP && (SUPPORT_SHADOWED_LOCAL_LIGHT_ON_FORWARD_LIT_TRANSLUCENT || MATERIAL_ENABLE_TRANSLUCENT_LOCAL_LIGHT_SHADOW)
|
|
BRANCH
|
|
if (LocalLight.Internal.VirtualShadowMapId != INDEX_NONE)
|
|
{
|
|
float DynamicShadowFactor = 1.f;
|
|
LightData.ShadowedBits = 1;
|
|
#if SUPPORT_VSM_FOWARD_QUALITY == 1 || MATERIAL_ENABLE_TRANSLUCENT_HIGH_QUALITY_LOCAL_LIGHT_SHADOW
|
|
{
|
|
const FLightShaderParameters LocalLightShaderParameters = ConvertToLightShaderParameters(LightData);
|
|
|
|
const float ScreenRayLengthWorld = VirtualShadowMap.ScreenRayLength * View.ClipToView[1][1] * GBufferData.Depth;
|
|
float SMRTRayOffset = ScreenRayLengthWorld;
|
|
|
|
FVirtualShadowMapSampleResult VirtualShadowMapSample = TraceLocalLight(
|
|
LocalLight.Internal.VirtualShadowMapId,
|
|
LocalLightShaderParameters,
|
|
PixelPos,
|
|
GBufferData.Depth,
|
|
TranslatedWorldPosition,
|
|
SMRTRayOffset,
|
|
Dither,
|
|
GBufferData.WorldNormal);
|
|
|
|
FilterVirtualShadowMapSampleResult(PixelPos, VirtualShadowMapSample);
|
|
|
|
DynamicShadowFactor *= VirtualShadowMapSample.ShadowFactor;
|
|
}
|
|
#else
|
|
{
|
|
FVirtualShadowMapSampleResult VirtualShadowMapSample = SampleVirtualShadowMapLocal(LocalLight.Internal.VirtualShadowMapId, TranslatedWorldPosition);
|
|
DynamicShadowFactor *= VirtualShadowMapSample.ShadowFactor;
|
|
}
|
|
#endif
|
|
|
|
LightAttenuation = DynamicShadowFactor;
|
|
}
|
|
#endif
|
|
|
|
float SurfaceShadow = 1.0f;
|
|
FDeferredLightingSplit NewLighting = GetDynamicLightingSplit(TranslatedWorldPosition, -CameraVector, GBufferData, 1, LightData, LightAttenuation, Dither, uint2(0,0), SurfaceShadow);
|
|
|
|
FLATTEN
|
|
if (LocalLightingChannelMask & LightingChannelMask)
|
|
{
|
|
DirectLighting.DiffuseLighting += NewLighting.DiffuseLighting;
|
|
DirectLighting.SpecularLighting += NewLighting.SpecularLighting;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
// Zero out direct lighting if show flag is disabled
|
|
if (ForwardLightStruct.DirectLightingShowFlag == 0)
|
|
{
|
|
DirectLighting.DiffuseLighting = 0.0f;
|
|
DirectLighting.SpecularLighting = 0.0f;
|
|
}
|
|
|
|
return DirectLighting;
|
|
}
|
|
|
|
float3 GetForwardDirectLightingForVertexLighting(uint GridIndex, float3 TranslatedWorldPosition, float SceneDepth, float3 WorldNormal, uint EyeIndex, float InDirectionalLightCloudShadow)
|
|
{
|
|
float3 DirectLighting = 0;
|
|
// Using white for diffuse color, real diffuse color will be incorporated per-pixel
|
|
float3 DiffuseColor = 1.0f;
|
|
|
|
const FDirectionalLightData DirectionalLightData = GetDirectionalLightData();
|
|
|
|
BRANCH
|
|
if (DirectionalLightData.HasDirectionalLight)
|
|
{
|
|
float3 N = WorldNormal;
|
|
float3 L = DirectionalLightData.DirectionalLightDirection;
|
|
float NoL = saturate(dot(N, L));
|
|
|
|
float3 LightColor = DirectionalLightData.DirectionalLightColor;
|
|
|
|
#if NON_DIRECTIONAL_DIRECT_LIGHTING
|
|
NoL = 1.0f;
|
|
#endif
|
|
|
|
float ShadowFactor = ComputeDirectionalLightStaticShadowing(TranslatedWorldPosition);
|
|
bool bUnused = false;
|
|
ShadowFactor *= ComputeDirectionalLightDynamicShadowing(TranslatedWorldPosition, SceneDepth, bUnused);
|
|
|
|
#if ENABLE_FORWARD_CLOUD_SHADOW
|
|
ShadowFactor *= InDirectionalLightCloudShadow;
|
|
#endif
|
|
|
|
// No specular for vertex lighting
|
|
float3 DiffuseLighting = Diffuse_Lambert(DiffuseColor);
|
|
DirectLighting += LightColor * (NoL * ShadowFactor) * DiffuseLighting;
|
|
}
|
|
|
|
const FCulledLightsGridHeader CulledLightsGridHeader = GetCulledLightsGridHeader(GridIndex);
|
|
|
|
// Limit max to ForwardLightStruct.NumLocalLights.
|
|
// This prevents GPU hangs when the PS tries to read from uninitialized NumCulledLightsGrid buffer
|
|
const uint NumLightsInGridCell = min(CulledLightsGridHeader.NumLights, GetMaxLightsPerCell());
|
|
|
|
LOOP
|
|
for (uint GridLightListIndex = 0; GridLightListIndex < NumLightsInGridCell; GridLightListIndex++)
|
|
{
|
|
const FLocalLightData LocalLight = GetLocalLightDataFromGrid(CulledLightsGridHeader.DataStartIndex + GridLightListIndex, EyeIndex);
|
|
const FSimpleDeferredLightData LightData = ConvertToSimpleLight(LocalLight);
|
|
|
|
// No specular for vertex lighting
|
|
float3 CameraVector = 0;
|
|
float3 SpecularColor = 0;
|
|
float Roughness = 1.0f;
|
|
DirectLighting += GetSimpleDynamicLighting(TranslatedWorldPosition, CameraVector, WorldNormal, 1, DiffuseColor, SpecularColor, Roughness, LightData);
|
|
}
|
|
|
|
// Zero out direct lighting if show flag is disabled
|
|
if (ForwardLightStruct.DirectLightingShowFlag == 0)
|
|
{
|
|
DirectLighting = 0.0f;
|
|
}
|
|
|
|
return DirectLighting;
|
|
}
|
|
|
|
uint MortonCode( uint x )
|
|
{
|
|
//x = (x ^ (x << 8)) & 0x00ff00ff;
|
|
//x = (x ^ (x << 4)) & 0x0f0f0f0f;
|
|
x = (x ^ (x << 2)) & 0x33333333;
|
|
x = (x ^ (x << 1)) & 0x55555555;
|
|
return x;
|
|
}
|
|
|
|
// Translucency Surface per-pixel uses blended reflection captures by default in the deferred renderer
|
|
// Have to opt-in to blended reflection captures in the forward renderer
|
|
#define USE_BLENDED_REFLECTION_CAPTURES_DEFERRED (!FORWARD_SHADING && (TRANSLUCENCY_LIGHTING_SURFACE_FORWARDSHADING || TRANSLUCENCY_LIGHTING_SURFACE_LIGHTINGVOLUME))
|
|
#define USE_BLENDED_REFLECTION_CAPTURES_FORWARD (FORWARD_SHADING && MATERIAL_HQ_FORWARD_REFLECTION_CAPTURES)
|
|
#define REFLECTION_COMPOSITE_USE_BLENDED_REFLECTION_CAPTURES ((USE_BLENDED_REFLECTION_CAPTURES_DEFERRED || USE_BLENDED_REFLECTION_CAPTURES_FORWARD) && FEATURE_LEVEL >= FEATURE_LEVEL_SM5)
|
|
// REFLECTION_COMPOSITE_SUPPORT_SKYLIGHT_BLEND covers forward rendered material for deferred and forward shading pathes.
|
|
#define REFLECTION_COMPOSITE_SUPPORT_SKYLIGHT_BLEND (MATERIAL_FORWARD_BLENDS_SKYLIGHT_CUBEMAPS || FORWARD_SHADING_FORCES_SKYLIGHT_CUBEMAPS_BLENDING)
|
|
#define REFLECTION_COMPOSITE_HAS_BOX_CAPTURES 1
|
|
#define REFLECTION_COMPOSITE_HAS_SPHERE_CAPTURES 1
|
|
#include "ReflectionEnvironmentComposite.ush"
|
|
|
|
half3 GetImageBasedReflectionSpecular(FMaterialPixelParameters MaterialParameters, float3 RayDirection, half Roughness, half IndirectIrradiance, uint GridIndex, int SingleCaptureIndex)
|
|
{
|
|
float3 SpecularIBL;
|
|
bool bUseLumenFrontLayerReflection = false;
|
|
|
|
#if (TRANSLUCENCY_LIGHTING_SURFACE_FORWARDSHADING || TRANSLUCENCY_LIGHTING_SURFACE_LIGHTINGVOLUME) && MATERIALBLENDING_ANY_TRANSLUCENT && PROJECT_SUPPORTS_LUMEN && !FORWARD_SHADING
|
|
|
|
// For translucent rendered after motion blur, we cannot use Lumen front layer reflection because depth test is disabled in this case
|
|
// (depth is jittered and post motion blur composition can happen at a different resolution with dynamic resolution scaling).
|
|
// Such Lumen reflections would look different on translucent meshes when occluded by opaque meshes.
|
|
bUseLumenFrontLayerReflection = UseFrontLayerReflection(MaterialParameters.ViewBufferUV, MaterialParameters.ScreenPosition.w) && !MATERIAL_TRANSLUCENT_PASS_AFTERMOTIONBLUR;
|
|
|
|
FRadianceCacheCoverage LumenRadianceCacheCoverage = InitRadianceCacheCoverage();
|
|
LumenRadianceCacheCoverage.bValid = false;
|
|
|
|
// Lumen Radiance Cache for reflections on translucency
|
|
if (FinalProbeResolution > 0 && !bUseLumenFrontLayerReflection)
|
|
{
|
|
float ClipmapDitherRandom = InterleavedGradientNoise(MaterialParameters.SvPosition.xy, View.StateFrameIndexMod8);
|
|
LumenRadianceCacheCoverage = GetRadianceCacheCoverage(WSHackToFloat(GetWorldPosition(MaterialParameters)), RayDirection, ClipmapDitherRandom);
|
|
}
|
|
|
|
if (bUseLumenFrontLayerReflection)
|
|
{
|
|
SpecularIBL = SampleFrontLayerReflection(MaterialParameters.ViewBufferUV);
|
|
}
|
|
else if (LumenRadianceCacheCoverage.bValid)
|
|
{
|
|
float ConeHalfAngle = 0;
|
|
SpecularIBL = SampleRadianceCacheInterpolated(LumenRadianceCacheCoverage, WSHackToFloat(GetWorldPosition(MaterialParameters)), RayDirection, ConeHalfAngle, /*RandomScalarForStochasticInterpolation*/ 0.5f).Radiance;
|
|
}
|
|
// Fallback to unshadowed skylight if no valid Radiance Cache
|
|
else
|
|
#endif
|
|
{
|
|
FCulledReflectionCapturesGridHeader CulledReflectionCapturesGridHeader = GetCulledReflectionCapturesGridHeader(GridIndex);
|
|
|
|
const bool bCompositeSkylight = true;
|
|
SpecularIBL = CompositeReflectionCapturesAndSkylightTWS(
|
|
1.0f,
|
|
MaterialParameters.WorldPosition_CamRelative,
|
|
RayDirection,
|
|
Roughness,
|
|
IndirectIrradiance,
|
|
1.0f,
|
|
0.0f,
|
|
CulledReflectionCapturesGridHeader.NumReflectionCaptures,
|
|
CulledReflectionCapturesGridHeader.DataStartIndex,
|
|
SingleCaptureIndex,
|
|
bCompositeSkylight);
|
|
}
|
|
|
|
#if MATERIAL_SSR && !FORWARD_SHADING
|
|
if( View.CameraCut == 0 && TranslucentBasePass.SSRQuality > 0 && !bUseLumenFrontLayerReflection)
|
|
{
|
|
float StepOffset = InterleavedGradientNoise( MaterialParameters.SvPosition.xy, View.StateFrameIndexMod8 );
|
|
StepOffset -= 0.5;
|
|
|
|
bool bDebugPrint = false;
|
|
|
|
float3 HitUVz;
|
|
float Level = 0;
|
|
|
|
bool bHit = RayCast(
|
|
TranslucentBasePass.HZBTexture, TranslucentBasePass.HZBSampler,
|
|
MaterialParameters.WorldPosition_CamRelative, RayDirection, Roughness, MaterialParameters.ScreenPosition.w,
|
|
12, StepOffset,
|
|
TranslucentBasePass.HZBUvFactorAndInvFactor,
|
|
bDebugPrint,
|
|
HitUVz,
|
|
Level
|
|
);
|
|
|
|
BRANCH if( bHit )
|
|
{
|
|
float2 SampleUV;
|
|
float Vignette;
|
|
ReprojectHit(TranslucentBasePass.PrevScreenPositionScaleBias, HitUVz, SampleUV, Vignette);
|
|
|
|
SampleUV = clamp(SampleUV, TranslucentBasePass.PrevSceneColorBilinearUVMin, TranslucentBasePass.PrevSceneColorBilinearUVMax);
|
|
|
|
float4 SSR = SampleScreenColor(
|
|
TranslucentBasePass.PrevSceneColor,
|
|
TranslucentBasePass.PrevSceneColorSampler,
|
|
SampleUV);
|
|
|
|
SSR *= Vignette * saturate( 2 - 6.6 * Roughness );
|
|
|
|
SSR.rgb *= TranslucentBasePass.PrevSceneColorPreExposureInv;
|
|
SpecularIBL.rgb = SpecularIBL.rgb * (1 - SSR.a) + SSR.rgb;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
float3 SpecularLighting = SpecularIBL.rgb;
|
|
|
|
// Have to opt-in to receiving planar reflections with forward shading
|
|
#if !FORWARD_SHADING || MATERIAL_PLANAR_FORWARD_REFLECTIONS
|
|
// Plane normal will be zero if the feature is disabled
|
|
BRANCH
|
|
if (abs(dot(PlanarReflectionStruct.ReflectionPlane.xyz, 1)) > .0001f)
|
|
{
|
|
// Reuse ReflectionCubemapSampler to avoid reducing the sampler count available to artists
|
|
float4 PlanarReflection = ComputePlanarReflections(MaterialParameters.WorldPosition_CamRelative, MaterialParameters.WorldNormal, Roughness);
|
|
// Planar reflections win over SSR and reflection environment
|
|
SpecularLighting = PlanarReflection.rgb + (1 - PlanarReflection.a) * SpecularLighting;
|
|
}
|
|
#endif
|
|
|
|
return SpecularLighting;
|
|
}
|
|
|
|
half3 GetImageBasedReflectionLighting(FMaterialPixelParameters MaterialParameters, half Roughness, half3 SpecularColor, half IndirectIrradiance, uint GridIndex, int SingleCaptureIndex)
|
|
{
|
|
float3 N = MaterialParameters.WorldNormal;
|
|
float3 V = MaterialParameters.CameraVector;
|
|
|
|
float3 RayDirection = 2 * dot(V, N) * N - V;
|
|
half NoV = saturate(dot(N, V));
|
|
|
|
const float3 SpecularLighting = GetImageBasedReflectionSpecular(MaterialParameters, RayDirection, Roughness, IndirectIrradiance, GridIndex, SingleCaptureIndex);
|
|
|
|
#if MATERIAL_USE_PREINTEGRATED_GF
|
|
SpecularColor = EnvBRDF(SpecularColor, Roughness, NoV);
|
|
#else
|
|
SpecularColor = EnvBRDFApprox(SpecularColor, Roughness, NoV);
|
|
#endif
|
|
return SpecularLighting * SpecularColor;
|
|
}
|