From 8e937abe6622b6d51f6c918072248ca32888d1af Mon Sep 17 00:00:00 2001
From: Adrian Courreges <a.courreges@gmail.com>
Date: Sat, 1 Dec 2018 10:28:04 +0900
Subject: [PATCH] GatherDOF integration (drop-in replacement for BokehDOF)
Enable with "r.GatherDOF.Enable 1".
Full details and information: http://www.adriancourreges.com/blog/2018/12/02/ue4-optimized-post-effects/
---
Engine/Shaders/Private/PostProcessGatherDOF.usf | 574 +++++++
.../Private/PostProcess/PostProcessBokehDOF.cpp | 1644 ++++++++++++++++++++
.../Private/PostProcess/PostProcessBokehDOF.h | 157 ++
.../Private/PostProcess/PostProcessing.cpp | 266 +++-
4 files changed, 2610 insertions(+), 31 deletions(-)
create mode 100644 Engine/Shaders/Private/PostProcessGatherDOF.usf
diff --git a/Engine/Shaders/Private/PostProcessGatherDOF.usf b/Engine/Shaders/Private/PostProcessGatherDOF.usf
new file mode 100644
index 00000000000..48bb5bd61c9
--- /dev/null
+++ b/Engine/Shaders/Private/PostProcessGatherDOF.usf
@@ -0,0 +1,574 @@
+/*=============================================================================
+ PostProcessGatherDOF.usf: Gather-type depth of field effect.
+
+ Inspired by Crytek 3 technique:
+ http://www.crytek.com/download/Sousa_Graphics_Gems_CryENGINE3.pdf
+
+ Supported bokeh shapes: disk, any N-sided polygon (hexagon, octagon...)
+
+ Overview:
+ - Work at half resolution, separate far field and near field based on CoC
+ - Flat-kernel blur of each field, with the bokeh shape desired using
+ a fixed number of taps (will create gaps at high radius).
+ 6x6 taps is an ok figure for a scene at 720p.
+ - Floodfill any gaps created by lack of sampling in the previous pass.
+ 3x3 taps can be enough.
+ - Special treatment for the near field, to have natural bleeding of
+ near objects onto the layers beneath:
+ . Create a tile map of near CoC, with several downscales, keeping
+ only max near CoC for the entire tile
+ . Bleed opaque tiles onto their transparent neighbors
+ - Composite the 3 layers: far / in-focus / near
+
+=============================================================================*/
+
+#include "Common.ush"
+#include "PostProcessCommon.ush"
+#include "DepthOfFieldCommon.ush"
+#include "SeparateTranslucency.ush"
+
+// High / low quality knob
+#if HIGH_QUALITY
+
+#define BLUR_TAP_COUNT 8
+#define FLOODFILL_TAP_COUNT 4
+
+#else
+
+#define BLUR_TAP_COUNT 6
+#define FLOODFILL_TAP_COUNT 3
+
+#endif // HIGH_QUALITY
+
+// .x : size of the bokeh blur radius in texel space
+// .y : rotation in radius to apply to the bokeh shape
+// .z : Number of edge of the polygon (number of blades). 0: circle. 4: square, 6: hexagon...
+float4 KernelSize;
+
+// Maps a unit square in [0, 1] to a unit disk in [-1, 1]
+// Returns polar coordinates (radius, angle)
+// Shirley 97 "A Low Distortion Map Between Disk and Square"
+float2 UnitSquareToUnitDiskPolar(float2 uv)
+{
+ float radius;
+ float angle;
+
+ const float PI_BY_2 = 1.5707963f; // PI / 2
+ const float PI_BY_4 = 0.785398f; // PI / 4
+ const float EPSILON = 0.000001f;
+
+ // Remap [0, 1] to [-1, 1] centered
+ float a = (2.0f * uv.x) - 1.0f;
+ float b = (2.0f * uv.y) - 1.0f;
+
+ // Morph to unit disk
+ if (abs(a) > abs(b))
+ {
+ // First region (left and right quadrants of the disk)
+ radius = a;
+ angle = b / (a + EPSILON) * PI_BY_4;
+ }
+ else
+ {
+ // Second region (top and bottom quadrants of the disk)
+ radius = b;
+ angle = PI_BY_2 - (a / (b + EPSILON) * PI_BY_4);
+ }
+
+ if (radius < 0)
+ {
+ radius *= -1.0f;
+ angle += PI;
+ }
+
+ return float2(radius, angle);
+}
+
+// Maps a unit square in [0, 1] to a unit disk in [-1, 1]
+// Returns new cartesian coordinates (u,v)
+float2 SquareToDiskMapping(float2 uv) {
+ float2 PolarCoord = UnitSquareToUnitDiskPolar(uv);
+ return float2(PolarCoord.x * cos(PolarCoord.y), PolarCoord.x * sin(PolarCoord.y));
+}
+
+// Remap a unit square in [0, 1] to a unit polygon in [-1, 1]
+// Returns new cartesian coordinates (u,v)
+float2 SquareToPolygonMapping(float2 uv) {
+ const float N = KernelSize.z; // Edge count of the polygon.
+ float2 PolarCoord = UnitSquareToUnitDiskPolar(uv); // (radius, angle)
+
+ if (N >= 3.0f)
+ {
+ // Re-scale radius to match a polygon shape
+ // http://www.crytek.com/download/Sousa_Graphics_Gems_CryENGINE3.pdf
+ PolarCoord.x *= ( cos(PI / N) / ( cos(PolarCoord.y - (2.0f * PI / N) * floor((N*PolarCoord.y + PI) / 2.0f / PI ) )));
+
+ // Apply a rotation to the polygon shape.
+ PolarCoord.y += KernelSize.y;
+ }
+
+ return float2(PolarCoord.x * cos(PolarCoord.y), PolarCoord.x * sin(PolarCoord.y));
+}
+
+// TODO Taken from BokehDOF. Centralize!
+// @return x:layer in front of focal plane, y: layer behind focal plane 1-x-y:layer in focus
+float2 ComputeLayerContributions(float Depth)
+{
+ float Front = saturate((View.DepthOfFieldFocalDistance - Depth) / View.DepthOfFieldNearTransitionRegion);
+ float Back = saturate((Depth - View.DepthOfFieldFocalDistance - View.DepthOfFieldFocalRegion) / max(View.DepthOfFieldFarTransitionRegion, 0.0001f));
+
+ return float2(Front, Back);
+}
+
+// TODO Taken from BokehDOF. Centralize!
+float4 CommonDOFSetup(in float2 CenterUV, out bool bFrontLayer, out float4 Mask )
+{
+ float2 Offset = PostprocessInput0Size.zw;
+
+ float2 UV[4];
+
+ // no filtering (2x2 kernel) to get no leaking in Depth of Field
+ UV[0] = CenterUV + Offset * float2(-0.5f, -0.5f);
+ UV[1] = CenterUV + Offset * float2( 0.5f, -0.5f);
+ UV[2] = CenterUV + Offset * float2(-0.5f, 0.5f);
+ UV[3] = CenterUV + Offset * float2( 0.5f, 0.5f);
+
+ float4 ColorAndDepth[4];
+ float2 Layer[4];
+
+ UNROLL for(uint i = 0; i < 4; ++i)
+ {
+ // clamping to a small number fixes black dots appearing (denorms?, 0 doesn't fix it)
+ ColorAndDepth[i].rgb = max(float3(0.0001f, 0.0001f, 0.0001f), Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV[i]).rgb);
+ ColorAndDepth[i].a = CalcSceneDepth(UV[i]);
+ Layer[i] = ComputeLayerContributions(ColorAndDepth[i].a);
+ }
+
+ float2 LayerSum = Layer[0] + Layer[1] + Layer[2] + Layer[3];
+ bFrontLayer = LayerSum.x > LayerSum.y;
+
+ Mask = bFrontLayer ?
+ float4(Layer[0].x, Layer[1].x, Layer[2].x, Layer[3].x) :
+ float4(Layer[0].y, Layer[1].y, Layer[2].y, Layer[3].y);
+
+ float SumMask = dot(Mask, 1);
+
+ float4 OutColor;
+
+ FLATTEN if(SumMask > 0.001f)
+ {
+ OutColor = (
+ ColorAndDepth[0] * Mask.x +
+ ColorAndDepth[1] * Mask.y +
+ ColorAndDepth[2] * Mask.z +
+ ColorAndDepth[3] * Mask.w ) / SumMask;
+ }
+ else
+ {
+ OutColor = ColorAndDepth[0];
+ }
+
+ return OutColor;
+}
+
+// Downsample scene to half res, extract near field and far field
+void MainSetupPS(in noperspective float4 UVAndScreenPos : TEXCOORD0,
+ out float4 OutColor0 : SV_Target0, // Far color + CoC
+ out float4 OutColor1 : SV_Target1, // Near color + CoC
+ out float OutColor2 : SV_Target2 // Near Max CoC tilemap
+ )
+{
+ // unused for this pass
+ bool bFrontLayer;
+ float4 Mask;
+
+ float4 SceneColorAndDepth = CommonDOFSetup(UVAndScreenPos.xy, bFrontLayer, Mask);
+
+ // clamp to avoid artifacts from exceeding fp16 through framebuffer blending of multiple very bright lights
+ SceneColorAndDepth.rgb = min(float3(256 * 256, 256 * 256, 256 * 256), SceneColorAndDepth.rgb);
+
+ float SceneDepth = SceneColorAndDepth.a;
+ float CircleOfConfusion = ComputeCircleOfConfusion(SceneDepth);
+
+ if (SceneDepth < View.DepthOfFieldFocalDistance) {
+ // Near
+ OutColor0 = float4(0, 0, 0, 0);
+ OutColor1 = float4(SceneColorAndDepth.rgb, CircleOfConfusion);
+ OutColor2 = CircleOfConfusion; // Separate CoC to build Max TileMap
+ }
+ else if (SceneDepth >= View.DepthOfFieldFocalDistance + View.DepthOfFieldFocalRegion)
+ {
+ // Far
+ OutColor0 = float4(SceneColorAndDepth.rgb, CircleOfConfusion);
+ OutColor1 = float4(0, 0, 0, 0);
+ OutColor2 = 0.0f;
+ }
+ else
+ {
+ // In-focus (inside focal region)
+ OutColor0 = float4(0, 0, 0, 0);
+ OutColor1 = float4(0, 0, 0, 0);
+ OutColor2 = 0.0f;
+ }
+}
+
+
+// This shader is useful to read BokehDOF setup buffer (especially post TAA) and prepare
+// the near / far fields for GatherDOF. We now extract all directly from the full-res scene.
+// Target0: Far field
+// Target1: Near field
+// Target2: Near CoC tilemap
+void MainExtractAreaPS(
+ in noperspective float4 UVAndScreenPos : TEXCOORD0,
+ out float4 OutColor0 : SV_Target0,
+ out float4 OutColor1 : SV_Target1,
+ out float OutColor2 : SV_Target2)
+{
+ float4 SceneColorAndDepth = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, UVAndScreenPos.xy, 0);
+ float SceneDepth = SceneColorAndDepth.a;
+ float CircleOfConfusion = ComputeCircleOfConfusion(SceneDepth);
+
+ if (SceneDepth < View.DepthOfFieldFocalDistance) {
+ // Near
+ OutColor0 = float4(0, 0, 0, 0);
+ OutColor1 = float4(SceneColorAndDepth.rgb, CircleOfConfusion);
+ OutColor2 = CircleOfConfusion; // Separate CoC to build Max TileMap
+ }
+ else if (SceneDepth >= View.DepthOfFieldFocalDistance + View.DepthOfFieldFocalRegion)
+ {
+ // Far
+ OutColor0 = float4(SceneColorAndDepth.rgb, CircleOfConfusion);
+ OutColor1 = float4(0, 0, 0, 0);
+ OutColor2 = 0.0f;
+ }
+ else
+ {
+ // In-focus (inside focal region)
+ OutColor0 = float4(0, 0, 0, 0);
+ OutColor1 = float4(0, 0, 0, 0);
+ OutColor2 = 0.0f;
+ }
+}
+
+// Blur far field, applying a grid pattern of samples remapped to a bokeh shape
+void MainBlurFarPS(
+ in noperspective float4 UVAndScreenPos : TEXCOORD0,
+ out float4 OutColor : SV_Target0)
+{
+
+ float4 PixelColor = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, UVAndScreenPos.xy, 0);
+ float PixelCoC = PixelColor.w;
+
+ float3 ResultColor = 0;
+ float Weight = 0;
+
+ int TAP_COUNT = BLUR_TAP_COUNT; // Higher means less noise and make floodfilling easier after
+
+ // Multiplying by PixelCoC guarantees a smooth evolution of the blur radius
+ // especially visible on plane (like the floor) where CoC slowly grows with the distance.
+ // This makes all the difference between a natural bokeh and some noticeable
+ // in-focus and out-of-focus layer blending
+ float radius = KernelSize.x * PixelCoC;
+
+ if (PixelCoC > 0) { // Ignore any pixel not belonging to far field
+
+ // Weighted average of the texture samples inside the bokeh pattern
+ // High radius and low sample count can create "gaps" which are fixed later (floodfill).
+ for (int u = 0; u < TAP_COUNT; ++u)
+ {
+ for (int v = 0; v < TAP_COUNT; ++v)
+ {
+ float2 uv = float2(u, v) / (TAP_COUNT - 1); // map to [0, 1]
+ uv = SquareToPolygonMapping( uv ) * PostprocessInput0Size.zw; // map to bokeh shape, then to texel size
+ uv = UVAndScreenPos.xy + radius * uv;
+
+ float4 tapColor = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, uv, 0);
+
+ // Weighted by CoC. Gives more influence to taps with a CoC higher than us.
+ float TapWeight = tapColor.w * saturate(1.0f - (PixelCoC - tapColor.w));
+
+ ResultColor += tapColor.xyz * TapWeight;
+ Weight += TapWeight;
+ }
+ }
+ if (Weight > 0) ResultColor /= Weight;
+ Weight = Weight / TAP_COUNT / TAP_COUNT;
+ }
+
+ Weight = saturate(Weight * 10); // From CoC 0.1, completely rely on the far field layer and stop lerping with in-focus layer
+ OutColor = float4(ResultColor, Weight);
+}
+
+// Box downscale the near CoC tilemap, keeping only the max value of the 4 taps
+void MainDownscaleTMPS(
+ in noperspective float4 UVAndScreenPos : TEXCOORD0,
+ out float OutColor : SV_Target0)
+{
+ float2 tapUV = UVAndScreenPos.xy;
+
+ float4 CoCs[4];
+
+ CoCs[0] = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, tapUV , 0);
+ CoCs[1] = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, tapUV + float2( 0.5f, 0.0f) * PostprocessInput0Size.zw, 0);
+ CoCs[2] = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, tapUV + float2( 0.0f, 0.5f) * PostprocessInput0Size.zw, 0);
+ CoCs[3] = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, tapUV + float2( 0.5f, 0.5f) * PostprocessInput0Size.zw, 0);
+
+ OutColor = max( CoCs[0], max( CoCs[1], max( CoCs[2], CoCs[3] ) ) );
+}
+
+// Creates a halo for the tilemap, so we can have nice bokeh shape spreading outside the original near field area
+void MainNearHaloPS(
+ in noperspective float4 UVAndScreenPos : TEXCOORD0,
+ out float OutColor : SV_Target0)
+{
+ float2 tapUV = UVAndScreenPos.xy;
+
+ float PixelColor = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, tapUV, 0);
+
+ if (PixelColor == 0) // Only fill the empty areas around near field
+ {
+ PixelColor = 0;
+ float Weight = 0;
+ int RADIUS_TAPS = 4; // 8x8 taps, but shouldn't be heavy at such low resolution
+ for (int u = -RADIUS_TAPS; u <= RADIUS_TAPS; ++u)
+ {
+ for (int v = -RADIUS_TAPS; v <= RADIUS_TAPS; ++v)
+ {
+ float tapValue = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, tapUV + float2(u,v) * PostprocessInput0Size.zw, 0);
+ float tapWeight = tapValue == 0.0f? 0.0f : 1.0f;
+ PixelColor += tapWeight * tapValue;
+ Weight += tapWeight;
+ }
+ }
+ PixelColor /= (Weight + 0.000001f);
+ }
+
+ OutColor = PixelColor;
+}
+
+// Makes near color bleed into transparent texel
+// (poor-man way since pre-multiplied alpha doesn't play nice with alpha boost in the main blur pass)
+void MainNearBorderPS(
+ in noperspective float4 UVAndScreenPos : TEXCOORD0,
+ out float4 OutColor : SV_Target0)
+{
+ float2 tapUV = UVAndScreenPos.xy;
+
+ float4 PixelColor = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, tapUV, 0);
+
+ if (PixelColor.w == 0) // Only fill the empty areas around near field
+ {
+ PixelColor = 0;
+ float Weight = 0;
+ int RADIUS_TAPS = 1;
+ for (int u = -RADIUS_TAPS; u <= RADIUS_TAPS; ++u)
+ {
+ for (int v = -RADIUS_TAPS; v <= RADIUS_TAPS; ++v)
+ {
+ float4 tapValue = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, tapUV + float2(u,v) * PostprocessInput0Size.zw, 0);
+ float tapWeight = tapValue.w == 0.0f? 0.0f : 1.0f;
+ PixelColor += tapWeight * tapValue;
+ Weight += tapWeight;
+ }
+ }
+ PixelColor /= (Weight + 0.0000001f);
+ PixelColor.w = 0;
+ }
+
+ OutColor = PixelColor;
+}
+
+
+// Blur near field
+void MainBlurNearPS(
+ in noperspective float4 UVAndScreenPos : TEXCOORD0,
+ out float4 OutColor : SV_Target0)
+{
+
+ float4 PixelColor = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, UVAndScreenPos.xy, 0);
+ float PixelCoC = Texture2DSampleLevel(PostprocessInput1, PostprocessInput1Sampler, UVAndScreenPos.xy, 0);
+
+ float3 ResultColor = 0;
+ float Weight = 0;
+ int TAP_COUNT = BLUR_TAP_COUNT; // Higher means less noise and make floodfilling easier after
+
+ float radius = KernelSize.x * 0.7f * PixelCoC;
+
+ if (PixelCoC > 0) { // Early exit based on MaxCoC tilemap
+ for (int u = 0; u < TAP_COUNT; ++u)
+ {
+ for (int v = 0; v < TAP_COUNT; ++v)
+ {
+ float2 uv = float2(u, v) / (TAP_COUNT - 1);
+ uv = SquareToPolygonMapping( uv ) * PostprocessInput0Size.zw; // map to shape, to texel size
+ uv = UVAndScreenPos.xy + radius * uv;
+
+ float4 tapColor = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, uv, 0);
+ float TapWeight = saturate(tapColor.w * 10.0f); // From CoC 0.1 rely only on the near field and stop lerping with in-focus area
+
+ ResultColor += tapColor.xyz * TapWeight;
+ Weight += TapWeight;
+ }
+ }
+
+ ResultColor /= (Weight + 0.0000001f);
+ Weight /= (TAP_COUNT * TAP_COUNT);
+ }
+
+ OutColor = float4(ResultColor, Weight);
+}
+
+
+// Performs a floodfill to fill the "gaps" which can appear between blur samples.
+void MainFloodfillPS(
+ in noperspective float4 UVAndScreenPos : TEXCOORD0,
+ out float4 OutColor : SV_Target0)
+{
+ float4 PixelColor = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, UVAndScreenPos.xy, 0);
+ float PixelCoC = PixelColor.w;
+
+#if COC_TILEMAP
+ // Use max CoC tilemap instead of the original pixel CoC for near field
+ PixelCoC = Texture2DSampleLevel(PostprocessInput1, PostprocessInput1Sampler, UVAndScreenPos.xy, 0);
+#endif
+
+ float4 ResultColor = PixelColor;
+
+ float radius = PixelCoC * 1.75f * KernelSize.x / 15.0f;
+
+ int TAP_COUNT = FLOODFILL_TAP_COUNT; // Higher count improves the floodfill
+
+ if (PixelCoC > 0) { // Early out if we're outside the field
+
+ // McIntosh12 http://ivizlab.sfu.ca/papers/cgf2012.pdf
+ // Keep the maximum of all the samples
+ for (int u = 0; u < TAP_COUNT; ++u)
+ {
+ for (int v = 0; v < TAP_COUNT; ++v)
+ {
+ float2 uv = float2(u, v) / (TAP_COUNT - 1); // map to [0, 1]
+ uv = SquareToPolygonMapping( uv ) * PostprocessInput0Size.zw; // map to bokeh shape, then to texel size
+ uv = UVAndScreenPos.xy + radius * uv;
+
+ float4 tapColor = Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, uv, 0);
+ ResultColor = max(ResultColor, tapColor);
+ }
+ }
+ }
+#if COC_TILEMAP
+ OutColor = ResultColor;
+#else
+ // do not touch alpha of far field
+ OutColor = float4(ResultColor.xyz, PixelCoC);
+#endif
+}
+
+// Blend in-focus / far / near layers on the top of each others
+float3 GetCombinedLayerColor(float2 uv, float4 SvPosition)
+{
+ // Set FadePower to 1.0f in the next line to be physically correct,
+ // but some in-focus silhouette might become visible at the border of the
+ // near field, where the gradient smoothly fades alpha to 0. [0 -> 0.5 -> 1]
+ // Limitation of screen-space effect: we don't have access to the actual color occluded by near field meshes.
+ // This makes the near-field fading gradient more aggressive [0 -> 1 -> 1] to hide any sharp silhouette.
+ float FadePower = 2.0f;
+
+#if 1 // Optimized version using early-out to avoid unnecessary texture fetches -> ~25% speed-up in reference scene.
+
+ float4 NearColor = Texture2DSampleLevel(PostprocessInput2, PostprocessInput2Sampler, uv, 0);
+
+ NearColor.w = saturate(NearColor.w * FadePower);
+
+ // Pure near field, early exit
+ if (NearColor.w == 1.0f)
+ {
+ return NearColor.rgb;
+ }
+
+ float4 FarColor = Texture2DSampleLevel(PostprocessInput1, PostprocessInput1Sampler, uv, 0);
+
+ // Original CoC to guarantee crisp edge of in-focus over far-field
+ float2 PixelPosCenter = SvPosition.xy;
+ float2 FullResUV = PixelPosCenter * PostprocessInput0Size.zw;
+ float SceneDepth = CalcSceneDepth(FullResUV);
+
+ bool isInFocus = (SceneDepth >= View.DepthOfFieldFocalDistance) &&
+ (SceneDepth < View.DepthOfFieldFocalDistance + View.DepthOfFieldFocalRegion);
+
+ if (!isInFocus) {
+ // Pure far field without any bleeding from near field, early exit
+ if (FarColor.w == 1.0f && NearColor.w == 0.0f )
+ {
+ return FarColor.rgb;
+ }
+
+ // Blending only between far and near
+ if (FarColor.w == 1.0f) {
+ return float3( NearColor.w * (NearColor.rgb) + (1.0f - NearColor.w) * FarColor.rgb );
+ }
+ } else {
+ // Pixel was originally in focus, background should never bleed onto it
+ FarColor.w = 0;
+ }
+
+ // Worst case: 3 layer merge
+ float3 FocusColor = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, uv).rgb;
+
+ float3 Result = FarColor.w * FarColor.rgb + (1.0f - FarColor.w) * FocusColor;
+ Result = NearColor.w * (NearColor) + (1.0f - NearColor.w) * Result;
+
+ return Result;
+
+#else // Original generic version
+
+ float3 FocusColor = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, uv).rgb;
+ float4 FarColor = Texture2DSampleLevel(PostprocessInput1, PostprocessInput1Sampler, uv, 0);
+ float4 NearColor = Texture2DSampleLevel(PostprocessInput2, PostprocessInput2Sampler, uv, 0);
+
+ // Original CoC to guarantee crisp edge of in-focus over far-field
+ float2 PixelPosCenter = SvPosition.xy;
+ float2 FullResUV = PixelPosCenter * PostprocessInput0Size.zw;
+ float SceneDepth = CalcSceneDepth(FullResUV);
+
+ bool isInFocus = (SceneDepth >= View.DepthOfFieldFocalDistance) &&
+ (SceneDepth < View.DepthOfFieldFocalDistance + View.DepthOfFieldFocalRegion);
+ if (isInFocus) FarColor.w = 0;
+
+ // Alpha composite far field on the top of the original scene.
+ float3 Result = FarColor.w * FarColor.rgb + (1.0f - FarColor.w) * FocusColor;
+
+ // Alpha composite on the near field
+ if (NearColor.w > 0) {
+ float blendFactor = saturate(NearColor.w * FadePower);
+ Result = blendFactor * (NearColor.rgb) + (1.0f - blendFactor) * Result;
+ }
+
+ return Result;
+#endif
+
+}
+
+
+void MainRecombinePS(
+ in noperspective float4 UVAndScreenPos : TEXCOORD0,
+ float4 SvPosition : SV_POSITION,
+ out float4 OutColor : SV_Target0)
+{
+ float2 uv = UVAndScreenPos;
+
+#if RECOMBINE_METHOD == 2 // Separate translucency
+ // SceneColor in full res
+ float2 PixelPosCenter = SvPosition.xy;
+ float2 FullResUV = PixelPosCenter * PostprocessInput0Size.zw;
+ float4 SeparateTranslucency = UpsampleSeparateTranslucency(SvPosition.xy, FullResUV, PostprocessInput3, PostprocessInput3Size.zw);
+#endif
+
+ float3 RecombinedLayersColor = GetCombinedLayerColor(uv, SvPosition);
+
+#if RECOMBINE_METHOD == 2
+ // Separate translucency as premultiplied alpha
+ RecombinedLayersColor.rgb = RecombinedLayersColor.rgb * SeparateTranslucency.a + SeparateTranslucency.rgb;
+#endif
+
+ OutColor = float4(RecombinedLayersColor, 1.0f);
+}
\ No newline at end of file
diff --git a/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessBokehDOF.cpp b/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessBokehDOF.cpp
index e43ed9e8640..78a6f2866bb 100644
--- a/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessBokehDOF.cpp
+++ b/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessBokehDOF.cpp
@@ -931,3 +931,1647 @@ FPooledRenderTargetDesc FRCPassPostProcessBokehDOF::ComputeOutputDesc(EPassOutpu
return Ret;
}
+
+//-------------------------------------------
+// Gather Bokeh Depth of Field
+//-------------------------------------------
+
+/** Encapsulates the post processing depth of field setup pixel shader. */
+class PostProcessGatherDOFSetupPS : public FGlobalShader
+{
+ DECLARE_SHADER_TYPE(PostProcessGatherDOFSetupPS, Global);
+
+ static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
+ {
+ return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
+ }
+
+ /** Default constructor. */
+ PostProcessGatherDOFSetupPS() {}
+
+public:
+ FPostProcessPassParameters PostprocessParameter;
+ FSceneTextureShaderParameters SceneTextureParameters;
+ FShaderParameter DepthOfFieldParams;
+
+ /** Initialization constructor. */
+ PostProcessGatherDOFSetupPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
+ : FGlobalShader(Initializer)
+ {
+ PostprocessParameter.Bind(Initializer.ParameterMap);
+ SceneTextureParameters.Bind(Initializer);
+ DepthOfFieldParams.Bind(Initializer.ParameterMap, TEXT("DepthOfFieldParams"));
+ }
+
+ // FShader interface.
+ virtual bool Serialize(FArchive& Ar) override
+ {
+ bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
+ Ar << PostprocessParameter << SceneTextureParameters << DepthOfFieldParams;
+ return bShaderHasOutdatedParameters;
+ }
+
+ void SetParameters(const FRenderingCompositePassContext& Context)
+ {
+ const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
+
+ FGlobalShader::SetParameters<FViewUniformShaderParameters>(Context.RHICmdList, ShaderRHI, Context.View.ViewUniformBuffer);
+
+ SceneTextureParameters.Set(Context.RHICmdList, ShaderRHI, Context.View.FeatureLevel, ESceneTextureSetupMode::All);
+ PostprocessParameter.SetPS(Context.RHICmdList, ShaderRHI, Context, TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
+
+ {
+ FVector4 DepthOfFieldParamValues[2];
+
+ // in rendertarget pixels (half res to scene color)
+ FRenderingCompositeOutput* Output = Context.Pass->GetOutput(ePId_Output0);
+
+ FRCPassPostProcessBokehDOF::ComputeDepthOfFieldParams(Context, DepthOfFieldParamValues);
+
+ SetShaderValueArray(Context.RHICmdList, ShaderRHI, DepthOfFieldParams, DepthOfFieldParamValues, 2);
+ }
+ }
+
+ static const TCHAR* GetSourceFilename()
+ {
+ return TEXT("/Engine/Private/PostProcessGatherDOF.usf");
+ }
+
+ static const TCHAR* GetFunctionName()
+ {
+ return TEXT("MainSetupPS");
+ }
+};
+
+IMPLEMENT_SHADER_TYPE3(PostProcessGatherDOFSetupPS, SF_Pixel);
+
+void FRCPassPostProcessGatherDOFSetup::Process(FRenderingCompositePassContext& Context)
+{
+ SCOPED_DRAW_EVENT(Context.RHICmdList, GatherDOFSetup);
+
+ const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
+
+ if (!InputDesc)
+ {
+ // input is not hooked up correctly
+ return;
+ }
+
+ const FViewInfo& View = Context.View;
+ const FSceneViewFamily& ViewFamily = *(View.Family);
+
+ FIntPoint SrcSize = InputDesc->Extent;
+ FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent;
+
+ // e.g. 4 means the input texture is 4x smaller than the buffer size
+ uint32 ScaleFactor = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+
+ FIntRect SrcRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleFactor);
+ FIntRect DestRect = FIntRect::DivideAndRoundUp(SrcRect, 2);
+
+ const FSceneRenderTargetItem& DestRenderTarget0 = PassOutputs[0].RequestSurface(Context);
+ const FSceneRenderTargetItem& DestRenderTarget1 = PassOutputs[1].RequestSurface(Context);
+ const FSceneRenderTargetItem& DestRenderTarget2 = PassOutputs[2].RequestSurface(Context);
+
+ // Set the view family's render target/viewport.
+ FTextureRHIParamRef RenderTargets[3] =
+ {
+ DestRenderTarget0.TargetableTexture,
+ DestRenderTarget1.TargetableTexture,
+ DestRenderTarget2.TargetableTexture
+ };
+ //@todo Ronin find a way to use the same codepath for all platforms.
+ const EShaderPlatform ShaderPlatform = GShaderPlatformForFeatureLevel[Context.GetFeatureLevel()];
+ if (IsVulkanMobilePlatform(ShaderPlatform))
+ {
+ SetRenderTargets(Context.RHICmdList, 3, RenderTargets, FTextureRHIParamRef(), ESimpleRenderTargetMode::EClearColorAndDepth, FExclusiveDepthStencil());
+ }
+ else
+ {
+ SetRenderTargets(Context.RHICmdList, 3, RenderTargets, FTextureRHIParamRef(), 0, NULL);
+ }
+
+ Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f);
+
+ FGraphicsPipelineStateInitializer GraphicsPSOInit;
+ Context.RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
+
+ // set the state
+ GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
+ GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
+ GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
+
+ // setup shader
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+ GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
+ GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
+ GraphicsPSOInit.PrimitiveType = PT_TriangleList;
+
+ {
+ TShaderMapRef<PostProcessGatherDOFSetupPS> PixelShader(Context.GetShaderMap());
+
+ GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
+ SetGraphicsPipelineState(Context.RHICmdList, GraphicsPSOInit);
+
+ VertexShader->SetParameters(Context);
+ PixelShader->SetParameters(Context);
+ }
+
+ DrawPostProcessPass(
+ Context.RHICmdList,
+ DestRect.Min.X, DestRect.Min.Y,
+ DestRect.Width(), DestRect.Height(),
+ SrcRect.Min.X, SrcRect.Min.Y,
+ SrcRect.Width(), SrcRect.Height(),
+ DestSize,
+ SrcSize,
+ *VertexShader,
+ View.StereoPass,
+ Context.HasHmdMesh(),
+ EDRF_UseTriangleOptimization);
+
+
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget0.TargetableTexture, DestRenderTarget0.ShaderResourceTexture, false, FResolveParams());
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget1.TargetableTexture, DestRenderTarget1.ShaderResourceTexture, false, FResolveParams());
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget2.TargetableTexture, DestRenderTarget2.ShaderResourceTexture, false, FResolveParams());
+}
+
+FPooledRenderTargetDesc FRCPassPostProcessGatherDOFSetup::ComputeOutputDesc(EPassOutputId InPassOutputId) const
+{
+ FPooledRenderTargetDesc Ret = GetInput(ePId_Input0)->GetOutput()->RenderTargetDesc;
+
+ Ret.Reset();
+ Ret.Extent /= 2;
+ Ret.Extent.X = FMath::Max(1, Ret.Extent.X);
+ Ret.Extent.Y = FMath::Max(1, Ret.Extent.Y);
+
+ switch (InPassOutputId) {
+
+ case ePId_Output0:
+ // Far
+ Ret.Format = PF_FloatRGBA;
+ Ret.DebugName = TEXT("GatherDOFSetupFar");
+ break;
+
+ case ePId_Output1:
+ // Near
+ Ret.Format = PF_FloatRGBA;
+ Ret.DebugName = TEXT("GatherDOFSetupNear");
+ break;
+
+ case ePId_Output2:
+ // Max Near CoC tilemap
+ Ret.Format = PF_G8;
+ Ret.DebugName = TEXT("GatherDOFSetupNearTilemap");
+ break;
+ }
+
+ return Ret;
+}
+
+
+
+
+//----------------------------------------------
+// Extracting near and far fields
+//----------------------------------------------
+
+class PostProcessBokehDOFGatherExtractAreaPS : public FGlobalShader
+{
+ DECLARE_SHADER_TYPE(PostProcessBokehDOFGatherExtractAreaPS, Global);
+
+ static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
+ {
+ return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
+ }
+
+ /** Default constructor. */
+ PostProcessBokehDOFGatherExtractAreaPS() {}
+
+public:
+ FPostProcessPassParameters PostprocessParameter;
+ FSceneTextureShaderParameters SceneTextureParameters;
+ FShaderParameter DepthOfFieldParams;
+
+ /** Initialization constructor. */
+ PostProcessBokehDOFGatherExtractAreaPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
+ : FGlobalShader(Initializer)
+ {
+ PostprocessParameter.Bind(Initializer.ParameterMap);
+ SceneTextureParameters.Bind(Initializer);
+ DepthOfFieldParams.Bind(Initializer.ParameterMap, TEXT("DepthOfFieldParams"));
+ }
+
+ // FShader interface.
+ virtual bool Serialize(FArchive& Ar) override
+ {
+ bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
+ Ar << PostprocessParameter << SceneTextureParameters << DepthOfFieldParams;
+ return bShaderHasOutdatedParameters;
+ }
+
+ void SetParameters(const FRenderingCompositePassContext& Context)
+ {
+ const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
+
+ FGlobalShader::SetParameters<FViewUniformShaderParameters>(Context.RHICmdList, ShaderRHI, Context.View.ViewUniformBuffer);
+
+ SceneTextureParameters.Set(Context.RHICmdList, ShaderRHI, Context.View.FeatureLevel, ESceneTextureSetupMode::All);
+ PostprocessParameter.SetPS(Context.RHICmdList, ShaderRHI, Context, TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
+
+ {
+ FVector4 DepthOfFieldParamValues[2];
+
+ // in rendertarget pixels (half res to scene color)
+ FRenderingCompositeOutput* Output = Context.Pass->GetOutput(ePId_Output0);
+
+ FRCPassPostProcessBokehDOF::ComputeDepthOfFieldParams(Context, DepthOfFieldParamValues);
+
+ SetShaderValueArray(Context.RHICmdList, ShaderRHI, DepthOfFieldParams, DepthOfFieldParamValues, 2);
+ }
+ }
+
+ static const TCHAR* GetSourceFilename()
+ {
+ return TEXT("/Engine/Private/PostProcessGatherDOF.usf");
+ }
+
+ static const TCHAR* GetFunctionName()
+ {
+ return TEXT("MainExtractAreaPS");
+ }
+};
+
+IMPLEMENT_SHADER_TYPE3(PostProcessBokehDOFGatherExtractAreaPS, SF_Pixel);
+
+
+void FRCPassPostProcessGatherDOFExtractArea::Process(FRenderingCompositePassContext& Context)
+{
+ SCOPED_DRAW_EVENT(Context.RHICmdList, BokehGatherExtractArea);
+
+ const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
+
+ if (!InputDesc)
+ {
+ // input is not hooked up correctly
+ return;
+ }
+
+ const FViewInfo& View = Context.View;
+ const FSceneViewFamily& ViewFamily = *(View.Family);
+
+ FIntPoint SrcSize = InputDesc->Extent;
+ FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent;
+
+ // e.g. 4 means the input texture is 4x smaller than the buffer size
+ uint32 ScaleFactor = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+
+ FIntRect SrcRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleFactor);
+ FIntRect DestRect = SrcRect;
+
+ const FSceneRenderTargetItem& DestRenderTarget0 = PassOutputs[0].RequestSurface(Context);
+ const FSceneRenderTargetItem& DestRenderTarget1 = PassOutputs[1].RequestSurface(Context);
+ const FSceneRenderTargetItem& DestRenderTarget2 = PassOutputs[2].RequestSurface(Context);
+
+ // Set the view family's render target/viewport.
+ FTextureRHIParamRef RenderTargets[3] =
+ {
+ DestRenderTarget0.TargetableTexture,
+ DestRenderTarget1.TargetableTexture,
+ DestRenderTarget2.TargetableTexture
+ };
+ //@todo Ronin find a way to use the same codepath for all platforms.
+ const EShaderPlatform ShaderPlatform = GShaderPlatformForFeatureLevel[Context.GetFeatureLevel()];
+ if (IsVulkanMobilePlatform(ShaderPlatform))
+ {
+ SetRenderTargets(Context.RHICmdList, 3, RenderTargets, FTextureRHIParamRef(), ESimpleRenderTargetMode::EClearColorAndDepth, FExclusiveDepthStencil());
+ }
+ else
+ {
+ SetRenderTargets(Context.RHICmdList, 3, RenderTargets, FTextureRHIParamRef(), 0, NULL);
+ }
+
+ Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f);
+
+ FGraphicsPipelineStateInitializer GraphicsPSOInit;
+ Context.RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
+
+ // set the state
+ GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
+ GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
+ GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
+
+ // setup shader
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+ GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
+ GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
+ GraphicsPSOInit.PrimitiveType = PT_TriangleList;
+
+ {
+ TShaderMapRef<PostProcessBokehDOFGatherExtractAreaPS> PixelShader(Context.GetShaderMap());
+
+ GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
+ SetGraphicsPipelineState(Context.RHICmdList, GraphicsPSOInit);
+
+ VertexShader->SetParameters(Context);
+ PixelShader->SetParameters(Context);
+ }
+
+ DrawPostProcessPass(
+ Context.RHICmdList,
+ DestRect.Min.X, DestRect.Min.Y,
+ DestRect.Width(), DestRect.Height(),
+ SrcRect.Min.X, SrcRect.Min.Y,
+ SrcRect.Width(), SrcRect.Height(),
+ DestSize,
+ SrcSize,
+ *VertexShader,
+ View.StereoPass,
+ Context.HasHmdMesh(),
+ EDRF_UseTriangleOptimization);
+
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget0.TargetableTexture, DestRenderTarget0.ShaderResourceTexture, false, FResolveParams());
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget1.TargetableTexture, DestRenderTarget1.ShaderResourceTexture, false, FResolveParams());
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget2.TargetableTexture, DestRenderTarget2.ShaderResourceTexture, false, FResolveParams());
+}
+
+FPooledRenderTargetDesc FRCPassPostProcessGatherDOFExtractArea::ComputeOutputDesc(EPassOutputId InPassOutputId) const
+{
+ FPooledRenderTargetDesc Ret = GetInput(ePId_Input0)->GetOutput()->RenderTargetDesc;
+
+ Ret.Reset();
+ // more precision for additive blending
+ Ret.Format = PF_FloatRGBA;
+
+
+
+ // we need space for the front part and the back part
+ ////Ret.Extent.Y = HalfRes * 2 + SafetyBorder;
+ Ret.DebugName = (InPassOutputId == ePId_Output0) ? TEXT("GatherDOFExtract_Far") : TEXT("GatherDOFExtract_Near");
+
+ // Extra target with near depth for tiling
+ if (InPassOutputId == ePId_Output2) {
+ Ret.Format = PF_G8;
+ Ret.DebugName = TEXT("GatherDOFNearTileMap");
+ }
+
+ return Ret;
+}
+
+
+//----------------------------------------------
+// Downscale Tilemap
+//----------------------------------------------
+
+class PostProcessBokehDOFGatherDownscaleTMPS : public FGlobalShader
+{
+ DECLARE_SHADER_TYPE(PostProcessBokehDOFGatherDownscaleTMPS, Global);
+
+ static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
+ {
+ return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
+ }
+
+ /** Default constructor. */
+ PostProcessBokehDOFGatherDownscaleTMPS() {}
+
+public:
+ FPostProcessPassParameters PostprocessParameter;
+ FSceneTextureShaderParameters SceneTextureParameters;
+ FShaderParameter DepthOfFieldParams;
+
+ /** Initialization constructor. */
+ PostProcessBokehDOFGatherDownscaleTMPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
+ : FGlobalShader(Initializer)
+ {
+ PostprocessParameter.Bind(Initializer.ParameterMap);
+ SceneTextureParameters.Bind(Initializer);
+ DepthOfFieldParams.Bind(Initializer.ParameterMap, TEXT("DepthOfFieldParams"));
+ }
+
+ // FShader interface.
+ virtual bool Serialize(FArchive& Ar) override
+ {
+ bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
+ Ar << PostprocessParameter << SceneTextureParameters << DepthOfFieldParams;
+ return bShaderHasOutdatedParameters;
+ }
+
+ void SetParameters(const FRenderingCompositePassContext& Context)
+ {
+ const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
+
+ FGlobalShader::SetParameters<FViewUniformShaderParameters>(Context.RHICmdList, ShaderRHI, Context.View.ViewUniformBuffer);
+
+ SceneTextureParameters.Set(Context.RHICmdList, ShaderRHI, Context.View.FeatureLevel, ESceneTextureSetupMode::All);
+ PostprocessParameter.SetPS(Context.RHICmdList, ShaderRHI, Context, TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
+
+ {
+ FVector4 DepthOfFieldParamValues[2];
+ FRCPassPostProcessBokehDOF::ComputeDepthOfFieldParams(Context, DepthOfFieldParamValues);
+ SetShaderValueArray(Context.RHICmdList, ShaderRHI, DepthOfFieldParams, DepthOfFieldParamValues, 2);
+ }
+ }
+
+ static const TCHAR* GetSourceFilename()
+ {
+ return TEXT("/Engine/Private/PostProcessGatherDOF.usf");
+ }
+
+ static const TCHAR* GetFunctionName()
+ {
+ return TEXT("MainDownscaleTMPS");
+ }
+};
+
+IMPLEMENT_SHADER_TYPE3(PostProcessBokehDOFGatherDownscaleTMPS, SF_Pixel);
+
+
+void FRCPassPostProcessGatherDOFDownscaleTM::Process(FRenderingCompositePassContext& Context)
+{
+ SCOPED_DRAW_EVENT(Context.RHICmdList, BokehGatherDownscaleTM);
+
+ const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
+
+ if (!InputDesc)
+ {
+ // input is not hooked up correctly
+ return;
+ }
+
+ const FViewInfo& View = Context.View;
+ const FSceneViewFamily& ViewFamily = *(View.Family);
+
+ FIntPoint SrcSize = InputDesc->Extent;
+ FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent;
+
+ // e.g. 4 means the input texture is 4x smaller than the buffer size
+ uint32 ScaleFactor = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+
+ FIntRect SrcRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleFactor);
+ FIntRect DestRect = FIntRect::DivideAndRoundUp(SrcRect, 2);
+
+ const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
+
+ SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef());
+
+ Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f);
+
+ FGraphicsPipelineStateInitializer GraphicsPSOInit;
+ Context.RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
+
+ // set the state
+ GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
+ GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
+ GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
+
+ // setup shader
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+ GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
+ GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
+ GraphicsPSOInit.PrimitiveType = PT_TriangleList;
+
+ {
+ TShaderMapRef<PostProcessBokehDOFGatherDownscaleTMPS> PixelShader(Context.GetShaderMap());
+
+ GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
+ SetGraphicsPipelineState(Context.RHICmdList, GraphicsPSOInit);
+
+ VertexShader->SetParameters(Context);
+ PixelShader->SetParameters(Context);
+ }
+
+ DrawPostProcessPass(
+ Context.RHICmdList,
+ DestRect.Min.X, DestRect.Min.Y,
+ DestRect.Width(), DestRect.Height(),
+ SrcRect.Min.X, SrcRect.Min.Y,
+ SrcRect.Width(), SrcRect.Height(),
+ DestSize,
+ SrcSize,
+ *VertexShader,
+ View.StereoPass,
+ Context.HasHmdMesh(),
+ EDRF_UseTriangleOptimization);
+
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
+}
+
+FPooledRenderTargetDesc FRCPassPostProcessGatherDOFDownscaleTM::ComputeOutputDesc(EPassOutputId InPassOutputId) const
+{
+ FPooledRenderTargetDesc Ret = GetInput(ePId_Input0)->GetOutput()->RenderTargetDesc;
+
+ Ret.Reset();
+ Ret.Extent /= 2;
+ Ret.Extent.X = FMath::Max(1, Ret.Extent.X);
+ Ret.Extent.Y = FMath::Max(1, Ret.Extent.Y);
+
+ // more precision for additive blending
+ Ret.Format = PF_G8;
+
+ Ret.DebugName = TEXT("GatherDOFDownscaleTM");
+
+ return Ret;
+}
+
+//-------------------------------------------------------------
+// Make Max CoC tilemap bleed onto transparent neighborhood
+//-------------------------------------------------------------
+
+class PostProcessBokehDOFGatherNearHaloPS : public FGlobalShader
+{
+ DECLARE_SHADER_TYPE(PostProcessBokehDOFGatherNearHaloPS, Global);
+
+ static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
+ {
+ return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
+ }
+
+ /** Default constructor. */
+ PostProcessBokehDOFGatherNearHaloPS() {}
+
+public:
+ FPostProcessPassParameters PostprocessParameter;
+ FSceneTextureShaderParameters SceneTextureParameters;
+ FShaderParameter DepthOfFieldParams;
+
+ /** Initialization constructor. */
+ PostProcessBokehDOFGatherNearHaloPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
+ : FGlobalShader(Initializer)
+ {
+ PostprocessParameter.Bind(Initializer.ParameterMap);
+ SceneTextureParameters.Bind(Initializer);
+ DepthOfFieldParams.Bind(Initializer.ParameterMap, TEXT("DepthOfFieldParams"));
+ }
+
+ // FShader interface.
+ virtual bool Serialize(FArchive& Ar) override
+ {
+ bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
+ Ar << PostprocessParameter << SceneTextureParameters << DepthOfFieldParams;
+ return bShaderHasOutdatedParameters;
+ }
+
+ void SetParameters(const FRenderingCompositePassContext& Context)
+ {
+ const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
+
+ FGlobalShader::SetParameters<FViewUniformShaderParameters>(Context.RHICmdList, ShaderRHI, Context.View.ViewUniformBuffer);
+
+ SceneTextureParameters.Set(Context.RHICmdList, ShaderRHI, Context.View.FeatureLevel, ESceneTextureSetupMode::All);
+ PostprocessParameter.SetPS(Context.RHICmdList, ShaderRHI, Context, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
+
+ {
+ FVector4 DepthOfFieldParamValues[2];
+ FRCPassPostProcessBokehDOF::ComputeDepthOfFieldParams(Context, DepthOfFieldParamValues);
+ SetShaderValueArray(Context.RHICmdList, ShaderRHI, DepthOfFieldParams, DepthOfFieldParamValues, 2);
+ }
+ }
+
+ static const TCHAR* GetSourceFilename()
+ {
+ return TEXT("/Engine/Private/PostProcessGatherDOF.usf");
+ }
+
+ static const TCHAR* GetFunctionName()
+ {
+ return TEXT("MainNearHaloPS");
+ }
+};
+
+IMPLEMENT_SHADER_TYPE3(PostProcessBokehDOFGatherNearHaloPS, SF_Pixel);
+
+
+void FRCPassPostProcessGatherDOFNearHalo::Process(FRenderingCompositePassContext& Context)
+{
+ SCOPED_DRAW_EVENT(Context.RHICmdList, BokehGatherNearHalo);
+
+ const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
+
+ if (!InputDesc)
+ {
+ // input is not hooked up correctly
+ return;
+ }
+
+ const FViewInfo& View = Context.View;
+ const FSceneViewFamily& ViewFamily = *(View.Family);
+
+ FIntPoint SrcSize = InputDesc->Extent;
+ FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent;
+
+ // e.g. 4 means the input texture is 4x smaller than the buffer size
+ uint32 ScaleFactor = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+
+ FIntRect SrcRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleFactor);
+ FIntRect DestRect = SrcRect;
+
+ const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
+
+ SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef());
+
+ Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f);
+
+ FGraphicsPipelineStateInitializer GraphicsPSOInit;
+ Context.RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
+
+ // set the state
+ GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
+ GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
+ GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
+
+ // setup shader
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+ GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
+ GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
+ GraphicsPSOInit.PrimitiveType = PT_TriangleList;
+
+ {
+ TShaderMapRef<PostProcessBokehDOFGatherNearHaloPS> PixelShader(Context.GetShaderMap());
+
+ GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
+ SetGraphicsPipelineState(Context.RHICmdList, GraphicsPSOInit);
+
+ VertexShader->SetParameters(Context);
+ PixelShader->SetParameters(Context);
+ }
+
+ DrawPostProcessPass(
+ Context.RHICmdList,
+ DestRect.Min.X, DestRect.Min.Y,
+ DestRect.Width(), DestRect.Height(),
+ SrcRect.Min.X, SrcRect.Min.Y,
+ SrcRect.Width(), SrcRect.Height(),
+ DestSize,
+ SrcSize,
+ *VertexShader,
+ View.StereoPass,
+ Context.HasHmdMesh(),
+ EDRF_UseTriangleOptimization);
+
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
+}
+
+FPooledRenderTargetDesc FRCPassPostProcessGatherDOFNearHalo::ComputeOutputDesc(EPassOutputId InPassOutputId) const
+{
+ FPooledRenderTargetDesc Ret = GetInput(ePId_Input0)->GetOutput()->RenderTargetDesc;
+
+ Ret.Reset();
+ // more precision for additive blending
+ Ret.Format = PF_G8;
+
+ Ret.DebugName = TEXT("GatherDOFNearHalo");
+
+ return Ret;
+}
+
+//-------------------------------------------------------------
+// 1-pixel border to make color bleed on transparent pixel
+// Poor-man way since pre-multiplied alpha doesn't play nice with
+// alpha boost in the main blur pass.
+//-------------------------------------------------------------
+
+class PostProcessBokehDOFGatherNearBorderPS : public FGlobalShader
+{
+ DECLARE_SHADER_TYPE(PostProcessBokehDOFGatherNearBorderPS, Global);
+
+ static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
+ {
+ return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
+ }
+
+ /** Default constructor. */
+ PostProcessBokehDOFGatherNearBorderPS() {}
+
+public:
+ FPostProcessPassParameters PostprocessParameter;
+ FSceneTextureShaderParameters SceneTextureParameters;
+ FShaderParameter DepthOfFieldParams;
+
+ /** Initialization constructor. */
+ PostProcessBokehDOFGatherNearBorderPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
+ : FGlobalShader(Initializer)
+ {
+ PostprocessParameter.Bind(Initializer.ParameterMap);
+ SceneTextureParameters.Bind(Initializer);
+ DepthOfFieldParams.Bind(Initializer.ParameterMap, TEXT("DepthOfFieldParams"));
+ }
+
+ // FShader interface.
+ virtual bool Serialize(FArchive& Ar) override
+ {
+ bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
+ Ar << PostprocessParameter << SceneTextureParameters << DepthOfFieldParams;
+ return bShaderHasOutdatedParameters;
+ }
+
+ void SetParameters(const FRenderingCompositePassContext& Context)
+ {
+ const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
+
+ FGlobalShader::SetParameters<FViewUniformShaderParameters>(Context.RHICmdList, ShaderRHI, Context.View.ViewUniformBuffer);
+
+ SceneTextureParameters.Set(Context.RHICmdList, ShaderRHI, Context.View.FeatureLevel, ESceneTextureSetupMode::All);
+ PostprocessParameter.SetPS(Context.RHICmdList, ShaderRHI, Context, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
+
+ {
+ FVector4 DepthOfFieldParamValues[2];
+ FRCPassPostProcessBokehDOF::ComputeDepthOfFieldParams(Context, DepthOfFieldParamValues);
+ SetShaderValueArray(Context.RHICmdList, ShaderRHI, DepthOfFieldParams, DepthOfFieldParamValues, 2);
+ }
+ }
+
+ static const TCHAR* GetSourceFilename()
+ {
+ return TEXT("/Engine/Private/PostProcessGatherDOF.usf");
+ }
+
+ static const TCHAR* GetFunctionName()
+ {
+ return TEXT("MainNearBorderPS");
+ }
+};
+
+IMPLEMENT_SHADER_TYPE3(PostProcessBokehDOFGatherNearBorderPS, SF_Pixel);
+
+
+void FRCPassPostProcessGatherDOFNearBorder::Process(FRenderingCompositePassContext& Context)
+{
+ SCOPED_DRAW_EVENT(Context.RHICmdList, BokehGatherNearBorder);
+
+ const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
+
+ if (!InputDesc)
+ {
+ // input is not hooked up correctly
+ return;
+ }
+
+ const FViewInfo& View = Context.View;
+ const FSceneViewFamily& ViewFamily = *(View.Family);
+
+ FIntPoint SrcSize = InputDesc->Extent;
+ FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent;
+
+ // e.g. 4 means the input texture is 4x smaller than the buffer size
+ uint32 ScaleFactor = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+
+ FIntRect SrcRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleFactor);
+ FIntRect DestRect = SrcRect;
+
+ const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
+
+ SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef());
+
+ Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f);
+
+ FGraphicsPipelineStateInitializer GraphicsPSOInit;
+ Context.RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
+
+ // set the state
+ GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
+ GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
+ GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
+
+ // setup shader
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+ GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
+ GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
+ GraphicsPSOInit.PrimitiveType = PT_TriangleList;
+
+ {
+ TShaderMapRef<PostProcessBokehDOFGatherNearBorderPS> PixelShader(Context.GetShaderMap());
+
+ GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
+ SetGraphicsPipelineState(Context.RHICmdList, GraphicsPSOInit);
+
+ VertexShader->SetParameters(Context);
+ PixelShader->SetParameters(Context);
+ }
+
+ DrawPostProcessPass(
+ Context.RHICmdList,
+ DestRect.Min.X, DestRect.Min.Y,
+ DestRect.Width(), DestRect.Height(),
+ SrcRect.Min.X, SrcRect.Min.Y,
+ SrcRect.Width(), SrcRect.Height(),
+ DestSize,
+ SrcSize,
+ *VertexShader,
+ View.StereoPass,
+ Context.HasHmdMesh(),
+ EDRF_UseTriangleOptimization);
+
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
+}
+
+FPooledRenderTargetDesc FRCPassPostProcessGatherDOFNearBorder::ComputeOutputDesc(EPassOutputId InPassOutputId) const
+{
+ FPooledRenderTargetDesc Ret = GetInput(ePId_Input0)->GetOutput()->RenderTargetDesc;
+
+ Ret.Reset();
+ // more precision for additive blending
+ Ret.Format = PF_FloatRGBA;
+
+ Ret.DebugName = TEXT("GatherDOFNearBorder");
+
+ return Ret;
+}
+
+// Returns quality level for GatherDOF
+static uint32 GetGatherDOFQuality()
+{
+ static const auto DofQuality = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.GatherDOF.Quality"));
+ return (DofQuality->GetValueOnRenderThread() > 0) ? 1 : 0;
+}
+
+// Returns sprite rotation for GatherDOF
+static float GetGatherDOFRotation()
+{
+ static const auto DofRotation = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.GatherDOF.Rotation"));
+ return DofRotation->GetValueOnRenderThread() / 180.0f * PI;
+}
+
+// Returns the number of edges for the bokeh shape
+static float GetGatherDOFEdgeCount()
+{
+ static const auto DofEdgeCount = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.GatherDOF.EdgeCount"));
+ return DofEdgeCount->GetValueOnRenderThread();
+}
+
+static float GetGatherDOFRadiusScale()
+{
+ static const auto RadiusScale = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.GatherDOF.RadiusScale"));
+ return RadiusScale->GetValueOnRenderThread() * 0.6f; // 0.46876f; // Allows keeping 1 as default CVar value
+}
+
+//----------------------------------------------
+// Blur one field
+//----------------------------------------------
+
+template <uint32 HighQuality>
+class PostProcessBokehDOFGatherBlurFarPS : public FGlobalShader
+{
+ DECLARE_SHADER_TYPE(PostProcessBokehDOFGatherBlurFarPS, Global);
+
+ static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
+ {
+ return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
+ }
+
+ static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
+ {
+ FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
+ OutEnvironment.SetDefine(TEXT("HIGH_QUALITY"), HighQuality);
+ }
+
+ /** Default constructor. */
+ PostProcessBokehDOFGatherBlurFarPS() {}
+
+public:
+ FPostProcessPassParameters PostprocessParameter;
+ FSceneTextureShaderParameters SceneTextureParameters;
+ FShaderParameter DepthOfFieldParams;
+ FShaderParameter KernelSize;
+
+ /** Initialization constructor. */
+ PostProcessBokehDOFGatherBlurFarPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
+ : FGlobalShader(Initializer)
+ {
+ PostprocessParameter.Bind(Initializer.ParameterMap);
+ SceneTextureParameters.Bind(Initializer);
+ DepthOfFieldParams.Bind(Initializer.ParameterMap, TEXT("DepthOfFieldParams"));
+ KernelSize.Bind(Initializer.ParameterMap, TEXT("KernelSize"));
+ }
+
+ // FShader interface.
+ virtual bool Serialize(FArchive& Ar) override
+ {
+ bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
+ Ar << PostprocessParameter << SceneTextureParameters << DepthOfFieldParams << KernelSize;
+ return bShaderHasOutdatedParameters;
+ }
+
+ void SetParameters(const FRenderingCompositePassContext& Context, float PixelKernelSize)
+ {
+ const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
+
+ FGlobalShader::SetParameters<FViewUniformShaderParameters>(Context.RHICmdList, ShaderRHI, Context.View.ViewUniformBuffer);
+
+ SceneTextureParameters.Set(Context.RHICmdList, ShaderRHI, Context.View.FeatureLevel, ESceneTextureSetupMode::All);
+ PostprocessParameter.SetPS(Context.RHICmdList, ShaderRHI, Context, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
+
+ {
+ FVector4 DepthOfFieldParamValues[2];
+ FRCPassPostProcessBokehDOF::ComputeDepthOfFieldParams(Context, DepthOfFieldParamValues);
+ SetShaderValueArray(Context.RHICmdList, ShaderRHI, DepthOfFieldParams, DepthOfFieldParamValues, 2);
+
+ FVector4 KernelSizeValue(PixelKernelSize * GetGatherDOFRadiusScale(), GetGatherDOFRotation(), GetGatherDOFEdgeCount(), 0);
+ SetShaderValue(Context.RHICmdList, ShaderRHI, KernelSize, KernelSizeValue);
+ }
+ }
+
+ static const TCHAR* GetSourceFilename()
+ {
+ return TEXT("/Engine/Private/PostProcessGatherDOF.usf");
+ }
+
+ static const TCHAR* GetFunctionName()
+ {
+ return TEXT("MainBlurFarPS");
+ }
+};
+
+// #define avoids a lot of code duplication
+#define VARIATION1(A) typedef PostProcessBokehDOFGatherBlurFarPS<A> PostProcessBokehDOFGatherBlurFarPS##A; \
+ IMPLEMENT_SHADER_TYPE2(PostProcessBokehDOFGatherBlurFarPS##A, SF_Pixel);
+
+VARIATION1(0) VARIATION1(1)
+#undef VARIATION1
+
+template <uint32 HighQuality>
+void FRCPassPostProcessGatherDOFBlurFar::SetShader(const FRenderingCompositePassContext& Context, float PixelKernelSize)
+{
+ FGraphicsPipelineStateInitializer GraphicsPSOInit;
+ Context.RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
+
+ // set the state
+ GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
+ GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
+ GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
+
+ // setup shader
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+ GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
+ GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
+ GraphicsPSOInit.PrimitiveType = PT_TriangleList;
+
+ {
+ TShaderMapRef<PostProcessBokehDOFGatherBlurFarPS<HighQuality> > PixelShader(Context.GetShaderMap());
+
+ GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
+ SetGraphicsPipelineState(Context.RHICmdList, GraphicsPSOInit);
+
+ VertexShader->SetParameters(Context);
+ PixelShader->SetParameters(Context, PixelKernelSize);
+ }
+}
+
+void FRCPassPostProcessGatherDOFBlurFar::Process(FRenderingCompositePassContext& Context)
+{
+ SCOPED_DRAW_EVENT(Context.RHICmdList, BokehGatherBlurFar);
+
+ const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
+
+ if (!InputDesc)
+ {
+ // input is not hooked up correctly
+ return;
+ }
+
+ const FViewInfo& View = Context.View;
+ const FSceneViewFamily& ViewFamily = *(View.Family);
+
+ FIntPoint SrcSize = InputDesc->Extent;
+ FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent;
+
+ // e.g. 4 means the input texture is 4x smaller than the buffer size
+ uint32 ScaleFactor = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+
+ FIntRect SrcRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleFactor);
+ FIntRect DestRect = SrcRect;
+
+ const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
+
+ SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef());
+
+ Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f);
+
+ uint32 ScaleToFullRes = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+ FIntRect LocalViewRect = View.ViewRect / ScaleToFullRes;
+ FIntPoint LocalViewSize = LocalViewRect.Size();
+ float PixelKernelSize = Context.View.FinalPostProcessSettings.DepthOfFieldMaxBokehSize / 100.0f * LocalViewSize.X;
+
+ if (GetGatherDOFQuality() > 0)
+ {
+ SetShader<1>(Context, PixelKernelSize);
+ }
+ else
+ {
+ SetShader<0>(Context, PixelKernelSize);
+ }
+
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+
+ DrawPostProcessPass(
+ Context.RHICmdList,
+ DestRect.Min.X, DestRect.Min.Y,
+ DestRect.Width(), DestRect.Height(),
+ SrcRect.Min.X, SrcRect.Min.Y,
+ SrcRect.Width(), SrcRect.Height(),
+ DestSize,
+ SrcSize,
+ *VertexShader,
+ View.StereoPass,
+ Context.HasHmdMesh(),
+ EDRF_UseTriangleOptimization);
+
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
+}
+
+FPooledRenderTargetDesc FRCPassPostProcessGatherDOFBlurFar::ComputeOutputDesc(EPassOutputId InPassOutputId) const
+{
+ FPooledRenderTargetDesc Ret = GetInput(ePId_Input0)->GetOutput()->RenderTargetDesc;
+
+ Ret.Reset();
+ // more precision for additive blending
+ Ret.Format = PF_FloatRGBA;
+
+ Ret.DebugName = TEXT("GatherDOFBlurFar");
+
+ return Ret;
+}
+
+
+//----------------------------------------------
+// Blur near field
+//----------------------------------------------
+
+template <uint32 HighQuality>
+class PostProcessBokehDOFGatherBlurNearPS : public FGlobalShader
+{
+ DECLARE_SHADER_TYPE(PostProcessBokehDOFGatherBlurNearPS, Global);
+
+ static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
+ {
+ return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
+ }
+
+ static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
+ {
+ FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
+ OutEnvironment.SetDefine(TEXT("HIGH_QUALITY"), HighQuality);
+ }
+
+ /** Default constructor. */
+ PostProcessBokehDOFGatherBlurNearPS() {}
+
+public:
+ FPostProcessPassParameters PostprocessParameter;
+ FSceneTextureShaderParameters SceneTextureParameters;
+ FShaderParameter DepthOfFieldParams;
+ FShaderParameter KernelSize;
+
+ /** Initialization constructor. */
+ PostProcessBokehDOFGatherBlurNearPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
+ : FGlobalShader(Initializer)
+ {
+ PostprocessParameter.Bind(Initializer.ParameterMap);
+ SceneTextureParameters.Bind(Initializer);
+ DepthOfFieldParams.Bind(Initializer.ParameterMap, TEXT("DepthOfFieldParams"));
+ KernelSize.Bind(Initializer.ParameterMap, TEXT("KernelSize"));
+ }
+
+ // FShader interface.
+ virtual bool Serialize(FArchive& Ar) override
+ {
+ bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
+ Ar << PostprocessParameter << SceneTextureParameters << DepthOfFieldParams << KernelSize;
+ return bShaderHasOutdatedParameters;
+ }
+
+ void SetParameters(const FRenderingCompositePassContext& Context, float PixelKernelSize)
+ {
+ const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
+
+ FGlobalShader::SetParameters<FViewUniformShaderParameters>(Context.RHICmdList, ShaderRHI, Context.View.ViewUniformBuffer);
+
+ SceneTextureParameters.Set(Context.RHICmdList, ShaderRHI, Context.View.FeatureLevel, ESceneTextureSetupMode::All);
+ PostprocessParameter.SetPS(Context.RHICmdList, ShaderRHI, Context, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
+
+ {
+ FVector4 DepthOfFieldParamValues[2];
+ FRCPassPostProcessBokehDOF::ComputeDepthOfFieldParams(Context, DepthOfFieldParamValues);
+ SetShaderValueArray(Context.RHICmdList, ShaderRHI, DepthOfFieldParams, DepthOfFieldParamValues, 2);
+
+ FVector4 KernelSizeValue(PixelKernelSize * GetGatherDOFRadiusScale(), GetGatherDOFRotation(), GetGatherDOFEdgeCount(), 0);
+ SetShaderValue(Context.RHICmdList, ShaderRHI, KernelSize, KernelSizeValue);
+ }
+ }
+
+ static const TCHAR* GetSourceFilename()
+ {
+ return TEXT("/Engine/Private/PostProcessGatherDOF.usf");
+ }
+
+ static const TCHAR* GetFunctionName()
+ {
+ return TEXT("MainBlurNearPS");
+ }
+};
+
+// #define avoids a lot of code duplication
+#define VARIATION1(A) typedef PostProcessBokehDOFGatherBlurNearPS<A> PostProcessBokehDOFGatherBlurNearPS##A; \
+ IMPLEMENT_SHADER_TYPE2(PostProcessBokehDOFGatherBlurNearPS##A, SF_Pixel);
+
+VARIATION1(0) VARIATION1(1)
+#undef VARIATION1
+
+template <uint32 HighQuality>
+void FRCPassPostProcessGatherDOFBlurNear::SetShader(const FRenderingCompositePassContext& Context, float PixelKernelSize)
+{
+ FGraphicsPipelineStateInitializer GraphicsPSOInit;
+ Context.RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
+
+ // set the state
+ GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
+ GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
+ GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
+
+ // setup shader
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+ GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
+ GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
+ GraphicsPSOInit.PrimitiveType = PT_TriangleList;
+
+ {
+ TShaderMapRef<PostProcessBokehDOFGatherBlurNearPS<HighQuality> > PixelShader(Context.GetShaderMap());
+
+ GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
+ SetGraphicsPipelineState(Context.RHICmdList, GraphicsPSOInit);
+
+ VertexShader->SetParameters(Context);
+ PixelShader->SetParameters(Context, PixelKernelSize);
+ }
+}
+
+void FRCPassPostProcessGatherDOFBlurNear::Process(FRenderingCompositePassContext& Context)
+{
+ SCOPED_DRAW_EVENT(Context.RHICmdList, BokehGatherBlurNear);
+
+ const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
+
+ if (!InputDesc)
+ {
+ // input is not hooked up correctly
+ return;
+ }
+
+ const FViewInfo& View = Context.View;
+ const FSceneViewFamily& ViewFamily = *(View.Family);
+
+ FIntPoint SrcSize = InputDesc->Extent;
+ FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent;
+
+ // e.g. 4 means the input texture is 4x smaller than the buffer size
+ uint32 ScaleFactor = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+
+ FIntRect SrcRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleFactor);
+ FIntRect DestRect = SrcRect;
+
+ const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
+
+ SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef());
+
+ Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f);
+
+ uint32 ScaleToFullRes = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+ FIntRect LocalViewRect = View.ViewRect / ScaleToFullRes;
+ FIntPoint LocalViewSize = LocalViewRect.Size();
+ float PixelKernelSize = Context.View.FinalPostProcessSettings.DepthOfFieldMaxBokehSize / 100.0f * LocalViewSize.X;
+
+ if (GetGatherDOFQuality() > 0)
+ {
+ SetShader<1>(Context, PixelKernelSize);
+ }
+ else
+ {
+ SetShader<0>(Context, PixelKernelSize);
+ }
+
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+
+ DrawPostProcessPass(
+ Context.RHICmdList,
+ DestRect.Min.X, DestRect.Min.Y,
+ DestRect.Width(), DestRect.Height(),
+ SrcRect.Min.X, SrcRect.Min.Y,
+ SrcRect.Width(), SrcRect.Height(),
+ DestSize,
+ SrcSize,
+ *VertexShader,
+ View.StereoPass,
+ Context.HasHmdMesh(),
+ EDRF_UseTriangleOptimization);
+
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
+}
+
+FPooledRenderTargetDesc FRCPassPostProcessGatherDOFBlurNear::ComputeOutputDesc(EPassOutputId InPassOutputId) const
+{
+ FPooledRenderTargetDesc Ret = GetInput(ePId_Input0)->GetOutput()->RenderTargetDesc;
+
+ Ret.Reset();
+ // more precision for additive blending
+ Ret.Format = PF_FloatRGBA;
+
+ Ret.DebugName = TEXT("GatherDOFBlurNear");
+
+ return Ret;
+}
+
+//----------------------------------------------
+// Floodfill gaps in the blur sampling
+//----------------------------------------------
+
+template <uint32 HighQuality>
+class PostProcessBokehDOFGatherFloodfillPS : public FGlobalShader
+{
+ DECLARE_SHADER_TYPE(PostProcessBokehDOFGatherFloodfillPS, Global);
+
+ static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
+ {
+ return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
+ }
+
+ static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
+ {
+ FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
+ OutEnvironment.SetDefine(TEXT("HIGH_QUALITY"), HighQuality & 0x1);
+ OutEnvironment.SetDefine(TEXT("COC_TILEMAP"), (HighQuality & 0x2)? 1 : 0);
+ }
+
+ /** Default constructor. */
+ PostProcessBokehDOFGatherFloodfillPS() {}
+
+public:
+ FPostProcessPassParameters PostprocessParameter;
+ FSceneTextureShaderParameters SceneTextureParameters;
+ FShaderParameter DepthOfFieldParams;
+ FShaderParameter KernelSize;
+
+ /** Initialization constructor. */
+ PostProcessBokehDOFGatherFloodfillPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
+ : FGlobalShader(Initializer)
+ {
+ PostprocessParameter.Bind(Initializer.ParameterMap);
+ SceneTextureParameters.Bind(Initializer);
+ DepthOfFieldParams.Bind(Initializer.ParameterMap, TEXT("DepthOfFieldParams"));
+ KernelSize.Bind(Initializer.ParameterMap, TEXT("KernelSize"));
+ }
+
+ // FShader interface.
+ virtual bool Serialize(FArchive& Ar) override
+ {
+ bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
+ Ar << PostprocessParameter << SceneTextureParameters << DepthOfFieldParams << KernelSize;
+ return bShaderHasOutdatedParameters;
+ }
+
+ void SetParameters(const FRenderingCompositePassContext& Context, float PixelKernelSize)
+ {
+ const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
+
+ FGlobalShader::SetParameters<FViewUniformShaderParameters>(Context.RHICmdList, ShaderRHI, Context.View.ViewUniformBuffer);
+
+ SceneTextureParameters.Set(Context.RHICmdList, ShaderRHI, Context.View.FeatureLevel, ESceneTextureSetupMode::All);
+ PostprocessParameter.SetPS(Context.RHICmdList, ShaderRHI, Context, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
+
+ {
+ FVector4 DepthOfFieldParamValues[2];
+ FRCPassPostProcessBokehDOF::ComputeDepthOfFieldParams(Context, DepthOfFieldParamValues);
+ SetShaderValueArray(Context.RHICmdList, ShaderRHI, DepthOfFieldParams, DepthOfFieldParamValues, 2);
+
+ FVector4 KernelSizeValue(PixelKernelSize * GetGatherDOFRadiusScale(), GetGatherDOFRotation(), GetGatherDOFEdgeCount(), 0);
+ SetShaderValue(Context.RHICmdList, ShaderRHI, KernelSize, KernelSizeValue);
+ }
+ }
+
+ static const TCHAR* GetSourceFilename()
+ {
+ return TEXT("/Engine/Private/PostProcessGatherDOF.usf");
+ }
+
+ static const TCHAR* GetFunctionName()
+ {
+ return TEXT("MainFloodfillPS");
+ }
+};
+
+// #define avoids a lot of code duplication
+#define VARIATION1(A) typedef PostProcessBokehDOFGatherFloodfillPS<A> PostProcessBokehDOFGatherFloodfillPS##A; \
+ IMPLEMENT_SHADER_TYPE2(PostProcessBokehDOFGatherFloodfillPS##A, SF_Pixel);
+
+VARIATION1(0) VARIATION1(1) VARIATION1(2) VARIATION1(3)
+#undef VARIATION1
+
+template <uint32 HighQuality>
+void FRCPassPostProcessGatherDOFFloodfill::SetShader(const FRenderingCompositePassContext& Context, float PixelKernelSize)
+{
+ FGraphicsPipelineStateInitializer GraphicsPSOInit;
+ Context.RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
+
+ // set the state
+ GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
+ GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
+ GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
+
+ // setup shader
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+ GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
+ GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
+ GraphicsPSOInit.PrimitiveType = PT_TriangleList;
+
+ {
+ TShaderMapRef<PostProcessBokehDOFGatherFloodfillPS<HighQuality> > PixelShader(Context.GetShaderMap());
+
+ GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
+ SetGraphicsPipelineState(Context.RHICmdList, GraphicsPSOInit);
+
+ VertexShader->SetParameters(Context);
+ PixelShader->SetParameters(Context, PixelKernelSize);
+ }
+}
+
+void FRCPassPostProcessGatherDOFFloodfill::Process(FRenderingCompositePassContext& Context)
+{
+ SCOPED_DRAW_EVENT(Context.RHICmdList, BokehGatherFloodfill);
+
+ const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
+
+ if (!InputDesc)
+ {
+ // input is not hooked up correctly
+ return;
+ }
+
+ const FViewInfo& View = Context.View;
+ const FSceneViewFamily& ViewFamily = *(View.Family);
+
+ FIntPoint SrcSize = InputDesc->Extent;
+ FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent;
+
+ // e.g. 4 means the input texture is 4x smaller than the buffer size
+ uint32 ScaleFactor = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+
+ FIntRect SrcRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleFactor);
+ FIntRect DestRect = SrcRect;
+
+ const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
+
+ SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef());
+
+ Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f);
+
+ uint32 ScaleToFullRes = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+ FIntRect LocalViewRect = View.ViewRect / ScaleToFullRes;
+ FIntPoint LocalViewSize = LocalViewRect.Size();
+ float PixelKernelSize = Context.View.FinalPostProcessSettings.DepthOfFieldMaxBokehSize / 100.0f * LocalViewSize.X;
+
+ if (GetGatherDOFQuality() > 0)
+ {
+ if (bUseCoCTilemap) SetShader<3>(Context, PixelKernelSize); else SetShader<1>(Context, PixelKernelSize);
+ }
+ else
+ {
+ //SetShader<0>(Context, PixelKernelSize);
+ if (bUseCoCTilemap) SetShader<2>(Context, PixelKernelSize); else SetShader<0>(Context, PixelKernelSize);
+ }
+
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+
+ DrawPostProcessPass(
+ Context.RHICmdList,
+ DestRect.Min.X, DestRect.Min.Y,
+ DestRect.Width(), DestRect.Height(),
+ SrcRect.Min.X, SrcRect.Min.Y,
+ SrcRect.Width(), SrcRect.Height(),
+ DestSize,
+ SrcSize,
+ *VertexShader,
+ View.StereoPass,
+ Context.HasHmdMesh(),
+ EDRF_UseTriangleOptimization);
+
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
+}
+
+FPooledRenderTargetDesc FRCPassPostProcessGatherDOFFloodfill::ComputeOutputDesc(EPassOutputId InPassOutputId) const
+{
+ FPooledRenderTargetDesc Ret = GetInput(ePId_Input0)->GetOutput()->RenderTargetDesc;
+
+ Ret.Reset();
+ // more precision for additive blending
+ Ret.Format = PF_FloatRGBA;
+
+ Ret.DebugName = TEXT("GatherDOFFloodfill");
+
+ return Ret;
+}
+
+//-----------------------------------------------------------
+// Recombine far + in-focus + near + separate translucency
+//-----------------------------------------------------------
+
+template <uint32 Method>
+class PostProcessBokehDOFGatherRecombinePS : public FGlobalShader
+{
+ DECLARE_SHADER_TYPE(PostProcessBokehDOFGatherRecombinePS, Global);
+
+ static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
+ {
+ return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
+ }
+
+ static int32 GetCombineFeatureMethod()
+ {
+ if (Method <= 2)
+ {
+ return Method;
+ }
+ else
+ {
+ return Method - 1;
+ }
+ }
+
+ static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
+ {
+ FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
+ OutEnvironment.SetDefine(TEXT("RECOMBINE_METHOD"), GetCombineFeatureMethod());
+ }
+
+ /** Default constructor. */
+ PostProcessBokehDOFGatherRecombinePS() {}
+
+public:
+ FPostProcessPassParameters PostprocessParameter;
+ FSceneTextureShaderParameters SceneTextureParameters;
+ FShaderParameter DepthOfFieldParams;
+ FShaderParameter SeparateTranslucencyResMultParam;
+ FShaderResourceParameter BilinearClampedSampler;
+ FShaderResourceParameter PointClampedSampler;
+
+ /** Initialization constructor. */
+ PostProcessBokehDOFGatherRecombinePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
+ : FGlobalShader(Initializer)
+ {
+ PostprocessParameter.Bind(Initializer.ParameterMap);
+ SceneTextureParameters.Bind(Initializer);
+ DepthOfFieldParams.Bind(Initializer.ParameterMap, TEXT("DepthOfFieldParams"));
+ SeparateTranslucencyResMultParam.Bind(Initializer.ParameterMap, TEXT("SeparateTranslucencyResMult"));
+ BilinearClampedSampler.Bind(Initializer.ParameterMap, TEXT("BilinearClampedSampler"));
+ PointClampedSampler.Bind(Initializer.ParameterMap, TEXT("PointClampedSampler"));
+ }
+
+ // FShader interface.
+ virtual bool Serialize(FArchive& Ar) override
+ {
+ bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
+ Ar << PostprocessParameter << SceneTextureParameters << DepthOfFieldParams << SeparateTranslucencyResMultParam << BilinearClampedSampler << PointClampedSampler;
+ return bShaderHasOutdatedParameters;
+ }
+
+ void SetParameters(const FRenderingCompositePassContext& Context)
+ {
+ const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
+
+ FGlobalShader::SetParameters<FViewUniformShaderParameters>(Context.RHICmdList, ShaderRHI, Context.View.ViewUniformBuffer);
+
+ SceneTextureParameters.Set(Context.RHICmdList, ShaderRHI, Context.View.FeatureLevel, ESceneTextureSetupMode::All);
+ PostprocessParameter.SetPS(Context.RHICmdList, ShaderRHI, Context, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
+
+ FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(Context.RHICmdList);
+ FIntPoint OutScaledSize;
+ float OutScale;
+ SceneContext.GetSeparateTranslucencyDimensions(OutScaledSize, OutScale);
+
+ SetShaderValue(Context.RHICmdList, ShaderRHI, SeparateTranslucencyResMultParam, FVector4(OutScale, OutScale, OutScale, OutScale));
+
+ {
+ FVector4 DepthOfFieldParamValues[2];
+ FRCPassPostProcessBokehDOF::ComputeDepthOfFieldParams(Context, DepthOfFieldParamValues);
+ SetShaderValueArray(Context.RHICmdList, ShaderRHI, DepthOfFieldParams, DepthOfFieldParamValues, 2);
+ }
+
+ SetSamplerParameter(Context.RHICmdList, ShaderRHI, BilinearClampedSampler, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
+ SetSamplerParameter(Context.RHICmdList, ShaderRHI, PointClampedSampler, TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI());
+ }
+
+ static const TCHAR* GetSourceFilename()
+ {
+ return TEXT("/Engine/Private/PostProcessGatherDOF.usf");
+ }
+
+ static const TCHAR* GetFunctionName()
+ {
+ return TEXT("MainRecombinePS");
+ }
+};
+
+// #define avoids a lot of code duplication
+#define VARIATION1(A) typedef PostProcessBokehDOFGatherRecombinePS<A> PostProcessBokehDOFGatherRecombinePS##A; \
+ IMPLEMENT_SHADER_TYPE2(PostProcessBokehDOFGatherRecombinePS##A, SF_Pixel);
+
+VARIATION1(1) VARIATION1(2)
+#undef VARIATION1
+
+template <uint32 Method>
+void FRCPassPostProcessGatherDOFRecombine::SetShader(const FRenderingCompositePassContext& Context)
+{
+ FGraphicsPipelineStateInitializer GraphicsPSOInit;
+ Context.RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
+
+ // set the state
+ GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
+ GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
+ GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
+
+ // setup shader
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+ GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
+ GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
+ GraphicsPSOInit.PrimitiveType = PT_TriangleList;
+
+ {
+ TShaderMapRef<PostProcessBokehDOFGatherRecombinePS<Method> > PixelShader(Context.GetShaderMap());
+
+ GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
+ SetGraphicsPipelineState(Context.RHICmdList, GraphicsPSOInit);
+
+ VertexShader->SetParameters(Context);
+ PixelShader->SetParameters(Context);
+ }
+}
+
+void FRCPassPostProcessGatherDOFRecombine::Process(FRenderingCompositePassContext& Context)
+{
+ SCOPED_DRAW_EVENT(Context.RHICmdList, BokehGatherRecombine);
+
+ uint32 Method = 1;
+
+ // Separate translucency provided
+ if (GetInput(ePId_Input3)->GetPass()) {
+ Method = 2;
+ }
+
+ const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
+
+ if (!InputDesc)
+ {
+ // input is not hooked up correctly
+ return;
+ }
+
+ const FViewInfo& View = Context.View;
+ const FSceneViewFamily& ViewFamily = *(View.Family);
+
+ FIntPoint SrcSize = InputDesc->Extent;
+ FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent;
+
+ // e.g. 4 means the input texture is 4x smaller than the buffer size
+ uint32 ScaleFactor = FSceneRenderTargets::Get(Context.RHICmdList).GetBufferSizeXY().X / SrcSize.X;
+
+ FIntRect SrcRect = FIntRect::DivideAndRoundUp(View.ViewRect, ScaleFactor);
+ FIntRect DestRect = SrcRect;
+
+ const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
+
+ SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef());
+
+ Context.SetViewportAndCallRHI(0, 0, 0.0f, PassOutputs[0].RenderTargetDesc.Extent.X, PassOutputs[0].RenderTargetDesc.Extent.Y, 1.0f);
+
+ switch (Method)
+ {
+ case 1: SetShader<1>(Context); break;
+ case 2: SetShader<2>(Context); break;
+ default:
+ check(0);
+ }
+
+ TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
+
+ DrawPostProcessPass(
+ Context.RHICmdList,
+ DestRect.Min.X, DestRect.Min.Y,
+ DestRect.Width(), DestRect.Height(),
+ SrcRect.Min.X, SrcRect.Min.Y,
+ SrcRect.Width(), SrcRect.Height(),
+ DestSize,
+ SrcSize,
+ *VertexShader,
+ View.StereoPass,
+ Context.HasHmdMesh(),
+ EDRF_UseTriangleOptimization);
+
+ Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
+}
+
+FPooledRenderTargetDesc FRCPassPostProcessGatherDOFRecombine::ComputeOutputDesc(EPassOutputId InPassOutputId) const
+{
+ FPooledRenderTargetDesc Ret = GetInput(ePId_Input0)->GetOutput()->RenderTargetDesc;
+
+ Ret.DebugName = TEXT("GatherDOFRecombine");
+
+ return Ret;
+}
diff --git a/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessBokehDOF.h b/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessBokehDOF.h
index 2ca0ad3aa06..0658da05229 100644
--- a/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessBokehDOF.h
+++ b/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessBokehDOF.h
@@ -85,3 +85,160 @@ private:
// border between front and back layer as we don't use viewports (only possible with GS)
const static uint32 SafetyBorder = 40;
};
+
+//------------------------------------------------------------------
+// GatherDOF
+//------------------------------------------------------------------
+
+// From full-res scene, extract near and far field + max NeaCoC tilemap
+// ePId_Input0: Color input
+// ePId_Input1: Depth input
+// ePId_Output0: Far field color + CoC
+// ePId_Output1: Near field CoC + CoC
+// ePId_Output2: Near Max CoC tilemap
+class FRCPassPostProcessGatherDOFSetup : public TRenderingCompositePassBase<2, 3>
+{
+public:
+ // interface FRenderingCompositePass ---------
+ virtual void Process(FRenderingCompositePassContext& Context) override;
+ virtual void Release() override { delete this; }
+ virtual FPooledRenderTargetDesc ComputeOutputDesc(EPassOutputId InPassOutputId) const override;
+};
+
+// Extract near field / far field from half-res input scene (from BokehDOFSetup)
+// ePId_Input0: Half res scene with depth in alpha
+// ePId_Output1: far field (color + CoC)
+// ePId_Output2: near field (color + CoC)
+class FRCPassPostProcessGatherDOFExtractArea : public TRenderingCompositePassBase<1, 3>
+{
+public:
+ // interface FRenderingCompositePass ---------
+
+ virtual void Process(FRenderingCompositePassContext& Context) override;
+ virtual void Release() override { delete this; }
+ virtual FPooledRenderTargetDesc ComputeOutputDesc(EPassOutputId InPassOutputId) const override;
+
+ //static void ComputeDepthOfFieldParams(const FRenderingCompositePassContext& Context, FVector4 Out[2]);
+};
+
+// Downscale max NearCoC tilemap
+class FRCPassPostProcessGatherDOFDownscaleTM : public TRenderingCompositePassBase<1, 1>
+{
+public:
+ // interface FRenderingCompositePass ---------
+
+ virtual void Process(FRenderingCompositePassContext& Context) override;
+ virtual void Release() override { delete this; }
+ virtual FPooledRenderTargetDesc ComputeOutputDesc(EPassOutputId InPassOutputId) const override;
+
+ static void ComputeDepthOfFieldParams(const FRenderingCompositePassContext& Context, FVector4 Out[2]);
+};
+
+// Halo for near field tilemap
+class FRCPassPostProcessGatherDOFNearHalo : public TRenderingCompositePassBase<1, 1>
+{
+public:
+ // interface FRenderingCompositePass ---------
+
+ virtual void Process(FRenderingCompositePassContext& Context) override;
+ virtual void Release() override { delete this; }
+ virtual FPooledRenderTargetDesc ComputeOutputDesc(EPassOutputId InPassOutputId) const override;
+
+ static void ComputeDepthOfFieldParams(const FRenderingCompositePassContext& Context, FVector4 Out[2]);
+};
+
+// Color border for near field
+class FRCPassPostProcessGatherDOFNearBorder : public TRenderingCompositePassBase<1, 1>
+{
+public:
+ // interface FRenderingCompositePass ---------
+
+ virtual void Process(FRenderingCompositePassContext& Context) override;
+ virtual void Release() override { delete this; }
+ virtual FPooledRenderTargetDesc ComputeOutputDesc(EPassOutputId InPassOutputId) const override;
+
+ static void ComputeDepthOfFieldParams(const FRenderingCompositePassContext& Context, FVector4 Out[2]);
+};
+
+// Blur far field with a flat-kernel following a bokeh shape
+// ePId_Input0: far field color + CoC
+// ePId_Output1: blurred field color + alpha
+class FRCPassPostProcessGatherDOFBlurFar : public TRenderingCompositePassBase<1, 1>
+{
+public:
+ // interface FRenderingCompositePass ---------
+
+ virtual void Process(FRenderingCompositePassContext& Context) override;
+ virtual void Release() override { delete this; }
+ virtual FPooledRenderTargetDesc ComputeOutputDesc(EPassOutputId InPassOutputId) const override;
+
+ static void ComputeDepthOfFieldParams(const FRenderingCompositePassContext& Context, FVector4 Out[2]);
+private:
+ template <uint32 HighQuality>
+ static void SetShader(const FRenderingCompositePassContext& Context, float PixelKernelSize);
+};
+
+// Blur near field with a flat-kernel following a bokeh shape
+// ePId_Input0: near field color + CoC
+// ePId_Output1: blurred field color + alpha
+class FRCPassPostProcessGatherDOFBlurNear : public TRenderingCompositePassBase<2, 1>
+{
+public:
+ // interface FRenderingCompositePass ---------
+
+ virtual void Process(FRenderingCompositePassContext& Context) override;
+ virtual void Release() override { delete this; }
+ virtual FPooledRenderTargetDesc ComputeOutputDesc(EPassOutputId InPassOutputId) const override;
+
+ static void ComputeDepthOfFieldParams(const FRenderingCompositePassContext& Context, FVector4 Out[2]);
+private:
+ template <uint32 HighQuality>
+ static void SetShader(const FRenderingCompositePassContext& Context, float PixelKernelSize);
+};
+
+// Floodfill sampling gaps from a previous blur
+// ePId_Input0: near field color + alpha
+// ePId_Input1: max NearCoC tilemap (optional)
+// ePId_Output1: blurred field color + alpha
+class FRCPassPostProcessGatherDOFFloodfill : public TRenderingCompositePassBase<2, 1>
+{
+public:
+ // interface FRenderingCompositePass ---------
+ FRCPassPostProcessGatherDOFFloodfill(bool useCoCTilemap)
+ {
+ bUseCoCTilemap = useCoCTilemap;
+ }
+
+ virtual void Process(FRenderingCompositePassContext& Context) override;
+ virtual void Release() override { delete this; }
+ virtual FPooledRenderTargetDesc ComputeOutputDesc(EPassOutputId InPassOutputId) const override;
+
+ static void ComputeDepthOfFieldParams(const FRenderingCompositePassContext& Context, FVector4 Out[2]);
+
+ bool bUseCoCTilemap;
+
+private:
+ template <uint32 HighQuality>
+ static void SetShader(const FRenderingCompositePassContext& Context, float PixelKernelSize);
+};
+
+// Recompose the layers far / in-focus / near
+// ePId_Input0: original full res scene
+// ePId_Input1: far field color + alpha
+// ePId_Input2: near field color + alpha
+// ePId_Input3: optional SeparateTranslucency
+// ePId_Output1: final full-res scene with DoF effect applied
+class FRCPassPostProcessGatherDOFRecombine : public TRenderingCompositePassBase<4, 1>
+{
+public:
+ // interface FRenderingCompositePass ---------
+
+ virtual void Process(FRenderingCompositePassContext& Context) override;
+ virtual void Release() override { delete this; }
+ virtual FPooledRenderTargetDesc ComputeOutputDesc(EPassOutputId InPassOutputId) const override;
+
+ static void ComputeDepthOfFieldParams(const FRenderingCompositePassContext& Context, FVector4 Out[2]);
+private:
+ template <uint32 Method>
+ static void SetShader(const FRenderingCompositePassContext& Context);
+};
diff --git a/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp b/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp
index 4fda27ad781..cba9e030402 100644
--- a/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp
+++ b/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp
@@ -218,6 +218,65 @@ TAutoConsoleVariable<int32> CVarUseDiaphragmDOF(
TEXT(" 1: Diaphragm DOF (default).\n"),
ECVF_RenderThreadSafe);
+TAutoConsoleVariable<int32> CVarUseGatherDOF(
+ TEXT("r.GatherDOF.Enable"),
+ 0,
+ TEXT("Enable/disable GatherDOF override for replacing BokehDOF."),
+ ECVF_RenderThreadSafe);
+
+TAutoConsoleVariable<int32> CVarGatherDOFQuality(
+ TEXT("r.GatherDOF.Quality"),
+ 1,
+ TEXT("High (1) or low (0) quality for GatherDOF. This can increase tap count and reduce noise."),
+ ECVF_RenderThreadSafe);
+
+TAutoConsoleVariable<float> CVarGatherDOFRadiusScale(
+ TEXT("r.GatherDOF.RadiusScale"),
+ 1.0f,
+ TEXT("Scale factor to apply to the CoC radius value."),
+ ECVF_RenderThreadSafe);
+
+TAutoConsoleVariable<float> CVarGatherDOFRotation(
+ TEXT("r.GatherDOF.Rotation"),
+ 0.0f,
+ TEXT("Rotation to apply to the polygonal bokeh shape, in degrees."),
+ ECVF_RenderThreadSafe);
+
+TAutoConsoleVariable<int32> CVarGatherDOFEdgeCount(
+ TEXT("r.GatherDOF.EdgeCount"),
+ 6,
+ TEXT("Number of edges in the polygonal bokeh shape. 0 for circle, 4 for square, 6 for hexagon..."),
+ ECVF_RenderThreadSafe);
+
+TAutoConsoleVariable<int32> CVarGatherDOFFloodfillFar(
+ TEXT("r.GatherDOF.Floodfill.Far"),
+ 1,
+ TEXT("Apply filter to smooth-out the far field."),
+ ECVF_RenderThreadSafe);
+
+TAutoConsoleVariable<int32> CVarGatherDOFFloodfillNear(
+ TEXT("r.GatherDOF.Floodfill.Near"),
+ 1,
+ TEXT("Apply filter to smooth-out the near field."),
+ ECVF_RenderThreadSafe);
+
+TAutoConsoleVariable<int32> CVarGatherDOFFarField(
+ TEXT("r.GatherDOF.FarField"),
+ 1,
+ TEXT("Apply DoF to the far field."),
+ ECVF_RenderThreadSafe);
+
+TAutoConsoleVariable<int32> CVarGatherDOFNearField(
+ TEXT("r.GatherDOF.NearField"),
+ 1,
+ TEXT("Apply DoF to the near field."),
+ ECVF_RenderThreadSafe);
+
+TAutoConsoleVariable<int32> CVarGatherDOFNoTAA(
+ TEXT("r.GatherDOF.NoTAA"),
+ 0,
+ TEXT("Even if Temporal-AA is globally on, do not use it for smoothing DoF. (faster)"),
+ ECVF_RenderThreadSafe);
IMPLEMENT_SHADER_TYPE(,FPostProcessVS,TEXT("/Engine/Private/PostProcessBloom.usf"),TEXT("MainPostprocessCommonVS"),SF_Vertex);
@@ -513,44 +572,189 @@ static void AddVisualizeBloomOverlay(FPostprocessContext& Context, FRenderingCom
static void AddPostProcessDepthOfFieldBokeh(FPostprocessContext& Context, FRenderingCompositeOutputRef& SeparateTranslucency, FRenderingCompositeOutputRef& VelocityInput)
{
- // downsample, mask out the in focus part, depth in alpha
- const bool bIsComputePass = ShouldDoComputePostProcessing(Context.View);
- FRenderingCompositePass* DOFSetup = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOFSetup(bIsComputePass));
- DOFSetup->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
- DOFSetup->SetInput(ePId_Input1, FRenderingCompositeOutputRef(Context.SceneDepth));
+ const float bUseGatherDOF = CVarUseGatherDOF.GetValueOnRenderThread();
- FSceneViewState* ViewState = (FSceneViewState*)Context.View.State;
-
- FRenderingCompositePass* DOFInputPass = DOFSetup;
- if( Context.View.AntiAliasingMethod == AAM_TemporalAA && ViewState )
- {
- FTAAPassParameters Parameters(Context.View);
- Parameters.Pass = ETAAPassConfig::LegacyDepthOfField;
- Parameters.bIsComputePass = bIsComputePass;
- Parameters.SetupViewRect(Context.View, /* ResolutionDivisor = */ 2);
+ if (!bUseGatherDOF)
+ { // Original BokehDOF flow
- FRenderingCompositePass* NodeTemporalAA = Context.Graph.RegisterPass( new(FMemStack::Get()) FRCPassPostProcessTemporalAA(
- Context, Parameters,
- ViewState->DOFHistory,
- &ViewState->DOFHistory) );
- NodeTemporalAA->SetInput( ePId_Input0, DOFSetup );
- NodeTemporalAA->SetInput( ePId_Input2, VelocityInput );
+ // downsample, mask out the in focus part, depth in alpha
+ const bool bIsComputePass = ShouldDoComputePostProcessing(Context.View);
+ FRenderingCompositePass* DOFSetup = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOFSetup(bIsComputePass));
+ DOFSetup->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
+ DOFSetup->SetInput(ePId_Input1, FRenderingCompositeOutputRef(Context.SceneDepth));
- DOFInputPass = NodeTemporalAA;
- ViewState->bDOFHistory = true;
+ FSceneViewState* ViewState = (FSceneViewState*)Context.View.State;
+
+ FRenderingCompositePass* DOFInputPass = DOFSetup;
+ if (Context.View.AntiAliasingMethod == AAM_TemporalAA && ViewState)
+ {
+ FTAAPassParameters Parameters(Context.View);
+ Parameters.Pass = ETAAPassConfig::LegacyDepthOfField;
+ Parameters.bIsComputePass = bIsComputePass;
+ Parameters.SetupViewRect(Context.View, /* ResolutionDivisor = */ 2);
+
+ FRenderingCompositePass* NodeTemporalAA = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessTemporalAA(
+ Context, Parameters,
+ ViewState->DOFHistory,
+ &ViewState->DOFHistory));
+ NodeTemporalAA->SetInput(ePId_Input0, DOFSetup);
+ NodeTemporalAA->SetInput(ePId_Input2, VelocityInput);
+
+ DOFInputPass = NodeTemporalAA;
+ ViewState->bDOFHistory = true;
+ }
+
+ FRenderingCompositePass* NodeBlurred = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOF());
+ NodeBlurred->SetInput(ePId_Input0, DOFInputPass);
+ NodeBlurred->SetInput(ePId_Input1, Context.SceneColor);
+ NodeBlurred->SetInput(ePId_Input2, Context.SceneDepth);
+
+ FRenderingCompositePass* NodeRecombined = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOFRecombine(bIsComputePass));
+ NodeRecombined->SetInput(ePId_Input0, Context.FinalOutput);
+ NodeRecombined->SetInput(ePId_Input1, NodeBlurred);
+ NodeRecombined->SetInput(ePId_Input2, SeparateTranslucency);
+
+ Context.FinalOutput = FRenderingCompositeOutputRef(NodeRecombined);
}
+ else {
- FRenderingCompositePass* NodeBlurred = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOF());
- NodeBlurred->SetInput(ePId_Input0, DOFInputPass);
- NodeBlurred->SetInput(ePId_Input1, Context.SceneColor);
- NodeBlurred->SetInput(ePId_Input2, Context.SceneDepth);
+ FSceneViewState* ViewState = (FSceneViewState*)Context.View.State;
- FRenderingCompositePass* NodeRecombined = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOFRecombine(bIsComputePass));
- NodeRecombined->SetInput(ePId_Input0, Context.FinalOutput);
- NodeRecombined->SetInput(ePId_Input1, NodeBlurred);
- NodeRecombined->SetInput(ePId_Input2, SeparateTranslucency);
+ bool doTAA = (Context.View.AntiAliasingMethod == AAM_TemporalAA && ViewState);
+ if (CVarGatherDOFNoTAA.GetValueOnRenderThread() == 1) doTAA = false;
+
+ // Gather DOF flow
+ FRenderingCompositePass* DOFinitialization;
+
+ if (!doTAA)
+ {
+ // Optimized path without support for TAA, prepare all fields in a single pass
+ // From full resolution scene and depth, extract at half-res near and far fields (color + CoC) and max NearCoC tilemap.
+ FRenderingCompositePass* DOFSetup = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGatherDOFSetup());
+ DOFSetup->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
+ DOFSetup->SetInput(ePId_Input1, FRenderingCompositeOutputRef(Context.SceneDepth));
+
+ DOFinitialization = DOFSetup;
+ }
+ else
+ {
+ // First setup phase with TAA, then field extraction
+
+ // downsample, mask out the in focus part, depth in alpha
+ const bool bIsComputePass = ShouldDoComputePostProcessing(Context.View);
+ FRenderingCompositePass* DOFSetup = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOFSetup(bIsComputePass));
+ DOFSetup->SetInput(ePId_Input0, FRenderingCompositeOutputRef(Context.FinalOutput));
+ DOFSetup->SetInput(ePId_Input1, FRenderingCompositeOutputRef(Context.SceneDepth));
+
+ FRenderingCompositePass* DOFInputPass = DOFSetup;
+
+ // Performs AA
+ FTAAPassParameters Parameters(Context.View);
+ Parameters.Pass = ETAAPassConfig::LegacyDepthOfField;
+ Parameters.bIsComputePass = bIsComputePass;
+ Parameters.SetupViewRect(Context.View, /* ResolutionDivisor = */ 2);
+ //Parameters.bUseFast = true; // not worth it
+
+ FRenderingCompositePass* NodeTemporalAA = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessTemporalAA(
+ Context, Parameters,
+ ViewState->DOFHistory,
+ &ViewState->DOFHistory));
+ NodeTemporalAA->SetInput(ePId_Input0, DOFSetup);
+ NodeTemporalAA->SetInput(ePId_Input2, VelocityInput);
+
+ DOFInputPass = NodeTemporalAA;
+ ViewState->bDOFHistory = true;
+
+
+ // After TAA is applied, separate near and far field (+ near min/max tile-map)
+ FRenderingCompositePass* GatherDOFSetup = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGatherDOFExtractArea());
+ GatherDOFSetup->SetInput(ePId_Input0, FRenderingCompositeOutputRef(DOFInputPass));
+
+ DOFinitialization = GatherDOFSetup;
+ }
+
+ FRenderingCompositeOutputRef DOFSetupFar(DOFinitialization, ePId_Output0);
+ FRenderingCompositeOutputRef DOFSetupNear(DOFinitialization, ePId_Output1);
+ FRenderingCompositeOutputRef DOFSetupNearTileMap(DOFinitialization, ePId_Output2);
+
+ //--------------
+ // Far field
+ //--------------
+
+ FRenderingCompositePass* FarFieldFinal = nullptr;
+
+ // Do we process far field at all
+ if (CVarGatherDOFFarField.GetValueOnRenderThread() == 1)
+ {
+ // Perform the multi-tap blur on far field
+ FRenderingCompositePass* FarBlurred = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGatherDOFBlurFar());
+ FarBlurred->SetInput(ePId_Input0, DOFSetupFar);
+
+ FarFieldFinal = FarBlurred;
+
+ if (CVarGatherDOFFloodfillFar.GetValueOnRenderThread() == 1) {
+ // Floodfill to fix sampling gaps of the previous pass
+ FRenderingCompositePass* FarFloodfilled = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGatherDOFFloodfill(false));
+ FarFloodfilled->SetInput(ePId_Input0, FarBlurred);
+
+ FarFieldFinal = FarFloodfilled;
+ }
+ }
+
+
+ //--------------
+ // Near field
+ //--------------
+
+ FRenderingCompositePass* NearFieldFinal = nullptr;
+
+ // Do we process near field at all
+ if (CVarGatherDOFNearField.GetValueOnRenderThread() == 1)
+ {
+ // Downscale max NearCoC tilemap. 1/4th of original scene res.
+ FRenderingCompositePass* DownscaledTileMap = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGatherDOFDownscaleTM());
+ DownscaledTileMap->SetInput(ePId_Input0, DOFSetupNearTileMap);
+
+ // Downscale tilemap. 1/8th of original scene res.
+ FRenderingCompositePass* DownscaledTileMap2 = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGatherDOFDownscaleTM());
+ DownscaledTileMap2->SetInput(ePId_Input0, DownscaledTileMap);
+
+ // Halo for near field (introduces bleeding in the max NearCoC tilemap, to avoid hard edges)
+ FRenderingCompositePass* NearHaloTileMap = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGatherDOFNearHalo());
+ NearHaloTileMap->SetInput(ePId_Input0, DownscaledTileMap2);
+
+ // Slight color bleeding around the border of near field (can't rely on premultiplied alpha)
+ FRenderingCompositePass* NearBorder = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGatherDOFNearBorder());
+ NearBorder->SetInput(ePId_Input0, DOFSetupNear);
+
+ // Blur the near field
+ FRenderingCompositePass* NearBlurred = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGatherDOFBlurNear());
+ NearBlurred->SetInput(ePId_Input0, NearBorder);
+ NearBlurred->SetInput(ePId_Input1, NearHaloTileMap);
+
+ NearFieldFinal = NearBlurred;
+
+ if (CVarGatherDOFFloodfillNear.GetValueOnRenderThread() == 1) {
+ // Floodfill to fix sampling gaps of the previous pass
+ FRenderingCompositePass* NearFloodfilled = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGatherDOFFloodfill(true));
+ NearFloodfilled->SetInput(ePId_Input0, NearBlurred);
+ NearFloodfilled->SetInput(ePId_Input1, NearHaloTileMap);
+
+ NearFieldFinal = NearFloodfilled;
+ }
+ }
+
+ // Combine the 3 layers: far + in-focus + near, and optionally separate translucency
+ FRenderingCompositePass* NodeRecombined = Context.Graph.RegisterPass(new(FMemStack::Get()) FRCPassPostProcessGatherDOFRecombine());
+ NodeRecombined->SetInput(ePId_Input0, Context.SceneColor);
+ if (CVarGatherDOFFarField.GetValueOnRenderThread() == 1) NodeRecombined->SetInput(ePId_Input1, FarFieldFinal);
+ if (CVarGatherDOFNearField.GetValueOnRenderThread() == 1) NodeRecombined->SetInput(ePId_Input2, NearFieldFinal);
+ NodeRecombined->SetInput(ePId_Input3, SeparateTranslucency);
+
+ Context.FinalOutput = FRenderingCompositeOutputRef(NodeRecombined);
+
+ }
- Context.FinalOutput = FRenderingCompositeOutputRef(NodeRecombined);
}
static bool AddPostProcessDepthOfFieldGaussian(FPostprocessContext& Context, FDepthOfFieldStats& Out, FRenderingCompositeOutputRef& VelocityInput, FRenderingCompositeOutputRef& SeparateTranslucencyRef)
--
2.14.1.windows.1