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

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;
}