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

186 lines
6.1 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PlanarReflectionShaders.usf
=============================================================================*/
#include "Common.ush"
#include "DeferredShadingCommon.ush"
#include "PlanarReflectionShared.ush"
#include "SceneTextureParameters.ush"
#include "Substrate/Substrate.ush"
Texture2D SceneColorInputTexture;
SamplerState SceneColorInputSampler;
float4 SampleSceneColorInput(float2 UV)
{
return Texture2DSampleLevel(SceneColorInputTexture, SceneColorInputSampler, UV, 0);
}
float GaussianWeight1d(float u, float KernelRadius)
{
float TwoKernelRadiusSq = 2 * KernelRadius * KernelRadius;
return exp(-u * u / TwoKernelRadiusSq) / sqrt(PI * TwoKernelRadiusSq);
}
float InvPrefilterRoughnessDistance;
void VerticalSample(float2 InUV, float y, float KernelRadiusY, float CenterDistanceToPlane, float CenterReflectionDistance, bool bSearchUp, inout float4 AccumulatedColor, inout float AccumulatedWeight)
{
float2 SampleUV = float2(InUV.x, InUV.y + y * View.BufferSizeAndInvSize.w);
float SampleDepth = CalcSceneDepth(SampleUV);
float SampleReflectionDistance = max(SampleDepth - CenterDistanceToPlane, 0);
float ScatterKernelSize = max(KernelRadiusY * saturate(SampleReflectionDistance * InvPrefilterRoughnessDistance), 1);
float SampleWeight = GaussianWeight1d(y, ScatterKernelSize);
float ColorHalfTexelOffset = .5f * View.BufferSizeAndInvSize.w;
float YPosition = InUV.y * View.BufferSizeAndInvSize.y + y;
if (bSearchUp)
{
// Mask by depth, don't accept contribution from pixels much further than the mirror reflection depth
// This prevents reading from background areas that were revealed due to the clip plane
float DepthMask = SampleReflectionDistance < CenterReflectionDistance * 2;
SampleWeight *= DepthMask;
// Can't use bilinear filtering when we are depth masking
ColorHalfTexelOffset = 0;
float ViewportMask = YPosition > View.ViewRectMin.y + 0.5;
SampleWeight *= ViewportMask;
}
else
{
float ViewportMask = YPosition < View.ViewRectMin.y + View.ViewSizeAndInvSize.y - 0.5;
SampleWeight *= ViewportMask;
}
// Half texel offset for bilinear
float4 SampleColor = SampleSceneColorInput(SampleUV + float2(0, ColorHalfTexelOffset));
// SampleColor might contain NaNs when sampling out of the view rect, and multiplying by 0 won't help
AccumulatedColor += -min(-SampleColor, 0.0) * SampleWeight;
AccumulatedWeight += SampleWeight;
}
float KernelRadiusY;
void PrefilterPlanarReflectionPS(
float2 InUV : TEXCOORD0,
float3 ScreenVector : TEXCOORD1,
float4 SVPos : SV_POSITION,
out float4 OutColor : SV_Target0
)
{
float2 BufferUV = min(InUV, (View.ViewSizeAndInvSize.xy + View.ViewRectMin.xy - 0.5) * View.BufferSizeAndInvSize.zw);
#if ENABLE_PLANAR_REFLECTIONS_PREFILTER
BRANCH
if (KernelRadiusY > 0)
{
// Compute distance from camera origin to the camera vector intersection with the reflection plane
float CenterDistanceToPlane = -(dot(PrimaryView.TranslatedWorldCameraOrigin, PlanarReflectionStruct.ReflectionPlane.xyz) - PlanarReflectionStruct.ReflectionPlane.w) / dot(normalize(ScreenVector), PlanarReflectionStruct.ReflectionPlane.xyz);
// Note: reading from depth after the temporal AA pass
// This works without jittering because we disable temporal AA jittering for planar reflections
float CenterSceneDepth = CalcSceneDepth(BufferUV);
float CenterReflectionDistance = max(CenterSceneDepth - CenterDistanceToPlane, 0);
float4 AccumulatedColor = 0;
float AccumulatedWeight = 0;
// Two texels at a time with bilinear
{
LOOP
for (float y = 0; y < KernelRadiusY; y += 2)
{
VerticalSample(BufferUV, y, KernelRadiusY, CenterDistanceToPlane, CenterReflectionDistance, false, AccumulatedColor, AccumulatedWeight);
}
}
{
LOOP
for (float y = -2; y > -KernelRadiusY; y -= 2)
{
VerticalSample(BufferUV, y, KernelRadiusY, CenterDistanceToPlane, CenterReflectionDistance, true, AccumulatedColor, AccumulatedWeight);
}
}
AccumulatedColor = AccumulatedColor / max(AccumulatedWeight, .0001f);
// We cleared the planar reflection alpha to 1, and base pass writes 0
float ValidContentMask = 1 - AccumulatedColor.a;
OutColor = float4(AccumulatedColor.rgb, ValidContentMask);
}
else
#endif // ENABLE_PLANAR_REFLECTIONS_PREFILTER
{
float4 SceneColorValue = SampleSceneColorInput(BufferUV);
#if (ES3_1_PROFILE)
// when mesh draw command pipline is active mobile scene color contains depth in the alpha channel, any value > 1.0 has been written to.
if( SceneColorValue.a > 1.0)
{
SceneColorValue.a = 0;
}
#endif
// We cleared the planar reflection alpha to 1, and base pass writes 0
float ValidContentMask = 1 - SceneColorValue.a;
OutColor = float4(SceneColorValue.xyz, ValidContentMask);
}
}
#if SHADING_PATH_DEFERRED
void PlanarReflectionPS(
float2 BufferUV : TEXCOORD0,
float3 ScreenVector : TEXCOORD1,
float4 SVPos : SV_POSITION,
out float4 OutColor : SV_Target0
)
{
ResolvedView = ResolveView();
OutColor = 0;
#if SUBTRATE_GBUFFER_FORMAT==1
const FSubstrateTopLayerData TopLayerData = SubstrateUnpackTopLayerData(Substrate.TopLayerTexture.Load(uint3(SVPos.xy, 0)));
float DeviceZ = SampleDeviceZFromSceneTextures(BufferUV);
float SceneDepth = ConvertFromDeviceZ(DeviceZ);
float3 TranslatedWorldPosition = ResolvedView.TranslatedWorldCameraOrigin + ScreenVector * SceneDepth;
const float Roughness = TopLayerData.Roughness;
const float3 WorldNormal = TopLayerData.WorldNormal;
const bool bNoMaterial = !IsSubstrateMaterial(TopLayerData);
// Skip unlit pixels (skybox)
BRANCH
if (!bNoMaterial)
{
OutColor = ComputePlanarReflections(TranslatedWorldPosition, WorldNormal, Roughness);
OutColor.xyz *= View.PreExposure;
}
#else
FGBufferData GBuffer = GetGBufferDataFromSceneTextures(BufferUV);
float3 TranslatedWorldPosition = GetTranslatedWorldCameraPosFromView(SVPos.xy) + ScreenVector * GBuffer.Depth;
// Skip unlit pixels (skybox)
BRANCH
if (GBuffer.ShadingModelID != SHADINGMODELID_UNLIT)
{
OutColor = ComputePlanarReflections(TranslatedWorldPosition, GBuffer.WorldNormal, GBuffer.Roughness);
OutColor.xyz *= View.PreExposure;
}
#endif
}
#endif