Files
UnrealEngineUWP/Engine/Shaders/DistanceFieldLightingPost.usf
Ryan Vance 43e9a07ed7 Copying //UE4/Dev-VR to //UE4/Dev-Main (Source: //UE4/Dev-VR @ 3016398)
#lockdown nick.penwarden
#rb nobody

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2945508 on 2016/04/15 by Nick.Whiting

	Integrating fix for module loading for SteamVR, prevents crashing in race conditions

Change 2950385 on 2016/04/20 by Ryan.Vance

	We need to test if the hmd is enabled if it exists. Otherwise, this will return true even if we aren't rendering in stereo if there's an hmd plugin loaded.

Change 2955406 on 2016/04/25 by Chad.Taylor

	Factor scale into the motion controller component late update

Change 2956275 on 2016/04/26 by Nick.Whiting

	Initial integration of OSVR plugin support

	#pr 2097

Change 2964412 on 2016/05/03 by Chad.Taylor

	PSVR's GetControllerOrientationAndPosition now returns false if status is NOT_STARTED or CALIBRATING

Change 2964612 on 2016/05/03 by Ryan.Vance

	Copying //UE4/Dev-VR-InstancedStereo to Dev-VR-Minimal (//UE4/Dev-VR-Minimal)

Change 2985528 on 2016/05/20 by Ryan.Vance

	#jira UE-30715
	Keep from spamming the output log for every single shader compile when instanced stereo is enabled for a shader platform that doesn't support it.

Change 2986246 on 2016/05/22 by Chad.Taylor

	HMD late-update thread safety

Change 2998629 on 2016/06/02 by Ryan.Vance

	Post 4.12 Oculus plugin integration

Change 3000057 on 2016/06/03 by Ryan.Vance

	Updating serialize function for custom material nodes.
	The instanced stereo refactor moved Frame uniforms *back* to View (post 4.11). This should update only objects that went through the prior transformation from View *to* Frame.
	The serialize function was also being used to update Parameters.WorldPosition to Parameters.AbsoluteWorldPosition. This should still run as expected.

Change 3002187 on 2016/06/06 by Ryan.Vance

	Switching from ._m syntax to array syntax. The cross compiler chokes on the former.

Change 3004153 on 2016/06/07 by Chad.Taylor

	Enable PSVR on VS2015

	#jira UE-31202

Change 3009958 on 2016/06/10 by Ryan.Vance

	#jira UE-31922
	Velocity and depth pre-pass for dynamic instanced meshes with isr was only rendering in the left eye.
	Need to loop over the draw call the same way we do for the base pass.

Change 3011054 on 2016/06/13 by Chad.Taylor

	Merging "SteamVR Positional Late-Update" back into Dev-VR

Change 3013361 on 2016/06/14 by Ryan.Vance

	#jira UE-32022
	Fixing dbuffer decal integration errors.

[CL 3018810 by Ryan Vance in Main branch]
2016-06-17 20:25:37 -04:00

