Files
UnrealEngineUWP/Engine/Shaders/Private/ForwardLightingCommon.ush

510 lines
21 KiB
Plaintext

// 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
#if VIRTUAL_SHADOW_MAP
#include "VirtualShadowMaps/VirtualShadowMapProjectionCommon.ush"
#endif
#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(
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)
{
float3 WorldPosition = TranslatedWorldPosition - LWCHackToFloat(PrimaryView.PreViewTranslation);
float4 DynamicShadowFactors = 1;
#if MATERIALBLENDING_SOLID || MATERIALBLENDING_MASKED
DynamicShadowFactors = GetForwardDynamicShadowFactors(ScreenUV);
#endif
FDeferredLightingSplit DirectLighting;
DirectLighting.DiffuseLighting = 0;
DirectLighting.SpecularLighting = 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(EyeIndex);
BRANCH
if (DirectionalLightData.HasDirectionalLight)
{
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 && ForwardLightData.DirectionalLightVSM != INDEX_NONE )
{
FVirtualShadowMapSampleResult VirtualShadowMapSample = SampleVirtualShadowMapTranslatedWorld( ForwardLightData.DirectionalLightVSM, TranslatedWorldPosition );
DynamicShadowFactor *= VirtualShadowMapSample.ShadowFactor;
}
#endif
float4 LightAttenuation = float4(DynamicShadowFactor.x, DynamicShadowFactor.x, 1, 1);
#endif
FDeferredLightingSplit NewLighting = GetDynamicLightingSplit(TranslatedWorldPosition, -CameraVector, GBufferData, 1, GBufferData.ShadingModelID, 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;
#endif
NewLighting.DiffuseLighting.rgb *= InDirectionalLightAtmosphereTransmittance;
NewLighting.SpecularLighting.rgb *= InDirectionalLightAtmosphereTransmittance;
if (bSeparateMainDirLightLuminance)
{
SeparatedMainDirLightLuminance += NewLighting.DiffuseLighting.rgb;
SeparatedMainDirLightLuminance += NewLighting.SpecularLighting.rgb;
}
else
{
DirectLighting.DiffuseLighting += NewLighting.DiffuseLighting;
DirectLighting.SpecularLighting += NewLighting.SpecularLighting;
}
}
}
#if !DISABLE_FORWARD_LOCAL_LIGHTS
const FCulledLightsGridData CulledLightsGrid = GetCulledLightsGrid(GridIndex, EyeIndex);
// Limit max to ForwardLightData.NumLocalLights.
// This prevents GPU hangs when the PS tries to read from uninitialized NumCulledLightsGrid buffer
const uint NumLocalLights = min(CulledLightsGrid.NumLocalLights, GetNumLocalLights(EyeIndex));
LOOP
for (uint LocalLightListIndex = 0; LocalLightListIndex < NumLocalLights; LocalLightListIndex++)
{
half4 PreviewShadowMapChannelMask = 1;
uint LocalLightingChannelMask = LIGHTING_CHANNEL_MASK;
const FLocalLightData LocalLight = GetLocalLightData(CulledLightsGrid.DataStartIndex + LocalLightListIndex, EyeIndex);
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
float DynamicShadowing = dot(PreviewShadowMapChannelMask, DynamicShadowFactors);
float4 LightAttenuation = float4(1, 1, DynamicShadowing.x, DynamicShadowing.x);
float SurfaceShadow = 1.0f;
FDeferredLightingSplit NewLighting = GetDynamicLightingSplit(TranslatedWorldPosition, -CameraVector, GBufferData, 1, GBufferData.ShadingModelID, 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 (ForwardLightData.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(EyeIndex);
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 FCulledLightsGridData CulledLightsGrid = GetCulledLightsGrid(GridIndex, EyeIndex);
// Limit max to ForwardLightData.NumLocalLights.
// This prevents GPU hangs when the PS tries to read from uninitialized NumCulledLightsGrid buffer
const uint NumLocalLights = min(CulledLightsGrid.NumLocalLights, GetNumLocalLights(EyeIndex));
LOOP
for (uint LocalLightListIndex = 0; LocalLightListIndex < NumLocalLights; LocalLightListIndex++)
{
const FLocalLightData LocalLight = GetLocalLightData(CulledLightsGrid.DataStartIndex + LocalLightListIndex, 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 (ForwardLightData.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)
#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, uint EyeIndex)
{
float3 SpecularIBL;
bool bUseLumenFrontLayerReflection = false;
#if (TRANSLUCENCY_LIGHTING_SURFACE_FORWARDSHADING || TRANSLUCENCY_LIGHTING_SURFACE_LIGHTINGVOLUME) && MATERIALBLENDING_ANY_TRANSLUCENT && PROJECT_SUPPORTS_LUMEN && !FORWARD_SHADING
bUseLumenFrontLayerReflection = UseFrontLayerReflection(MaterialParameters.ViewBufferUV, MaterialParameters.ScreenPosition.w);
FRadianceCacheCoverage LumenRadianceCacheCoverage = InitRadianceCacheCoverage();
LumenRadianceCacheCoverage.bValid = false;
// Lumen Radiance Cache for reflections on translucency
if (TranslucentBasePass_FinalProbeResolution > 0 && !bUseLumenFrontLayerReflection)
{
float ClipmapDitherRandom = InterleavedGradientNoise(MaterialParameters.SvPosition.xy, View.StateFrameIndexMod8);
LumenRadianceCacheCoverage = GetRadianceCacheCoverage(LWCHackToFloat(MaterialParameters.AbsoluteWorldPosition), RayDirection, ClipmapDitherRandom);
}
if (bUseLumenFrontLayerReflection)
{
SpecularIBL = SampleFrontLayerReflection(MaterialParameters.ViewBufferUV);
}
else if (LumenRadianceCacheCoverage.bValid)
{
float ConeHalfAngle = 0;
SpecularIBL = SampleRadianceCacheInterpolated(LumenRadianceCacheCoverage, LWCHackToFloat(MaterialParameters.AbsoluteWorldPosition), RayDirection, ConeHalfAngle);
}
// Fallback to unshadowed skylight if no valid Radiance Cache
else
#endif
{
uint NumLocalReflectionCaptures = 0;
uint DataStartIndex = 0;
#if REFLECTION_COMPOSITE_USE_BLENDED_REFLECTION_CAPTURES
#if INSTANCED_STEREO
BRANCH
if (EyeIndex == 0)
{
#endif
uint NumCulledEntryIndex = (ForwardLightData.NumGridCells + GridIndex) * NUM_CULLED_LIGHTS_GRID_STRIDE;
NumLocalReflectionCaptures = min(ForwardLightData.NumCulledLightsGrid[NumCulledEntryIndex + 0], ForwardLightData.NumReflectionCaptures);
DataStartIndex = ForwardLightData.NumCulledLightsGrid[NumCulledEntryIndex + 1];
#if INSTANCED_STEREO
}
else
{
uint NumCulledEntryIndex = (ForwardLightDataISR.NumGridCells + GridIndex) * NUM_CULLED_LIGHTS_GRID_STRIDE;
NumLocalReflectionCaptures = min(ForwardLightDataISR.NumCulledLightsGrid[NumCulledEntryIndex + 0], ForwardLightDataISR.NumReflectionCaptures);
DataStartIndex = ForwardLightDataISR.NumCulledLightsGrid[NumCulledEntryIndex + 1];
}
#endif
#endif
const bool bCompositeSkylight = true;
SpecularIBL = CompositeReflectionCapturesAndSkylightTWS(
1.0f,
MaterialParameters.WorldPosition_CamRelative,
RayDirection,
Roughness,
IndirectIrradiance,
1.0f,
0.0f,
NumLocalReflectionCaptures,
DataStartIndex,
SingleCaptureIndex,
bCompositeSkylight,
EyeIndex);
}
#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, ReflectionStruct.ReflectionCubemapSampler);
// 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, uint EyeIndex)
{
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, EyeIndex);
#if MATERIAL_USE_PREINTEGRATED_GF
SpecularColor = EnvBRDF(SpecularColor, Roughness, NoV);
#else
SpecularColor = EnvBRDFApprox(SpecularColor, Roughness, NoV);
#endif
return SpecularLighting * SpecularColor;
}
half3 GetImageBasedReflectionLighting(FMaterialPixelParameters MaterialParameters, half Roughness, half3 SpecularColor, half IndirectIrradiance, uint GridIndex, int SingleCaptureIndex)
{
return GetImageBasedReflectionLighting(MaterialParameters, Roughness, SpecularColor, IndirectIrradiance, GridIndex, SingleCaptureIndex, 0);
}