// Copyright Epic Games, Inc. All Rights Reserved. #include "Common.ush" #include "ScreenPass.ush" #include "PostProcessCommon.ush" #include "LensDistortion.ush" #ifndef MSAA_SAMPLE_COUNT #define MSAA_SAMPLE_COUNT 1 #endif #define CONFIG_SAMPLES 6 // K = Center of the nearest input pixel. // O = Center of the output pixel. // // | | // 0 | 1 | 2 // | | // | | // --------+-----------+-------- // | | // | O | // 3 | K | 5 // | | // | | // --------+-----------+-------- // | | // | | // 6 | 7 | 8 // | | // static const int2 kOffsets3x3[9] = { int2(-1, -1), int2(0, -1), int2(1, -1), int2(-1, 0), int2(0, 0), // K int2(1, 0), int2(-1, 1), int2(0, 1), int2(1, 1), }; // T = Center of the nearest top left pixel input pixel. // O = Center of the output pixel. // // | // T | . // | // O | // --------+-------- // | // | // . | . // | static const int2 Offsets2x2[4] = { int2( 0, 0), // T int2( 1, 0), int2( 0, 1), int2( 1, 1), }; // Indexes of the 3x3 square. static const uint kSquareIndexes3x3[9] = { 4u, 0u, 1u, 2u, 3u, 5u, 6u, 7u, 8u }; // Indexes of the offsets to have plus + shape. static const uint kPlusIndexes3x3[5] = { 4u, 1u, 3u, 5u, 7u }; Texture2D UndistortingDisplacementTexture; SamplerState UndistortingDisplacementSampler; #if MSAA_SAMPLE_COUNT > 1 Texture2DMS EditorPrimitivesColor; Texture2DMS EditorPrimitivesDepth; #else Texture2D EditorPrimitivesColor; Texture2D EditorPrimitivesDepth; #endif Texture2D ColorTexture; SamplerState ColorSampler; Texture2D DepthTexture; SamplerState DepthSampler; Texture2D VelocityTexture; SamplerState VelocitySampler; Texture2D PrevHistoryTexture; SamplerState PrevHistorySampler; SCREEN_PASS_TEXTURE_VIEWPORT(Color) SCREEN_PASS_TEXTURE_VIEWPORT(Depth) SCREEN_PASS_TEXTURE_VIEWPORT(PrevHistory) SCREEN_PASS_TEXTURE_VIEWPORT(History) FScreenTransform PassSvPositionToViewportUV; FScreenTransform ViewportUVToColorUV; FScreenTransform ViewportUVToDepthUV; float2 DepthTextureJitter; uint bCameraCut; uint bProcessAlpha; float4 SampleOffsetArray[MSAA_SAMPLE_COUNT]; uint bOpaqueEditorGizmo; uint bCompositeAnyNonNullDepth; void MainTemporalUpsampleDepthPS( float4 SvPosition : SV_POSITION, out float OutDeviceZ : SV_Target0) { float2 ViewportUV = (SvPosition.xy - History_ViewportMin) * History_ViewportSizeInverse; float2 ScreenPos = ViewportUVToScreenPos(ViewportUV); // Pixel coordinate of the center of output pixel O in the input viewport. float2 PPCo = ViewportUV * Depth_ViewportSize + DepthTextureJitter; // Pixel coordinate of the center of the nearest input pixel K. float2 PPCk = floor(PPCo) + 0.5; // Sample nearest depth float InputDeviceZ; { float2 SampleInputBufferUV = (Depth_ViewportMin + PPCk) * Depth_ExtentInverse; SampleInputBufferUV = clamp(SampleInputBufferUV, Depth_UVViewportBilinearMin, Depth_UVViewportBilinearMax); InputDeviceZ = Texture2DSampleLevel(DepthTexture, DepthSampler, SampleInputBufferUV, 0).r; } // Compute the screen position to sample in history float2 PrevScreenPos = ScreenPos; #if 1 { float4 ThisClip = float4(ScreenPos, InputDeviceZ, 1); float4 PrevClip = mul(ThisClip, View.ClipToPrevClip); PrevScreenPos = PrevClip.xy / PrevClip.w; #if 0 { float2 SampleInputBufferUV = (Depth_ViewportMin + PPCk) * Depth_ExtentInverse; SampleInputBufferUV = clamp(SampleInputBufferUV, Depth_UVViewportBilinearMin, Depth_UVViewportBilinearMax); float4 EncodedVelocity = VelocityTexture.SampleLevel(VelocitySampler, SampleInputBufferUV, 0); bool DynamicN = EncodedVelocity.x > 0.0; if(DynamicN) { PrevScreenPos = ScreenPos - DecodeVelocityFromTexture(EncodedVelocity).xy; } } #endif } #endif // Sample the history. float PrevHistoryDeviceZ; { float2 PrevHistoryBufferUV = (PrevHistory_ScreenPosToViewportScale * PrevScreenPos + PrevHistory_ScreenPosToViewportBias) * PrevHistory_ExtentInverse; PrevHistoryBufferUV = clamp(PrevHistoryBufferUV, PrevHistory_UVViewportBilinearMin, PrevHistory_UVViewportBilinearMax); PrevHistoryDeviceZ = Texture2DSampleLevel(PrevHistoryTexture, PrevHistorySampler, PrevHistoryBufferUV, 0).r; } // Correct the DeviceZ from the previous depth to the current depth. float CorrectedPrevHistoryDeviceZ; #if 1 { float PrevHistoryDepth = ConvertFromDeviceZ(PrevHistoryDeviceZ); const float3 PreViewTranslationOffset = DFFastSubtractDemote(PrimaryView.PreViewTranslation, PrimaryView.PrevPreViewTranslation); const float3 PrevHistoryTranslatedWorldPosition = mul(float4(PrevScreenPos * PrevHistoryDepth, PrevHistoryDepth, 1), View.PrevScreenToTranslatedWorld).xyz + PreViewTranslationOffset; float CorrectedPrevHistoryDepth = mul(float4(PrevHistoryTranslatedWorldPosition, 1.0), View.TranslatedWorldToView).z; CorrectedPrevHistoryDeviceZ = ConvertToDeviceZ(CorrectedPrevHistoryDepth); } #else { CorrectedPrevHistoryDeviceZ = PrevHistoryDeviceZ; } #endif // Replaces NaN from history with 0.0 CorrectedPrevHistoryDeviceZ = -min(-CorrectedPrevHistoryDeviceZ, 0.0); // Sample current frame bool bUpdateHistory; float InputMinDeviceZ; float InputMaxDeviceZ; { // Vector in pixel between pixel K -> O. float2 dKO = float2(PPCo - PPCk); bUpdateHistory = all(abs(dKO) < 0.5 * (Depth_ViewportSize.x * History_ViewportSizeInverse.x)) || bCameraCut != 0; InputMinDeviceZ = InputDeviceZ; InputMaxDeviceZ = InputDeviceZ; UNROLL_N(CONFIG_SAMPLES - 1) for (uint SampleId = 1; SampleId < CONFIG_SAMPLES; SampleId++) { float2 PixelOffset; #if CONFIG_SAMPLES == 9 { const uint SampleIdx = kSquareIndexes3x3[SampleId]; PixelOffset = kOffsets3x3[SampleIdx]; } #elif CONFIG_SAMPLES == 5 || CONFIG_SAMPLES == 6 { if (SampleId == 5) { PixelOffset = SignFastInt(float2(dKO)); } else { const uint SampleIdx = kPlusIndexes3x3[SampleId]; PixelOffset = kOffsets3x3[SampleIdx]; } } #else #error Unknown sample count #endif float2 SampleInputBufferUV = (Depth_ViewportMin + PPCk + PixelOffset) * Depth_ExtentInverse; SampleInputBufferUV = clamp(SampleInputBufferUV, Depth_UVViewportBilinearMin, Depth_UVViewportBilinearMax); float SampleDeviceZ = Texture2DSampleLevel(DepthTexture, DepthSampler, SampleInputBufferUV, 0).r; InputMinDeviceZ = min(InputMinDeviceZ, SampleDeviceZ); InputMaxDeviceZ = max(InputMaxDeviceZ, SampleDeviceZ); } } // Reject history based on neighborhood float ClampedHistoryDeviceZ = clamp(CorrectedPrevHistoryDeviceZ, InputMinDeviceZ, InputMaxDeviceZ); // Output history float FinalHistoryDeviceZ = bUpdateHistory ? InputDeviceZ : ClampedHistoryDeviceZ; OutDeviceZ = -min(-FinalHistoryDeviceZ, 0.0); } void MainPopulateSceneDepthPS( #if USE_MSAA && !COMPILER_HLSLCC sample noperspective float4 SvPosition : SV_POSITION, #else noperspective float4 SvPosition : SV_POSITION, #endif out float4 OutColor : SV_Target0, out float OutDepth : SV_DEPTH) { float2 ViewportUV = (SvPosition.xy - View.ViewRectMin.xy) * View.ViewSizeAndInvSize.zw; float2 DepthUV = clamp( (Depth_ViewportMin + ViewportUV * Depth_ViewportSize - DepthTextureJitter) * Depth_ExtentInverse, Depth_UVViewportBilinearMin, Depth_UVViewportBilinearMax); DepthUV = clamp(DepthUV, float2(0, 0), float2(1, 1)); #if FORCE_DEBUG_DRAW_COLOR //This extended pass is primarily to compensate for the lack of scene color output when compositing the debug draw primitives with SimpleElementShaders //Draw out the color whilst compensating for the viewport not necessarily starting at 0,0. //Does not use jitter for Scene Color as it is not upscaled for this pass float2 ColorUV = clamp( (Color_ViewportMin + ViewportUV * Color_ViewportSize) * Color_ExtentInverse, Color_UVViewportBilinearMin, Color_UVViewportBilinearMax); ColorUV = clamp(ColorUV, float2(0, 0), float2(1, 1)); OutColor = Texture2DSampleLevel(ColorTexture, ColorSampler, ColorUV, 0).rgba; #if METAL_MSAA_HDR_DECODE OutColor.rgb *= rcp(OutColor.r * (-0.299) + OutColor.g * (-0.587) + OutColor.b * (-0.114) + 1.0); #endif #else OutColor = 0.0; #endif OutDepth = Texture2DSampleLevel(DepthTexture, DepthSampler, DepthUV, 0).r; } float ComputeDepthMask(float SceneDeviceZ, float EditorDeviceZ) { // Soft Bias with SceneDeviceZ for best quality const float DeviceDepthFade = 0.00005f; return saturate(1.0f - (SceneDeviceZ - EditorDeviceZ) / DeviceDepthFade); } void ResolveEditorPrimitiveColor(int2 PixelPos, out float4 OutColor, out float OutDeviceZ) { // Furthest device Z or 0 if there is none. OutDeviceZ = 0; OutColor = 0; // Bring out of premultiplied. OutColor.rgb /= max(OutColor.a, 0.0001f); // Fix gamma. OutColor.rgb = pow(OutColor.rgb, 1.0f / 2.2f); // Bring back to premultiplied OutColor.rgb *= OutColor.a; } void MainCompositeEditorPrimitivesPS( float4 SvPosition : SV_POSITION, #if MSAA_DONT_RESOLVE && MSAA_SAMPLE_COUNT > 1 uint TargetSampleIndex : SV_SampleIndex, #endif out float4 OutColor : SV_Target0 #if WRITE_DEPTH ,out float OutDepth : SV_Depth #endif ) { const float2 DistortedViewportUV = ApplyScreenTransform(SvPosition.xy, PassSvPositionToViewportUV); const float2 UndistortedViewportUV = ApplyLensDistortionOnViewportUV(UndistortingDisplacementTexture, UndistortingDisplacementSampler, DistortedViewportUV); const float2 DistortedColorUV = ApplyScreenTransform(DistortedViewportUV, ViewportUVToColorUV); const float2 ColorUV = ApplyScreenTransform(UndistortedViewportUV, ViewportUVToColorUV); const float2 DepthUV = ApplyScreenTransform(UndistortedViewportUV, ViewportUVToDepthUV); const int2 ColorPixelPos = int2(ColorUV * Color_Extent); float4 SceneColor = Texture2DSample(ColorTexture, ColorSampler, DistortedColorUV); // Resolve editor primitive scene color and depth. float4 EditorPrimitiveColor; float DepthMask; #if WRITE_DEPTH OutDepth = 0; #endif #if MSAA_DONT_RESOLVE && MSAA_SAMPLE_COUNT > 1 { TargetSampleIndex = min(TargetSampleIndex, MSAA_SAMPLE_COUNT-1); const float2 SampleOffset = SampleOffsetArray[TargetSampleIndex].xy; float2 SampleDepthUV = (UndistortedViewportUV * Depth_ViewportSize + Depth_ViewportMin + SampleOffset - DepthTextureJitter) * Depth_ExtentInverse; SampleDepthUV = clamp(SampleDepthUV, Depth_UVViewportBilinearMin, Depth_UVViewportBilinearMax); EditorPrimitiveColor = EditorPrimitivesColor.Load(ColorPixelPos, TargetSampleIndex); float SampleEditorDeviceZ = EditorPrimitivesDepth.Load(ColorPixelPos, TargetSampleIndex).r; float SampleSceneDeviceZ = Texture2DSampleLevel(DepthTexture, DepthSampler, SampleDepthUV, 0).r; if (SampleEditorDeviceZ > 0.0 && bCompositeAnyNonNullDepth) { EditorPrimitiveColor.a = 1; } DepthMask = ComputeDepthMask(SampleSceneDeviceZ, SampleEditorDeviceZ); #if WRITE_DEPTH OutDepth = HAS_INVERTED_Z_BUFFER ? max(SampleSceneDeviceZ, SampleEditorDeviceZ) : min(SampleSceneDeviceZ, SampleEditorDeviceZ); #endif } #elif MSAA_SAMPLE_COUNT > 1 { EditorPrimitiveColor = 0.0; DepthMask = 0.0; float DepthMaskWeight = 0.0; UNROLL_N(MSAA_SAMPLE_COUNT) for (uint SampleIndex = 0; SampleIndex < MSAA_SAMPLE_COUNT; ++SampleIndex) { const float2 SampleOffset = SampleOffsetArray[SampleIndex].xy; float2 SampleDepthUV = (UndistortedViewportUV * Depth_ViewportSize + Depth_ViewportMin + SampleOffset - DepthTextureJitter) * Depth_ExtentInverse; SampleDepthUV = clamp(SampleDepthUV, Depth_UVViewportBilinearMin, Depth_UVViewportBilinearMax); float SampleSceneDeviceZ = Texture2DSampleLevel(DepthTexture, DepthSampler, SampleDepthUV, 0).r; float4 Sample = EditorPrimitivesColor.Load(ColorPixelPos, SampleIndex); float SampleEditorDeviceZ = EditorPrimitivesDepth.Load(ColorPixelPos, SampleIndex).r; // Check if any color was applied to this pixel. Note: This prevents actual black pixels from being visible. float Weight = Sample.a; float SampleDepthMask = ComputeDepthMask(SampleSceneDeviceZ, SampleEditorDeviceZ); if (SampleEditorDeviceZ > 0.0 && bCompositeAnyNonNullDepth) { Weight = 1; } FLATTEN if (Weight) { EditorPrimitiveColor += float4(Sample.rgb, Weight); DepthMask += SampleDepthMask * Weight; DepthMaskWeight += Weight; } #if WRITE_DEPTH OutDepth = HAS_INVERTED_Z_BUFFER ? max3(OutDepth, SampleSceneDeviceZ, SampleEditorDeviceZ) : min3(OutDepth, SampleSceneDeviceZ, SampleEditorDeviceZ); #endif } EditorPrimitiveColor.rgb /= MSAA_SAMPLE_COUNT; EditorPrimitiveColor.a /= MSAA_SAMPLE_COUNT; DepthMask *= DepthMaskWeight > 0 ? rcp(DepthMaskWeight) : 0.0; } #else { // De-jitter the sample position and make a filtered lookup - for planes this allows to reconstruct a much less jittery depth comparison function. It doesn't fix silhouettes, however. float2 SampleDepthUV = (UndistortedViewportUV * Depth_ViewportSize + Depth_ViewportMin - DepthTextureJitter) * Depth_ExtentInverse; SampleDepthUV = clamp(SampleDepthUV, Depth_UVViewportBilinearMin, Depth_UVViewportBilinearMax); float SceneDeviceZ = Texture2DSampleLevel(DepthTexture, DepthSampler, SampleDepthUV, 0).r; float EditorDeviceZ = EditorPrimitivesDepth.Load(int3(ColorPixelPos, 0)).r; EditorPrimitiveColor = EditorPrimitivesColor.Load(int3(ColorPixelPos, 0)); DepthMask = ComputeDepthMask(SceneDeviceZ, EditorDeviceZ); #if WRITE_DEPTH OutDepth = HAS_INVERTED_Z_BUFFER ? max(SceneDeviceZ, EditorDeviceZ) : min(SceneDeviceZ, EditorDeviceZ); #endif if (EditorDeviceZ > 0.0 && bCompositeAnyNonNullDepth) { EditorPrimitiveColor.a = 1; } } #endif // Fixes the gama of editor primitives { // Bring out of premultiplied. EditorPrimitiveColor.rgb /= max(EditorPrimitiveColor.a, 0.0001f); // Fix gamma. EditorPrimitiveColor.rgb = pow(EditorPrimitiveColor.rgb, 1.0f / 2.2f); // Bring back to premultiplied EditorPrimitiveColor.rgb *= EditorPrimitiveColor.a; } #if 1 // Don't modify based on occlusion static const float OccludedColorMultiplier = 1.0f; float DarkenMask = 1.0f; #else // Apply pattern mask based on occlusion static const float OccludedColorMultiplier = 0.7f; // Generate 2x2 square tiling pattern for foreground primitive that end up behind scene opaque primitives. float PatternMask = ((ColorPixelPos.x / 2 + ColorPixelPos.y / 2) % 2) * OccludedColorMultiplier; // These constants express the two opacity values we see when the primitive is hidden float LowContrastPatternMask = lerp(OccludedColorMultiplier, 1, PatternMask); LowContrastPatternMask = saturate(lerp(LowContrastPatternMask, 1, bOpaqueEditorGizmo)); float DarkenMask = lerp(LowContrastPatternMask, 1.0f, DepthMask); #endif // Blend editor primitives with scene color. OutColor.rgb = SceneColor.rgb * (1 - EditorPrimitiveColor.a) + EditorPrimitiveColor.rgb * DarkenMask; FLATTEN if (bProcessAlpha) { static const float OccludedAlphaMultiplier = OccludedColorMultiplier; float HiddenMask = lerp(OccludedAlphaMultiplier, 1.0f, DepthMask); OutColor.a = lerp(SceneColor.a, 1, EditorPrimitiveColor.a * DarkenMask * HiddenMask); } else { OutColor.a = 0; } }