632 lines
25 KiB
Plaintext

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
DistanceFieldLightingPost.usf
=============================================================================*/
#include "Common.usf"
#include "DeferredShadingCommon.usf"
#include "DistanceFieldLightingShared.usf"
#include "DistanceFieldAOShared.usf"
// 1 / ((1 - FadeDistanceFraction) * AOMaxViewDistance)
float DistanceFadeScale;
float SelfOcclusionReplacement;
/** Normalizes the splatted surface cache values, packs depth in alpha. */
void AOCombinePS(
in float4 UVAndScreenPos : TEXCOORD0
,out float4 OutBentNormal : SV_Target0
#if SUPPORT_IRRADIANCE
,out float4 OutIrradiance : SV_Target1
#endif
)
{
#if SUPPORT_IRRADIANCE
OutIrradiance = 0;
#endif
float3 WorldNormal;
float SceneDepth;
bool bHasDistanceFieldRepresentation;
bool bHasHeightfieldRepresentation;
GetDownsampledGBuffer(UVAndScreenPos.xy, WorldNormal, SceneDepth, bHasDistanceFieldRepresentation, bHasHeightfieldRepresentation);
#define VISUALIZE_ACCUMULATED_WEIGHTS 0
#if VISUALIZE_ACCUMULATED_WEIGHTS
float3 BentNormalAO = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, UVAndScreenPos.xy, 0).aaa;
OutBentNormal = float4(BentNormalAO, SceneDepth);
#else
float4 BentNormalAccumulation = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, UVAndScreenPos.xy, 0);
if (BentNormalAccumulation.w > 0)
{
OutBentNormal.rgb = BentNormalAccumulation.xyz / BentNormalAccumulation.w;
OutBentNormal.a = SceneDepth;
if (!bHasDistanceFieldRepresentation)
{
// Pixels without a distance field representation interpolate AO instead of a bent normal
// Construct a bent normal from the interpolated AO
OutBentNormal.rgb = OutBentNormal.r * WorldNormal;
}
#if SUPPORT_IRRADIANCE
OutIrradiance.rgb = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, UVAndScreenPos.xy, 0).xyz / BentNormalAccumulation.w;
if (!bHasDistanceFieldRepresentation && !bHasHeightfieldRepresentation)
{
OutIrradiance.rgb *= SelfOcclusionReplacement;
}
#endif
}
else
{
OutBentNormal.rgb = float3(0, 0, .1f);
// Sign bit stores whether texel is valid
OutBentNormal.a = -SceneDepth;
}
float BentNormalLength = length(OutBentNormal.rgb);
// Fade to unoccluded in the distance
float FadeAlpha = saturate((AOMaxViewDistance - SceneDepth) * DistanceFadeScale);
float3 NormalizedBentNormal = OutBentNormal.rgb / max(BentNormalLength, .0001f);
OutBentNormal.rgb = NormalizedBentNormal * lerp(1, BentNormalLength, FadeAlpha);
// Clamp Nan's before they get to the filter
OutBentNormal.rgb = clamp(OutBentNormal.rgb, -1, 1);
FLATTEN
if (SceneDepth > AOMaxViewDistance)
{
// Mark as valid for the gap filling pass, so we don't get overwritten
OutBentNormal.a = abs(OutBentNormal.a);
}
#endif
}
float2 BentNormalAOTexelSize;
float MinDownsampleFactorToBaseLevel;
#define HALF_FILL_KERNEL_SIZE 2
/** Fills in texels with no splatted weight from screenspace neighbors. */
void FillGapsPS(
in float4 UVAndScreenPos : TEXCOORD0
,out float4 OutBentNormal : SV_Target0
#if SUPPORT_IRRADIANCE
,out float4 OutIrradiance : SV_Target1
#endif
)
{
float4 CenterValue = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, UVAndScreenPos.xy, 0);
float SceneDepth = abs(CenterValue.w);
#if HIGH_QUALITY_FILL_GAPS
float4 Accumulation = 0;
float3 IrradianceAccumulation = 0;
float Length = 0;
// Pixels without a valid interpolation need to find valid neighbors
// Pixels with a valid interpolation just get a slight spatial filter so don't rely on a large kernel
float HalfKernelSize = CenterValue.w < 0 ? HALF_FILL_KERNEL_SIZE : 1;
for (float y = -HalfKernelSize; y <= HalfKernelSize; y++)
{
float WeightY = exp2(-abs(y * 10.0f / HALF_FILL_KERNEL_SIZE));
for (float x = -HalfKernelSize; x <= HalfKernelSize; x++)
{
float2 UVOffset = BentNormalAOTexelSize * float2(x, y);
float4 TextureValue = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, UVAndScreenPos.xy + UVOffset, 0);
float SampleSceneDepth = abs(TextureValue.w);
float ValidMask = TextureValue.w > 0;
// Don't let depth weight go to 0 with huge depth differences
float DepthWeight = max(exp2(-abs(SceneDepth - SampleSceneDepth) * .01f), .001f);
float2 Weight2D = float2(exp2(-abs(x * 10.0f / HALF_FILL_KERNEL_SIZE)), WeightY);
float ScreenSpaceSpatialWeight = max(Weight2D.x, Weight2D.y);
float Weight = ValidMask * ScreenSpaceSpatialWeight * DepthWeight;
// Track length separately
Length += length(TextureValue.rgb) * Weight;
Accumulation.rgb += TextureValue.rgb * Weight;
Accumulation.a += Weight;
#if SUPPORT_IRRADIANCE
float3 Irradiance = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, UVAndScreenPos.xy + UVOffset, 0).xyz;
IrradianceAccumulation += Irradiance * Weight;
#endif
}
}
float InvSafeWeight = 1.0f / max(Accumulation.a, .00001f);
float AverageLength = Length * InvSafeWeight;
float3 AverageBentNormal = Accumulation.rgb * InvSafeWeight;
float BentNormalLength = length(AverageBentNormal);
float3 AverageIrradiance = IrradianceAccumulation * InvSafeWeight;
if (BentNormalLength < AverageLength && BentNormalLength > 0)
{
// Fixup normal shortening due to weighted average of vectors
AverageBentNormal = AverageBentNormal / BentNormalLength * AverageLength;
}
#else
float3 AverageBentNormal;
float3 AverageIrradiance;
if (CenterValue.w < 0)
{
float2 LowResBufferSize = 1.0f / (MinDownsampleFactorToBaseLevel * BentNormalAOTexelSize);
float2 LowResTexelSize = BentNormalAOTexelSize * MinDownsampleFactorToBaseLevel;
float2 Corner00UV = floor(UVAndScreenPos.xy * LowResBufferSize) / LowResBufferSize + .5f * BentNormalAOTexelSize;
float2 BilinearWeights = (UVAndScreenPos.xy - Corner00UV) * LowResBufferSize;
float4 TextureValues00 = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, Corner00UV, 0);
float4 TextureValues10 = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, Corner00UV + float2(LowResTexelSize.x, 0), 0);
float4 TextureValues01 = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, Corner00UV + float2(0, LowResTexelSize.y), 0);
float4 TextureValues11 = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, Corner00UV + LowResTexelSize, 0);
float4 CornerWeights = float4(
(1 - BilinearWeights.y) * (1 - BilinearWeights.x),
(1 - BilinearWeights.y) * BilinearWeights.x,
BilinearWeights.y * (1 - BilinearWeights.x),
BilinearWeights.y * BilinearWeights.x);
float Epsilon = .0001f;
float4 CornerDepths = abs(float4(TextureValues00.w, TextureValues10.w, TextureValues01.w, TextureValues11.w));
float4 DepthWeights = min(10.0f / (abs(CornerDepths - SceneDepth.xxxx) + Epsilon), 1);
float4 FinalWeights = CornerWeights * DepthWeights;
float InvSafeWeight = 1.0f / max(dot(FinalWeights, 1), .00001f);
AverageBentNormal =
(FinalWeights.x * TextureValues00.xyz
+ FinalWeights.y * TextureValues10.xyz
+ FinalWeights.z * TextureValues01.xyz
+ FinalWeights.w * TextureValues11.xyz)
* InvSafeWeight;
#if SUPPORT_IRRADIANCE
float4 IrradianceValues00 = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, Corner00UV, 0);
float4 IrradianceValues10 = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, Corner00UV + float2(LowResTexelSize.x, 0), 0);
float4 IrradianceValues01 = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, Corner00UV + float2(0, LowResTexelSize.y), 0);
float4 IrradianceValues11 = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, Corner00UV + LowResTexelSize, 0);
AverageIrradiance =
(FinalWeights.x * IrradianceValues00.xyz
+ FinalWeights.y * IrradianceValues10.xyz
+ FinalWeights.z * IrradianceValues01.xyz
+ FinalWeights.w * IrradianceValues11.xyz)
* InvSafeWeight;
#endif
}
else
{
AverageBentNormal = CenterValue.xyz;
#if SUPPORT_IRRADIANCE
AverageIrradiance = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, UVAndScreenPos.xy, 0).xyz;
#endif
}
#endif
OutBentNormal = float4(AverageBentNormal, CenterValue.w);
#if SUPPORT_IRRADIANCE
OutIrradiance = float4(AverageIrradiance, 0);
#endif
}
Texture2D BentNormalHistoryTexture;
SamplerState BentNormalHistorySampler;
Texture2D IrradianceHistoryTexture;
SamplerState IrradianceHistorySampler;
float HistoryWeight;
float HistoryDistanceThreshold;
float UseHistoryFilter;
Texture2D VelocityTexture;
SamplerState VelocityTextureSampler;
float ComputeHistoryWeightBasedOnPosition(float2 ScreenPosition, float SceneDepth, float2 OldScreenPosition, float HistoryDepth)
{
float3 WorldPosition;
float3 PrevWorldPosition;
{
float4 HomogeneousWorldPosition = mul(float4(ScreenPosition * SceneDepth, SceneDepth, 1), View.ScreenToWorld);
WorldPosition = HomogeneousWorldPosition.xyz / HomogeneousWorldPosition.w;
float4 PrevPositionTranslatedWorld = mul(float4(OldScreenPosition * HistoryDepth, HistoryDepth, 1), View.PrevScreenToTranslatedWorld);
PrevPositionTranslatedWorld.xyz /= PrevPositionTranslatedWorld.w;
PrevWorldPosition = PrevPositionTranslatedWorld.xyz - View.PrevPreViewTranslation;
}
float DistanceToHistoryValue = length(PrevWorldPosition - WorldPosition);
float RelativeHistoryDistanceThreshold = HistoryDistanceThreshold / 1000.0f;
return DistanceToHistoryValue / SceneDepth > RelativeHistoryDistanceThreshold ? 0.0f : 1.0f;
}
// Doesn't work yet
#define HIGH_QUALITY_HISTORY_REJECTION 0
/** Reproject the occlusion history. */
void UpdateHistoryPS(
in float4 UVAndScreenPos : TEXCOORD0
,out float4 OutBentNormal : SV_Target0
#if SUPPORT_IRRADIANCE
,out float4 OutIrradiance : SV_Target1
#endif
)
{
// Distance field AO was computed at 0,0 regardless of viewrect min
float2 DistanceFieldUVs = UVAndScreenPos.xy - View.ViewRectMin.xy * View.BufferSizeAndInvSize.zw;
float4 NewValue = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, DistanceFieldUVs, 0);
float SceneDepth = abs(NewValue.w);
float OutputDepthSign = 1.0f;
float2 OldUV;
{
float3 xyd;
xyd.xy = UVAndScreenPos.zw * float2(0.5, -0.5) + 0.5;
xyd.z = ConvertToDeviceZ(SceneDepth);
float scaleM = 1.0 / (dot(xyd, CameraMotion.Value[0].xyz) + CameraMotion.Value[0].w);
float2 MotionVector;
// Unwound vector-matrix transform with special care for precision taken from Temporal AA
MotionVector.x = ((xyd.x * ((CameraMotion.Value[1].x * xyd.y) + (CameraMotion.Value[1].y * xyd.z) + CameraMotion.Value[1].z)) + (CameraMotion.Value[1].w * xyd.y) + (CameraMotion.Value[2].x * xyd.x * xyd.x) + (CameraMotion.Value[2].y * xyd.z) + CameraMotion.Value[2].z) * scaleM;
MotionVector.y = ((xyd.y * ((CameraMotion.Value[3].x * xyd.x) + (CameraMotion.Value[3].y * xyd.z) + CameraMotion.Value[3].z)) + (CameraMotion.Value[3].w * xyd.x) + (CameraMotion.Value[4].x * xyd.y * xyd.y) + (CameraMotion.Value[4].y * xyd.z) + CameraMotion.Value[4].z) * scaleM;
// Note: have to sample from one of the high res texels exactly to avoid filtering of the velocity buffer
// 0 is stored where camera velocity should be used, which must not be filtered with valid object velocities
float2 FullResTexel = UVAndScreenPos.xy - .5f * View.BufferSizeAndInvSize.zw;
float2 VelocityN = Texture2DSampleLevel(VelocityTexture, VelocityTextureSampler, FullResTexel, 0).xy;
if (VelocityN.x > 0)
{
// Use the per-pixel velocity vector where valid, this handles object, bone and WPO movement
MotionVector = float2(-.5f, .5f) * DecodeVelocityFromTexture(VelocityN);
}
OldUV = UVAndScreenPos.xy + MotionVector / float2(0.5, -0.5) * View.ScreenPositionScaleBias.xy;
}
float2 OldScreenPosition = (OldUV.xy - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
float2 OldDistanceFieldUVs = OldUV.xy - View.ViewRectMin.xy * View.BufferSizeAndInvSize.zw;
float2 MinUV = View.ViewRectMin.xy * View.BufferSizeAndInvSize.zw;
// Pull in the max UV to exclude the region which will read outside the viewport due to bilinear filtering
float2 MaxUV = MinUV + (View.ViewSizeAndInvSize.xy - 1 * DOWNSAMPLE_FACTOR) * View.BufferSizeAndInvSize.zw;
float EffectiveHistoryWeight = HistoryWeight;
FLATTEN
if (any(OldUV > MaxUV) || any(OldUV < MinUV))
{
EffectiveHistoryWeight = 0;
}
#define MANUAL_HISTORY_RESAMPLE HIGH_QUALITY_HISTORY_REJECTION
#if MANUAL_HISTORY_RESAMPLE
float3 Unused;
float Unused2;
//@todo - pack into BentNormalAOTexture
bool bHasDistanceFieldRepresentation;
bool bUnused;
GetDownsampledGBuffer(DistanceFieldUVs, Unused, Unused2, bHasDistanceFieldRepresentation, bUnused);
OutputDepthSign = bHasDistanceFieldRepresentation ? 1.0f : -1.0f;
float2 HistoryTexelSize = View.BufferSizeAndInvSize.zw * DOWNSAMPLE_FACTOR;
float2 OldDistanceFieldUVsCorner00 = HistoryTexelSize * (floor(OldDistanceFieldUVs / HistoryTexelSize) + .5f);
float2 OldDistanceFieldUVsCorner01 = OldDistanceFieldUVsCorner00 + float2(0, HistoryTexelSize.y);
float2 OldDistanceFieldUVsCorner11 = OldDistanceFieldUVsCorner00 + HistoryTexelSize;
float2 OldDistanceFieldUVsCorner10 = OldDistanceFieldUVsCorner00 + float2(HistoryTexelSize.x, 0);
float4 Corner00Value = Texture2DSampleLevel(BentNormalHistoryTexture, BentNormalHistorySampler, OldDistanceFieldUVsCorner00, 0);
float4 Corner01Value = Texture2DSampleLevel(BentNormalHistoryTexture, BentNormalHistorySampler, OldDistanceFieldUVsCorner01, 0);
float4 Corner11Value = Texture2DSampleLevel(BentNormalHistoryTexture, BentNormalHistorySampler, OldDistanceFieldUVsCorner11, 0);
float4 Corner10Value = Texture2DSampleLevel(BentNormalHistoryTexture, BentNormalHistorySampler, OldDistanceFieldUVsCorner10, 0);
float2 LerpWeights = OldDistanceFieldUVs - OldDistanceFieldUVsCorner00;
#define COMPUTE_SEPARATE_POSITION_WEIGHTS 1
#if COMPUTE_SEPARATE_POSITION_WEIGHTS
float PositionWeight00 = ComputeHistoryWeightBasedOnPosition(UVAndScreenPos.zw, SceneDepth, OldScreenPosition, abs(Corner00Value.w));
float PositionWeight01 = ComputeHistoryWeightBasedOnPosition(UVAndScreenPos.zw, SceneDepth, OldScreenPosition, abs(Corner01Value.w));
float PositionWeight11 = ComputeHistoryWeightBasedOnPosition(UVAndScreenPos.zw, SceneDepth, OldScreenPosition, abs(Corner11Value.w));
float PositionWeight10 = ComputeHistoryWeightBasedOnPosition(UVAndScreenPos.zw, SceneDepth, OldScreenPosition, abs(Corner10Value.w));
float ClassificationWeight00 = (Corner00Value.w > 0 == bHasDistanceFieldRepresentation) ? PositionWeight00 : 0.0f;
float ClassificationWeight01 = (Corner01Value.w > 0 == bHasDistanceFieldRepresentation) ? PositionWeight01 : 0.0f;
float ClassificationWeight11 = (Corner11Value.w > 0 == bHasDistanceFieldRepresentation) ? PositionWeight11 : 0.0f;
float ClassificationWeight10 = (Corner10Value.w > 0 == bHasDistanceFieldRepresentation) ? PositionWeight10 : 0.0f;
float FinalWeight00 = (1 - LerpWeights.y) * (1 - LerpWeights.x) * ClassificationWeight00;
float FinalWeight01 = (LerpWeights.y) * (1 - LerpWeights.x) * ClassificationWeight01;
float FinalWeight11 = (LerpWeights.y) * (LerpWeights.x) * ClassificationWeight11;
float FinalWeight10 = (1 - LerpWeights.y) * (LerpWeights.x) * ClassificationWeight10;
float TotalWeight = FinalWeight00 + FinalWeight01 + FinalWeight11 + FinalWeight10;
float3 HistoryValue = (Corner00Value.xyz * FinalWeight00
+ Corner01Value.xyz * FinalWeight01
+ Corner11Value.xyz * FinalWeight11
+ Corner10Value.xyz * FinalWeight10) / max(TotalWeight, .00001f);
EffectiveHistoryWeight *= TotalWeight > 0 ? 1.0f : 0.0f;
#else
// Filter encoded depth path, for testing
float4 VerticalLerp0 = lerp(Corner00Value, Corner01Value, LerpWeights.y);
float4 VerticalLerp1 = lerp(Corner10Value, Corner11Value, LerpWeights.y);
float4 HistoryValue = lerp(VerticalLerp0, VerticalLerp1, LerpWeights.x);
float PositionWeight = ComputeHistoryWeightBasedOnPosition(UVAndScreenPos.zw, SceneDepth, OldScreenPosition, abs(HistoryValue.w));
EffectiveHistoryWeight *= PositionWeight;
#endif
OutBentNormal.rgb = lerp(NewValue.rgb, HistoryValue.rgb, EffectiveHistoryWeight);
#else
// Fast path that uses hardware interpolation
// This fails to reject based on depth consistently because the depth has been filtered
float4 HistoryValue = Texture2DSampleLevel(BentNormalHistoryTexture, BentNormalHistorySampler, OldDistanceFieldUVs, 0);
float PositionWeight = ComputeHistoryWeightBasedOnPosition(UVAndScreenPos.zw, SceneDepth, OldScreenPosition, abs(HistoryValue.w));
EffectiveHistoryWeight *= PositionWeight;
OutBentNormal.rgb = lerp(NewValue.rgb, HistoryValue.rgb, EffectiveHistoryWeight);
#endif
#if SUPPORT_IRRADIANCE
float3 NewIrradiance = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, DistanceFieldUVs, 0).xyz;
float3 HistoryIrradianceValue = Texture2DSampleLevel(IrradianceHistoryTexture, IrradianceHistorySampler, OldDistanceFieldUVs, 0).xyz;
OutIrradiance = float4(lerp(NewIrradiance, HistoryIrradianceValue, EffectiveHistoryWeight), 0);
#endif
OutBentNormal.rgb = isnan(OutBentNormal.rgb) ? 0 : OutBentNormal.rgb;
OutBentNormal.a = abs(NewValue.a);
FLATTEN
if (UseHistoryFilter > 0)
{
// Sign bit of alpha stores whether the history was rejected or not, to be read by the history filter pass
OutBentNormal.a *= EffectiveHistoryWeight > 0 ? 1 : -1;
}
else
{
OutBentNormal.a *= OutputDepthSign;
}
}
#define HALF_HISTORY_FILL_KERNEL_SIZE 2
/** Seeds newly rejected history values (which are sources of temporal instability) with the results of a spatial search from stable history values */
void FilterHistoryPS(
in float4 UVAndScreenPos : TEXCOORD0
,out float4 OutBentNormal : SV_Target0
#if SUPPORT_IRRADIANCE
,out float4 OutIrradiance : SV_Target1
#endif
)
{
float4 HistoryValue = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, UVAndScreenPos.xy, 0);
#if SUPPORT_IRRADIANCE
float3 IrradianceValue = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, UVAndScreenPos.xy, 0).xyz;
#endif
// Only do the spatial search for pixels who discarded their history value
if (HistoryValue.w < 0)
{
float SceneDepth = abs(HistoryValue.w);
float4 Accumulation = 0;
float3 IrradianceAccumulation = 0;
for (float y = -HALF_HISTORY_FILL_KERNEL_SIZE; y <= HALF_HISTORY_FILL_KERNEL_SIZE; y++)
{
for (float x = -HALF_HISTORY_FILL_KERNEL_SIZE; x <= HALF_HISTORY_FILL_KERNEL_SIZE; x++)
{
float2 UVOffset = BentNormalAOTexelSize * float2(x, y);
float4 TextureValue = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, UVAndScreenPos.xy + UVOffset, 0);
float SampleSceneDepth = abs(TextureValue.w);
float ValidMask = TextureValue.w > 0;
// Weight by depth to avoid pulling in values of a foreground object
// This is a careful tradeoff between ghosting behind panning foreground objects and successful spatial searches to reduce flickering
float DepthWeight = exp2(-1000 * abs(SceneDepth - SampleSceneDepth) / SceneDepth);
float2 Weight2D = exp2(-abs(float2(x, y) * 10.0f / HALF_HISTORY_FILL_KERNEL_SIZE));
float ScreenSpaceSpatialWeight = max(Weight2D.x, Weight2D.y);
float Weight = ValidMask * ScreenSpaceSpatialWeight * DepthWeight;
Accumulation.rgb += TextureValue.rgb * Weight;
Accumulation.a += Weight;
#if SUPPORT_IRRADIANCE
float3 Irradiance = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, UVAndScreenPos.xy + UVOffset, 0).xyz;
IrradianceAccumulation += Irradiance * Weight;
#endif
}
}
// Only change the history value if the spatial search turned up something applicable
if (Accumulation.a > 0)
{
float InvWeight = 1.0f / Accumulation.a;
// Construct the history value as if the spatial search result was the previous history,
// And the AO we just computed this frame was the new value
HistoryValue.xyz = lerp(HistoryValue.xyz, Accumulation.xyz * InvWeight, HistoryWeight);
#if SUPPORT_IRRADIANCE
IrradianceValue = lerp(IrradianceValue, IrradianceAccumulation * InvWeight, HistoryWeight);
#endif
}
}
OutBentNormal = HistoryValue;
// Remove sign bit so future reprojection interpolation isn't affected
OutBentNormal.w = abs(OutBentNormal.w);
#if HIGH_QUALITY_HISTORY_REJECTION
float3 Unused;
float Unused2;
//@todo - pack into BentNormalAOTexture
bool bHasDistanceFieldRepresentation;
bool bUnused;
GetDownsampledGBuffer(UVAndScreenPos.xy, Unused, Unused2, bHasDistanceFieldRepresentation, bUnused);
// Store whether the pixel has a distance field representation for next frame
// This can only be supported with manual history filtering
OutBentNormal.w *= bHasDistanceFieldRepresentation ? 1.0f : -1.0f;
#endif
#if SUPPORT_IRRADIANCE
OutIrradiance = float4(IrradianceValue, 0);
#endif
}
#if MODULATE_SCENE_COLOR
#define IRRADIANCE_TARGET SV_Target2
#else
#define IRRADIANCE_TARGET SV_Target1
#endif
Texture2D SpecularOcclusionTexture;
SamplerState SpecularOcclusionSampler;
float MinIndirectDiffuseOcclusion;
/** Upsamples the AO results to full resolution using a bilateral filter. */
void AOUpsamplePS(
in float4 UVAndScreenPos : TEXCOORD0
, out float4 OutBentNormal : SV_Target0
#if MODULATE_SCENE_COLOR
, out float4 OutSceneColorModulate : SV_Target1
#endif
#if OUTPUT_BENT_NORMAL && SUPPORT_IRRADIANCE
, out float4 OutIrradiance : IRRADIANCE_TARGET
#endif
)
{
// Distance field AO was computed at 0,0 regardless of viewrect min
float2 DistanceFieldUVs = UVAndScreenPos.xy - View.ViewRectMin.xy * View.BufferSizeAndInvSize.zw;
float3 Irradiance = 0;
float SpecularOcclusion = 1;
#define BILATERAL_UPSAMPLE 1
#if BILATERAL_UPSAMPLE
float2 LowResBufferSize = floor(View.RenderTargetSize / DOWNSAMPLE_FACTOR);
float2 LowResTexelSize = 1.0f / LowResBufferSize;
float2 Corner00UV = floor(DistanceFieldUVs * LowResBufferSize - .5f) / LowResBufferSize + .5f * LowResTexelSize;
float2 BilinearWeights = (DistanceFieldUVs - Corner00UV) * LowResBufferSize;
float4 TextureValues00 = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, Corner00UV, 0);
float4 TextureValues10 = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, Corner00UV + float2(LowResTexelSize.x, 0), 0);
float4 TextureValues01 = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, Corner00UV + float2(0, LowResTexelSize.y), 0);
float4 TextureValues11 = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, Corner00UV + LowResTexelSize, 0);
float4 CornerWeights = float4(
(1 - BilinearWeights.y) * (1 - BilinearWeights.x),
(1 - BilinearWeights.y) * BilinearWeights.x,
BilinearWeights.y * (1 - BilinearWeights.x),
BilinearWeights.y * BilinearWeights.x);
float Epsilon = .0001f;
float4 CornerDepths = abs(float4(TextureValues00.w, TextureValues10.w, TextureValues01.w, TextureValues11.w));
float SceneDepth = CalcSceneDepth(UVAndScreenPos.xy);
float4 DepthWeights = 1.0f / (abs(CornerDepths - SceneDepth.xxxx) + Epsilon);
float4 FinalWeights = CornerWeights * DepthWeights;
float InvWeight = 1.0f / dot(FinalWeights, 1);
float3 InterpolatedResult =
(FinalWeights.x * TextureValues00.xyz
+ FinalWeights.y * TextureValues10.xyz
+ FinalWeights.z * TextureValues01.xyz
+ FinalWeights.w * TextureValues11.xyz)
* InvWeight;
float3 BentNormal = InterpolatedResult.xyz;
#if SUPPORT_IRRADIANCE
float4 IrradianceValues00 = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, Corner00UV, 0);
float4 IrradianceValues10 = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, Corner00UV + float2(LowResTexelSize.x, 0), 0);
float4 IrradianceValues01 = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, Corner00UV + float2(0, LowResTexelSize.y), 0);
float4 IrradianceValues11 = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, Corner00UV + LowResTexelSize, 0);
Irradiance =
(FinalWeights.x * IrradianceValues00.xyz
+ FinalWeights.y * IrradianceValues10.xyz
+ FinalWeights.z * IrradianceValues01.xyz
+ FinalWeights.w * IrradianceValues11.xyz)
* InvWeight;
#endif
#if SUPPORT_SPECULAR_OCCLUSION
float SpecularOcclusionValues00 = Texture2DSampleLevel(SpecularOcclusionTexture, SpecularOcclusionSampler, Corner00UV, 0).x;
float SpecularOcclusionValues10 = Texture2DSampleLevel(SpecularOcclusionTexture, SpecularOcclusionSampler, Corner00UV + float2(LowResTexelSize.x, 0), 0).x;
float SpecularOcclusionValues01 = Texture2DSampleLevel(SpecularOcclusionTexture, SpecularOcclusionSampler, Corner00UV + float2(0, LowResTexelSize.y), 0).x;
float SpecularOcclusionValues11 = Texture2DSampleLevel(SpecularOcclusionTexture, SpecularOcclusionSampler, Corner00UV + LowResTexelSize, 0).x;
SpecularOcclusion =
(FinalWeights.x * SpecularOcclusionValues00
+ FinalWeights.y * SpecularOcclusionValues10
+ FinalWeights.z * SpecularOcclusionValues01
+ FinalWeights.w * SpecularOcclusionValues11)
* InvWeight;
#endif
#else
float3 BentNormal = Texture2DSampleLevel(BentNormalAOTexture, BentNormalAOSampler, DistanceFieldUVs, 0).xyz;
#if SUPPORT_IRRADIANCE
Irradiance = Texture2DSampleLevel(IrradianceTexture, IrradianceSampler, DistanceFieldUVs, 0).xyz;
#endif
#if SUPPORT_SPECULAR_OCCLUSION
SpecularOcclusion = Texture2DSampleLevel(SpecularOcclusionTexture, SpecularOcclusionSampler, DistanceFieldUVs, 0).x;
#endif
#endif
#if MODULATE_SCENE_COLOR
float Visibility = lerp(length(BentNormal), 1, MinIndirectDiffuseOcclusion);
OutSceneColorModulate = Visibility;
#endif
#if OUTPUT_AO
OutBentNormal = float4(length(BentNormal).xxx, 1);
//OutBentNormal = float4(BentNormal, 1);
#else
#if OUTPUT_BENT_NORMAL
OutBentNormal = float4(BentNormal, SpecularOcclusion);
#if SUPPORT_IRRADIANCE
OutIrradiance = float4(Irradiance, 1);
#endif
#else
// Output to RT0 to visualize Irradiance or BentNormal
#if SUPPORT_IRRADIANCE
OutBentNormal = float4(Irradiance, 1);
#endif
#endif
#endif
}