Files
UnrealEngineUWP/Engine/Shaders/PostProcessDOF.usf
2014-03-14 14:13:41 -04:00

220 lines
7.7 KiB
Plaintext

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PostProcessDOF.usf: PostProcessing Depth of Field
=============================================================================*/
#include "Common.usf"
#include "PostProcessCommon.usf"
#include "DeferredShadingCommon.usf" // FGBufferData
#include "DepthOfFieldCommon.usf"
float4 GetSceneDepthQuad(float2 UV)
{
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
// using Gather: xyzw in counter clockwise order starting with the sample to the lower left of the queried location
float4 DeviceZ = SceneDepthTextureNonMS.Gather(BilinearTextureSampler0, UV);
return float4(
ConvertFromDeviceZ(DeviceZ.x),
ConvertFromDeviceZ(DeviceZ.y),
ConvertFromDeviceZ(DeviceZ.z),
ConvertFromDeviceZ(DeviceZ.w)
);
#elif FEATURE_LEVEL == FEATURE_LEVEL_SM4
float2 TexelScale = 0.5f * PostprocessInput1Size.zw;
// @todo Optimize
// Manually sample neighbouring texels in counter clockwise order starting with the sample to the lower left of the queried location.
return float4(
CalcSceneDepth(UV + (float2(-1, 1) * TexelScale)),
CalcSceneDepth(UV + (float2( 1, 1) * TexelScale)),
CalcSceneDepth(UV + (float2( 1,-1) * TexelScale)),
CalcSceneDepth(UV + (float2(-1,-1) * TexelScale))
);
#endif // FEATURE_LEVEL
}
// todo move to central place
float ComputeDOFNearFocalMask(float SceneDepth)
{
float NearFocalPlane = View.DepthOfFieldFocalDistance;
return saturate((NearFocalPlane - SceneDepth) / View.DepthOfFieldNearTransitionRegion);
}
// todo move to central place
float ComputeDOFFarFocalMask(float SceneDepth)
{
float FarFocalPlane = View.DepthOfFieldFocalDistance + View.DepthOfFieldFocalRegion;
return saturate((SceneDepth - FarFocalPlane) / View.DepthOfFieldFarTransitionRegion);
}
// .x:far, .y:near
float2 ComputeDOFFocalMask(float SceneDepth, float SkyWithoutHorizonMask)
{
float2 Ret = float2(ComputeDOFFarFocalMask(SceneDepth), ComputeDOFNearFocalMask(SceneDepth));
float SkyFocusDistance = DepthOfFieldParams[0].x;
// The skybox should not be faded out, expect in the horizon, this can be optimized
if(SceneDepth > SkyFocusDistance)
{
Ret.x = lerp(Ret.x, 0, SkyWithoutHorizonMask);
}
return Ret;
}
// pixel shader entry point
void SetupPS(
float4 UVAndScreenPos : TEXCOORD0
, out float4 OutColor0 : SV_Target0
#if ENABLE_NEAR_BLUR
, out float4 OutColor1 : SV_Target1
#endif
)
{
float2 UV = UVAndScreenPos.xy;
float2 Offset = 0.5f * PostprocessInput0Size.zw;
float MaskDistance = View.DepthOfFieldFocalDistance + View.DepthOfFieldFocalRegion * 0.5f;
float4 DepthQuad = GetSceneDepthQuad(UV);
#if ENABLE_NEAR_BLUR == 0
// We aren't writing out to the second render target, so we'll just make a dummy value here which
// doesn't end up going anywhere. Then, the source code can stay neat and tidy, while the compiler can still
// strip it out
float4 OutColor1;
#endif
OutColor0 = 0;
OutColor1 = 0;
float2 Mask;
float4 Sample;
// for each sample of the full res input image
// we compute the mask (front of back layer)
// and put into MRT0 or MRT1
// screen position in [-1, 1] screen space
float2 ScreenSpacePos = UVAndScreenPos.zw;
// can be optimized, needed to not blur the skybox
float3 ScreenVector = normalize(mul(float4(ScreenSpacePos, 1, 0), View.ScreenToWorld).xyz);
float SkyWithoutHorizonMask = saturate(ScreenVector.z * 3.0f);
Mask = ComputeDOFFocalMask(DepthQuad.x, SkyWithoutHorizonMask);
Sample = float4(Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, UV + Offset * float2(-1, 1), 0).rgb, 1);
OutColor0 += Sample * Mask.x;
OutColor1 += Sample * Mask.y;
Mask = ComputeDOFFocalMask(DepthQuad.y, SkyWithoutHorizonMask);
Sample = float4(Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV + Offset * float2(1, 1)).rgb, 1);
OutColor0 += Sample * Mask.x;
OutColor1 += Sample * Mask.y;
Mask = ComputeDOFFocalMask(DepthQuad.z, SkyWithoutHorizonMask);
Sample = float4(Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV + Offset * float2(1, -1)).rgb, 1);
OutColor0 += Sample * Mask.x;
OutColor1 += Sample * Mask.y;
Mask = ComputeDOFFocalMask(DepthQuad.w, SkyWithoutHorizonMask);
Sample = float4(Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV + Offset * float2(-1, -1)).rgb, 1);
OutColor0 += Sample * Mask.x;
OutColor1 += Sample * Mask.y;
// we average 4 samples
OutColor0 /= 4;
OutColor1 /= 4;
// OutColor0.rgb *= float3(1,0,0);
// OutColor1.rgb *= float3(0,1,0);
}
float4 DepthOfFieldUVLimit;
// pixel shader to combine the full res scene and the blurred images behind and in front of the the focal plane
void MainRecombinePS(
in float4 UVAndScreenPos : TEXCOORD0,
out float4 OutColor : SV_Target0
)
{
// SceneColor in full res
float2 PixelPosCenter = UVAndScreenPos.zw * ScreenPosToPixel.xy + ScreenPosToPixel.zw + 0.5f;
float2 FullResUV = PixelPosCenter * PostprocessInput0Size.zw;
// DOF in half res
// float2 ViewportUV = FullResUV * float2(1, DepthOfFieldParams[1].z);// - 0.5 * PostprocessInput1Size.zw;
// float2 ViewportUV = (PixelPos * 0.5f + 0.5f) * PostprocessInput1Size.zw;
float2 ViewportUV = UVAndScreenPos.xy;
// Clamp UV to avoid pulling bad data.
ViewportUV.x = clamp(ViewportUV.x, DepthOfFieldUVLimit.x, DepthOfFieldUVLimit.z);
ViewportUV.y = clamp(ViewportUV.y, DepthOfFieldUVLimit.y, DepthOfFieldUVLimit.w);
float4 SceneColorAndDepth = float4(Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, FullResUV).rgb, CalcSceneDepth(FullResUV));
float3 UnfocusedSceneColor = SceneColorAndDepth.rgb;
// behind focal plane
float4 DOFAccumLayer1 = Texture2DSample(PostprocessInput1, PostprocessInput1Sampler, ViewportUV);
#if ENABLE_NEAR_BLUR
float4 DOFAccumLayer3 = Texture2DSample(PostprocessInput2, PostprocessInput2Sampler, ViewportUV);
#else
// I'm presuming all that matters here is the W==0 bit to mask out this value
// TODO: Should check that compiler is doing a good job of removing the usages of this
// from the rest of the code. It has no reason not to be able to do so...
float4 DOFAccumLayer3 = float4(0,0,0,0);
#endif
float Layer1Mask = DOFAccumLayer1.a;
float Layer2Mask = 1.0f - ComputeDOFFarFocalMask(SceneColorAndDepth.a);
// float Layer2Mask = 1.0f - DOFAccumLayer1.a;
float Layer3Mask = DOFAccumLayer3.a;
float PerPixelNearMask = ComputeDOFNearFocalMask(SceneColorAndDepth.a);
// 3 layers
float Div0Bias = 0.0001f;
// RGB color, A how much the full resolution showes through
float3 LayerMerger = 0;
// Layer 1: half res background
LayerMerger = (UnfocusedSceneColor * Div0Bias + DOFAccumLayer1.rgb) / (DOFAccumLayer1.a + Div0Bias);
// Needed to cope with the skybox not being blurred, the tweak value
// avoids having a discontinuity between blurry far objects and the skybox
// and is choosen to not produce too much blobby looking out of focus rendering.
float Blend = DOFAccumLayer1.a;
// Magic function to transform alpha into smooth blend function against in-focus skybox.
Blend = sqrt(Blend);
Blend = sqrt(Blend);
Blend = Blend * Blend * (3.0 - 2.0 * Blend);
LayerMerger = lerp(UnfocusedSceneColor, LayerMerger, Blend);
// Layer 2: then we add the focused scene to fill the empty areas
float Smash = 0.25;
Layer2Mask = saturate((Layer2Mask - (1.0 - Smash)) * rcp(Smash));
Layer2Mask *= Layer2Mask;
// LayerMerger = lerp(LayerMerger, SceneColorAndDepth.rgb, Layer2Mask * (1 - PerPixelNearMask));
LayerMerger = lerp(LayerMerger, SceneColorAndDepth.rgb, Layer2Mask);
float3 FrontLayer = (UnfocusedSceneColor * Div0Bias + DOFAccumLayer3.rgb) / (DOFAccumLayer3.a + Div0Bias);
// Layer 3: on top of that blend the front half res layer
LayerMerger = lerp(LayerMerger, FrontLayer, saturate(Layer3Mask * 5));
OutColor.rgb = LayerMerger;
OutColor.a = 0;
}