├── Content ├── AreaTex.uasset └── SearchTex.uasset ├── Resources └── Icon128.png ├── README.md ├── Source └── SMAAPlugin │ ├── Public │ ├── SMAAPlugin.h │ ├── SMAADeveloperSettings.h │ └── SMAASceneExtension.h │ ├── Private │ ├── SMAADeveloperSettings.cpp │ ├── SMAAPlugin.cpp │ ├── PostProcess │ │ ├── PostProcessSMAA.h │ │ └── PostProcessSMAA.cpp │ └── SMAASceneExtension.cpp │ └── SMAAPlugin.Build.cs ├── SMAAPlugin.uplugin ├── Shaders └── Private │ ├── SMAA_BlendWeighting.usf │ ├── SMAA_NeighbourhoodBlend.usf │ ├── SMAA_T2XResolve.usf │ ├── SMAA_EdgeDetection.usf │ ├── SMAA_UE5.usf │ └── SMAAReference.usf └── .gitignore /Content/AreaTex.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XPOL555/SMAAPlugin/HEAD/Content/AreaTex.uasset -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XPOL555/SMAAPlugin/HEAD/Resources/Icon128.png -------------------------------------------------------------------------------- /Content/SearchTex.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XPOL555/SMAAPlugin/HEAD/Content/SearchTex.uasset -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SMAA Plugin 2 | SMAA plugin for Unreal Engine 5.4+, copied from code from this PR: [Enhanced Subpixel Morphological Anti-Aliasing for UE5](https://github.com/EpicGames/UnrealEngine/pull/11840/) 3 | 4 | Turn off antialiasing with the command `r.AntiAliasingMethod 0` and then enable SMAA with the command `r.SMAA 1` 5 | 6 | [Demonstration video](https://www.youtube.com/watch?v=UT8kHgAnibU) -------------------------------------------------------------------------------- /Source/SMAAPlugin/Public/SMAAPlugin.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FSMAASceneExtension; 9 | 10 | class FSMAAPluginModule : public IModuleInterface 11 | { 12 | public: 13 | 14 | /** IModuleInterface implementation */ 15 | virtual void StartupModule() override; 16 | virtual void ShutdownModule() override; 17 | 18 | void UpdateExtensions(); 19 | 20 | protected: 21 | TSharedPtr SMAASceneExtension; 22 | }; 23 | -------------------------------------------------------------------------------- /SMAAPlugin.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "SMAAPlugin", 6 | "Description": "", 7 | "Category": "Other", 8 | "CreatedBy": "EPICGameGuy", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "SMAAPlugin", 20 | "Type": "Runtime", 21 | "LoadingPhase": "PostConfigInit" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /Shaders/Private/SMAA_BlendWeighting.usf: -------------------------------------------------------------------------------- 1 | #include "/SMAAPlugin/Private/SMAA_UE5.usf" 2 | 3 | Texture2D AreaTexture; 4 | Texture2D SearchTexture; 5 | Texture2D InputEdges; 6 | RWTexture2D BlendTexture; 7 | float2 TemporalJitterPixels; 8 | float4 SubpixelWeights; 9 | 10 | // Custom, modified version 11 | [numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, THREADGROUP_SIZEZ)] 12 | void BlendWeightingCS(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID) 13 | { 14 | // Compute Texture Coord 15 | float2 ViewportUV = (float2(DispatchThreadId.xy) + 0.5f) * ViewportMetrics.xy; 16 | 17 | BlendTexture[DispatchThreadId.xy] = SMAABlendingWeightCalculationCS(ViewportUV, InputEdges, AreaTexture, SearchTexture, SubpixelWeights); 18 | } 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Source/SMAAPlugin/Private/SMAADeveloperSettings.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "SMAADeveloperSettings.h" 5 | 6 | USMAADeveloperSettings::USMAADeveloperSettings(const FObjectInitializer& ObjectInitializer) 7 | : Super(ObjectInitializer) 8 | { 9 | SMAAAreaTextureName = FSoftObjectPath("/SMAAPlugin/AreaTex.AreaTex"); 10 | SMAASearchTextureName = FSoftObjectPath("/SMAAPlugin/SearchTex.SearchTex"); 11 | } 12 | 13 | void USMAADeveloperSettings::LoadTextures() 14 | { 15 | if (SMAAAreaTexture == nullptr && !SMAAAreaTextureName.IsNull()) 16 | { 17 | SMAAAreaTexture = SMAAAreaTextureName.LoadSynchronous(); 18 | SMAAAreaTexture->AddToRoot(); 19 | } 20 | 21 | if (SMAASearchTexture == nullptr && !SMAASearchTextureName.IsNull()) 22 | { 23 | SMAASearchTexture = SMAASearchTextureName.LoadSynchronous(); 24 | SMAASearchTexture->AddToRoot(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Shaders/Private/SMAA_NeighbourhoodBlend.usf: -------------------------------------------------------------------------------- 1 | #include "SMAA_UE5.usf" 2 | 3 | Texture2D SceneColour; 4 | Texture2D InputBlend; 5 | Texture2D VelocityTexture; 6 | Texture2D SceneDepth; 7 | RWTexture2D FinalFrame; 8 | 9 | 10 | // Custom, modified version 11 | [numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, THREADGROUP_SIZEZ)] 12 | void NeighbourhoodBlendingCS(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID) 13 | { 14 | // Compute Texture Coord 15 | float2 ViewportUV = (float2(DispatchThreadId.xy) + 0.5f) * ViewportMetrics.xy; 16 | 17 | #if SMAA_REPROJECTION 18 | FinalFrame[DispatchThreadId.xy] = SMAANeighborhoodBlendingCS(ViewportUV, SceneColour, InputBlend, VelocityTexture, SceneDepth); 19 | #else 20 | FinalFrame[DispatchThreadId.xy] = SMAANeighborhoodBlendingCS(ViewportUV, SceneColour, InputBlend); 21 | #endif 22 | 23 | 24 | } 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Source/SMAAPlugin/SMAAPlugin.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SMAAPlugin : ModuleRules 6 | { 7 | public SMAAPlugin(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange( 12 | new string[] 13 | { 14 | "Core", 15 | "RHI", 16 | "RenderCore", 17 | "Renderer", 18 | "DeveloperSettings" 19 | } 20 | ); 21 | 22 | 23 | PrivateDependencyModuleNames.AddRange( 24 | new string[] 25 | { 26 | "CoreUObject", 27 | "Engine", 28 | "Slate", 29 | "SlateCore", 30 | "Projects", 31 | } 32 | ); 33 | 34 | PrivateIncludePaths.AddRange( 35 | new string[] { 36 | System.IO.Path.Combine(GetModuleDirectory("Renderer"), "Private"), 37 | } 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Shaders/Private/SMAA_T2XResolve.usf: -------------------------------------------------------------------------------- 1 | float ReprojectionWeight; 2 | #define SMAA_REPROJECTION_WEIGHT_SCALE ReprojectionWeight 3 | 4 | float TemporalHistoryBias; 5 | #define SMAA_REPROJECTION_WEIGHT_BASE TemporalHistoryBias 6 | 7 | #include "SMAA_UE5.usf" 8 | 9 | Texture2D CurrentSceneColour; 10 | Texture2D PastSceneColour; 11 | Texture2D VelocityTexture; 12 | Texture2D SceneDepth; 13 | RWTexture2D Resolved; 14 | 15 | // Custom, modified version 16 | [numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, THREADGROUP_SIZEZ)] void 17 | TemporalResolveCS(uint3 LocalThreadId 18 | : SV_GroupThreadID, uint3 WorkGroupId 19 | : SV_GroupID, uint3 DispatchThreadId 20 | : SV_DispatchThreadID) { 21 | 22 | // Compute Texture Coord 23 | float2 BufferUV = (float2(DispatchThreadId.xy) + 0.5f) * ViewportMetrics.xy; 24 | 25 | #if SMAA_REPROJECTION 26 | Resolved[DispatchThreadId.xy] = SMAAResolveCS( 27 | BufferUV, CurrentSceneColour, PastSceneColour, VelocityTexture, SceneDepth); 28 | #else 29 | Resolved[DispatchThreadId.xy] = 30 | SMAAResolveCS(BufferUV, CurrentSceneColour, PastSceneColour); 31 | #endif 32 | 33 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 user specific files 2 | .vs/ 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | *.ipa 33 | 34 | # These project files can be generated by the engine 35 | *.xcodeproj 36 | *.xcworkspace 37 | *.sln 38 | *.suo 39 | *.opensdf 40 | *.sdf 41 | 42 | # Precompiled Assets 43 | SourceArt/**/*.png 44 | SourceArt/**/*.tga 45 | 46 | # Binary Files 47 | Binaries/* 48 | Plugins/*/Binaries/* 49 | 50 | # Builds 51 | Build/* 52 | 53 | # Whitelist PakBlacklist-.txt files 54 | !Build/*/ 55 | Build/*/** 56 | !Build/*/PakBlacklist*.txt 57 | 58 | # Don't ignore icon files in Build 59 | !Build/**/*.ico 60 | 61 | # Built data for maps 62 | *_BuiltData.uasset 63 | 64 | # Configuration files generated by the Editor 65 | Saved/* 66 | 67 | # Compiled source files for the engine to use 68 | Intermediate/* 69 | Plugins/*/Intermediate/* 70 | 71 | # Cache files for the editor to use 72 | DerivedDataCache/* -------------------------------------------------------------------------------- /Source/SMAAPlugin/Public/SMAADeveloperSettings.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Engine/DeveloperSettings.h" 7 | #include "SMAADeveloperSettings.generated.h" 8 | 9 | class UTexture2D; 10 | 11 | /** 12 | * 13 | */ 14 | UCLASS(config = Engine, defaultconfig) 15 | class SMAAPLUGIN_API USMAADeveloperSettings : public UDeveloperSettings 16 | { 17 | GENERATED_BODY() 18 | 19 | public: 20 | USMAADeveloperSettings(const FObjectInitializer& ObjectInitializer); 21 | 22 | /** Area Texture used by SMAA. */ 23 | UPROPERTY() 24 | TObjectPtr SMAAAreaTexture; 25 | 26 | ///** Path of the Area Texture used by SMAA. */ 27 | UPROPERTY(globalconfig, EditAnywhere, Category = "SMAA", meta = (ConfigRestartRequired = true)) 28 | TSoftObjectPtr SMAAAreaTextureName; 29 | 30 | /** Search Texture used by SMAA. */ 31 | UPROPERTY() 32 | TObjectPtr SMAASearchTexture; 33 | 34 | ///** Path of the Search Texture used by SMAA. */ 35 | UPROPERTY(globalconfig, EditAnywhere, Category = "SMAA", meta=(ConfigRestartRequired = true)) 36 | TSoftObjectPtr SMAASearchTextureName; 37 | 38 | void LoadTextures(); 39 | 40 | static USMAADeveloperSettings* Get() { return GetMutableDefault(); } 41 | }; 42 | -------------------------------------------------------------------------------- /Shaders/Private/SMAA_EdgeDetection.usf: -------------------------------------------------------------------------------- 1 | float AdaptationFactor; 2 | #define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR AdaptationFactor 3 | 4 | #if SMAA_PREDICATION 5 | Texture2D Predicate; 6 | 7 | float PredicationThreshold; 8 | float PredicationScale; 9 | float PredicationStrength; 10 | 11 | #define SMAA_PREDICATION_THRESHOLD PredicationThreshold 12 | #define SMAA_PREDICATION_SCALE PredicationScale 13 | #define SMAA_PREDICATION_STRENGTH PredicationStrength 14 | #endif 15 | 16 | #include "SMAA_UE5.usf" 17 | 18 | Texture2D InputDepth; 19 | Texture2D InputSceneColor; 20 | RWTexture2D EdgesTexture; 21 | 22 | // Custom, modified version of EdgeDetection-PS and -VS 23 | [numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, THREADGROUP_SIZEZ)] 24 | void EdgeDetectionCS(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID) 25 | { 26 | // Compute Texture Coord 27 | float2 ViewportUV = (float2(DispatchThreadId.xy) + 0.5f) * ViewportMetrics.xy; 28 | float2 Edges; 29 | 30 | #if SMAA_EDMODE == 0 31 | // Depth 32 | Edges = SMAADepthEdgeDetectionCS(ViewportUV, InputDepth).xy; 33 | #elif SMAA_EDMODE == 1 34 | // Luminance 35 | 36 | #if SMAA_PREDICATION 37 | Edges = SMAALumaEdgeDetectionCS(ViewportUV, InputSceneColor, Predicate).xy; 38 | #else 39 | Edges = SMAALumaEdgeDetectionCS(ViewportUV, InputSceneColor).xy; 40 | #endif 41 | 42 | #elif SMAA_EDMODE == 2 || SMAA_EDMODE > 2 43 | // Colour, WorldNormal, GBufferB 44 | #if SMAA_PREDICATION 45 | Edges = SMAAColorEdgeDetectionCS(ViewportUV, InputSceneColor, Predicate).xy; 46 | #else 47 | Edges = SMAAColorEdgeDetectionCS(ViewportUV, InputSceneColor).xy; 48 | #endif 49 | #endif 50 | 51 | EdgesTexture[DispatchThreadId.xy] = float4(Edges.x, Edges.y, 1, 1); 52 | } 53 | -------------------------------------------------------------------------------- /Source/SMAAPlugin/Private/SMAAPlugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "SMAAPlugin.h" 4 | 5 | #include "SMAASceneExtension.h" 6 | #include "SMAADeveloperSettings.h" 7 | #include "Interfaces/IPluginManager.h" 8 | #include "Misc/Paths.h" 9 | #include "ShaderCore.h" 10 | 11 | #define LOCTEXT_NAMESPACE "FSMAAPluginModule" 12 | 13 | UE_DISABLE_OPTIMIZATION 14 | void FSMAAPluginModule::StartupModule() 15 | { 16 | FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("SMAAPlugin"))->GetBaseDir(), TEXT("Shaders")); 17 | AddShaderSourceDirectoryMapping(TEXT("/SMAAPlugin"), PluginShaderDir); 18 | 19 | FCoreDelegates::OnPostEngineInit.AddLambda([this]() 20 | { 21 | USMAADeveloperSettings::Get()->LoadTextures(); 22 | 23 | UpdateExtensions(); 24 | }); 25 | } 26 | 27 | void FSMAAPluginModule::ShutdownModule() 28 | { 29 | SMAASceneExtension.Reset(); 30 | } 31 | 32 | void FSMAAPluginModule::UpdateExtensions() 33 | { 34 | if (!SMAASceneExtension.IsValid()) 35 | { 36 | UTexture2D* AreaTexture = USMAADeveloperSettings::Get()->SMAAAreaTexture; 37 | UTexture2D* SearchTexture = USMAADeveloperSettings::Get()->SMAASearchTexture; 38 | FTexture2DResource* AreaTextureResource = nullptr; 39 | FTexture2DResource* SearchTextureResource = nullptr; 40 | 41 | if (ensure(AreaTexture)) 42 | { 43 | //AreaTexture->UpdateResource(); 44 | const auto TextureResource = AreaTexture->CreateResource(); 45 | 46 | if (ensure(TextureResource)) 47 | { 48 | AreaTextureResource = TextureResource->GetTexture2DResource(); 49 | } 50 | } 51 | 52 | if (ensure(SearchTexture)) 53 | { 54 | //SearchTexture->UpdateResource(); 55 | const auto TextureResource = SearchTexture->CreateResource(); 56 | 57 | if (ensure(TextureResource)) 58 | { 59 | SearchTextureResource = TextureResource->GetTexture2DResource(); 60 | } 61 | } 62 | 63 | SMAASceneExtension = FSceneViewExtensions::NewExtension(AreaTextureResource, SearchTextureResource); 64 | } 65 | } 66 | UE_ENABLE_OPTIMIZATION 67 | 68 | #undef LOCTEXT_NAMESPACE 69 | 70 | IMPLEMENT_MODULE(FSMAAPluginModule, SMAAPlugin) -------------------------------------------------------------------------------- /Source/SMAAPlugin/Private/PostProcess/PostProcessSMAA.h: -------------------------------------------------------------------------------- 1 | // [TEMPLATE] 2 | 3 | #pragma once 4 | 5 | #include "ScreenPass.h" 6 | 7 | //DEFINE_LOG_CATEGORY_STATIC(LogSMAA, Warning, All); 8 | 9 | enum class ESMAAEdgeDetectors : uint8 10 | { 11 | Depth, 12 | Luminance, 13 | Colour, 14 | Normal, 15 | 16 | MAX UMETA(HIDDEN) 17 | }; 18 | 19 | enum class ESMAAPreset : uint8 20 | { 21 | Low, 22 | Medium, 23 | High, 24 | Ultra, 25 | 26 | MAX UMETA(HIDDEN) 27 | }; 28 | 29 | enum class ESMAAPredicationTexture : uint8 30 | { 31 | None, 32 | Depth, 33 | WorldNormal, 34 | MRS, 35 | 36 | MAX UMETA(HIDDEN) 37 | }; 38 | 39 | ESMAAPreset GetSMAAPreset(); 40 | ESMAAEdgeDetectors GetSMAAEdgeDetectors(); 41 | ESMAAPredicationTexture GetPredicateSource(); 42 | 43 | uint8 GetSMAAMaxSearchSteps(); 44 | uint8 GetSMAAMaxDiagonalSearchSteps(); 45 | uint8 GetSMAACornerRounding(); 46 | 47 | float GetSMAAAdaptationFactor(); 48 | float GetSMAAReprojectionWeight(); 49 | float GetSMAAPredicationThreshold(); 50 | float GetSMAAPredicationScale(); 51 | float GetSMAAPredicationStrength(); 52 | float GetSMAATemporalHistoryBias(); 53 | 54 | 55 | struct FSMAAInputs 56 | { 57 | // [Optional] Render to the specified output. If invalid, a new texture is created and returned. 58 | FScreenPassRenderTarget OverrideOutput; 59 | 60 | // [Required] HDR scene color to filter. 61 | FScreenPassTexture SceneColor; 62 | 63 | // [Required] Depth Buffer. 64 | //FScreenPassTexture SceneDepth; 65 | 66 | // [Required] Scene Velocity. 67 | FScreenPassTexture SceneVelocity; 68 | 69 | // [Optional] Predicate 70 | //FScreenPassTexture PredicateTexture; 71 | 72 | 73 | //FScreenPassTexture WorldNormal; 74 | 75 | // SMAA quality. 76 | ESMAAPreset Quality = ESMAAPreset::MAX; 77 | 78 | // SMAA Edge Detectors 79 | // What data are we using to detect edges 80 | ESMAAEdgeDetectors EdgeMode = ESMAAEdgeDetectors::MAX; 81 | 82 | ESMAAPredicationTexture PredicationSource = ESMAAPredicationTexture::MAX; 83 | 84 | // SMAA Max Search Steps 85 | uint8 MaxSearchSteps = UINT8_MAX; 86 | 87 | // SMAA Max Diagonal Search Steps 88 | uint8 MaxDiagonalSearchSteps = UINT8_MAX; 89 | 90 | // SMAA Corner Roundness 91 | uint8 CornerRounding = UINT8_MAX; 92 | 93 | float AdaptationFactor = 2.f; 94 | 95 | // SMAA Reprojection 96 | float ReprojectionWeight = 30.f; 97 | 98 | float PredicationThreshold = 0.01f; 99 | 100 | float PredicationScale = 2.f; 101 | 102 | float PredicationStrength = 0.4f; 103 | 104 | float TemporalHistoryBias = 0.5f; 105 | 106 | }; 107 | 108 | FScreenPassTexture AddSMAAPasses(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSMAAInputs& Inputs, const struct FPostProcessMaterialInputs& InOutInputs, TSharedRef ViewData); 109 | 110 | FScreenPassTexture AddVisualizeSMAAPasses(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSMAAInputs& Inputs, const struct FPostProcessMaterialInputs& InOutInputs, TSharedRef ViewData); 111 | -------------------------------------------------------------------------------- /Source/SMAAPlugin/Public/SMAASceneExtension.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | #include "SceneViewExtension.h" 8 | 9 | // Structure in charge of storing all information about SMAA's history. 10 | struct SMAAPLUGIN_API FSMAAHistory 11 | { 12 | TRefCountPtr PastFrame; 13 | 14 | // Reference size of RT. Might be different than RT's actual size to handle down res. 15 | FIntPoint ReferenceBufferSize; 16 | 17 | // Viewport coordinate of the history in RT according to ReferenceBufferSize. 18 | FIntRect ViewportRect; 19 | 20 | void SafeRelease() 21 | { 22 | *this = FSMAAHistory(); 23 | } 24 | 25 | bool IsValid() const 26 | { 27 | return PastFrame.IsValid(); 28 | } 29 | 30 | //uint64 GetGPUSizeBytes(bool bLogSizes) const 31 | //{ 32 | // return GetRenderTargetGPUSizeBytes(PastFrame, bLogSizes); 33 | //} 34 | }; 35 | 36 | struct SMAAPLUGIN_API FSMAAViewData : public TSharedFromThis 37 | { 38 | // SMAA Specific Textures 39 | const FTexture2DResource* SMAAAreaTexture; 40 | const FTexture2DResource* SMAASearchTexture; 41 | 42 | int32 JitterIndex; 43 | FSMAAHistory SMAAHistory; 44 | 45 | virtual ~FSMAAViewData() {}; 46 | }; 47 | 48 | /** 49 | * 50 | */ 51 | class SMAAPLUGIN_API FSMAASceneExtension : public FSceneViewExtensionBase 52 | { 53 | public: 54 | FSMAASceneExtension(const FAutoRegister& AutoReg, FTexture2DResource* SMAAAreaTexture, FTexture2DResource* SMAASearchTexture); 55 | 56 | /** 57 | * Called on game thread when creating the view family. 58 | */ 59 | virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) {}; 60 | 61 | /** 62 | * Called on game thread when creating the view. 63 | */ 64 | virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override; 65 | 66 | /** 67 | * Called on game thread when view family is about to be rendered. 68 | */ 69 | virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) {}; 70 | 71 | virtual void PreRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView) override; 72 | 73 | /** 74 | * This will be called at the beginning of post processing to make sure that each view extension gets a chance to subscribe to an after pass event. 75 | */ 76 | virtual void SubscribeToPostProcessingPass(EPostProcessingPass Pass, FAfterPassCallbackDelegateArray& InOutPassCallbacks, bool bIsPassEnabled); 77 | 78 | TSharedPtr GetOrCreateViewData(const FSceneView& InView); 79 | 80 | virtual bool IsActiveThisFrame_Internal(const FSceneViewExtensionContext& Context) const override; 81 | 82 | protected: 83 | virtual FScreenPassTexture PostProcessPass_RenderThread( 84 | FRDGBuilder& GraphBuilder, 85 | const FSceneView& View, 86 | const FPostProcessMaterialInputs& InOutInputs, 87 | EPostProcessingPass Pass 88 | ); 89 | 90 | // SMAA Specific Textures 91 | FTexture2DResource* SMAAAreaTexture; 92 | FTexture2DResource* SMAASearchTexture; 93 | 94 | TMap> ViewDataMap; 95 | 96 | void ApplyJitter(FViewInfo& View, FSceneViewState* ViewState, FIntRect ViewRect, TSharedRef ViewData); 97 | }; 98 | -------------------------------------------------------------------------------- /Source/SMAAPlugin/Private/SMAASceneExtension.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "SMAASceneExtension.h" 5 | 6 | #include "SceneView.h" 7 | #include "ScreenPass.h" 8 | #include "CommonRenderResources.h" 9 | #include "PostProcess/PostProcessing.h" 10 | #include "PostProcess/PostProcessMaterial.h" 11 | #include "ScenePrivate.h" 12 | #include "Engine/TextureRenderTarget2D.h" 13 | #include "DynamicResolutionState.h" 14 | #include "FXRenderingUtils.h" 15 | #include "Rendering/Texture2DResource.h" 16 | 17 | #include "PostProcess/PostProcessSMAA.h" 18 | 19 | TAutoConsoleVariable CVarSMAAEnabled( 20 | TEXT("r.SMAA"), 0, 21 | TEXT(" 0 - off\n") 22 | TEXT(" 1 - on"), 23 | ECVF_RenderThreadSafe); 24 | 25 | TAutoConsoleVariable CVarSMAAVisualizeEnabled( 26 | TEXT("r.SMAA.Visualize"), 0, 27 | TEXT(" 0 - off\n") 28 | TEXT(" 1 - on"), 29 | ECVF_RenderThreadSafe); 30 | 31 | FSMAASceneExtension::FSMAASceneExtension(const FAutoRegister& AutoReg, FTexture2DResource* InSMAAAreaTexture, FTexture2DResource* InSMAASearchTexture) 32 | : FSceneViewExtensionBase(AutoReg) 33 | , SMAAAreaTexture(InSMAAAreaTexture) 34 | , SMAASearchTexture(InSMAASearchTexture) 35 | { 36 | //check(SMAAAreaTexture) 37 | } 38 | 39 | void FSMAASceneExtension::SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) 40 | { 41 | GetOrCreateViewData(InView); 42 | 43 | //check(InView.bIsViewInfo); 44 | //GEngine->AddOnScreenDebugMessage(-1, 0.f, FColor::Red, FString::Printf(L"Jitter index: %d", ((FViewInfo&)InView).TemporalJitterIndex)); 45 | 46 | //InView.ViewMatrices.HackAddTemporalAAProjectionJitter(FVector2D(1.f, 1.f)); 47 | } 48 | 49 | TSharedPtr FSMAASceneExtension::GetOrCreateViewData(const FSceneView& InView) 50 | { 51 | if (InView.State == nullptr) 52 | { 53 | return nullptr; 54 | } 55 | 56 | const uint32 Index = InView.State->GetViewKey(); 57 | TSharedPtr& ViewData = ViewDataMap.FindOrAdd(Index); 58 | if (!ViewData.IsValid()) 59 | { 60 | ViewData = MakeShared(); 61 | ViewData->SMAAAreaTexture = SMAAAreaTexture; 62 | ViewData->SMAASearchTexture = SMAASearchTexture; 63 | ViewData->JitterIndex = 0; 64 | } 65 | return ViewData; 66 | } 67 | 68 | bool FSMAASceneExtension::IsActiveThisFrame_Internal(const FSceneViewExtensionContext& Context) const 69 | { 70 | return CVarSMAAEnabled.GetValueOnAnyThread() == 1; 71 | } 72 | 73 | void FSMAASceneExtension::PreRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView) 74 | { 75 | check(InView.bIsViewInfo); 76 | 77 | FViewInfo& View = (FViewInfo&)InView; 78 | if (InView.State == nullptr) 79 | { 80 | return; 81 | } 82 | FSceneViewState* ViewState = InView.State->GetConcreteViewState(); 83 | 84 | ApplyJitter(View, ViewState, InView.UnconstrainedViewRect, GetOrCreateViewData(InView).ToSharedRef()); 85 | } 86 | 87 | void FSMAASceneExtension::SubscribeToPostProcessingPass(EPostProcessingPass Pass, FAfterPassCallbackDelegateArray& InOutPassCallbacks, bool bIsPassEnabled) 88 | { 89 | if (Pass == EPostProcessingPass::FXAA) 90 | { 91 | InOutPassCallbacks.Add(FAfterPassCallbackDelegate::CreateRaw(this, &FSMAASceneExtension::PostProcessPass_RenderThread, Pass)); 92 | } 93 | } 94 | 95 | FScreenPassTexture FSMAASceneExtension::PostProcessPass_RenderThread(FRDGBuilder& GraphBuilder, const FSceneView& View, const FPostProcessMaterialInputs& InOutInputs, EPostProcessingPass Pass) 96 | { 97 | InOutInputs.Validate(); 98 | 99 | SMAAAreaTexture->InitRHI(GetImmediateCommandList_ForRenderCommand()); 100 | SMAASearchTexture->InitRHI(GetImmediateCommandList_ForRenderCommand()); 101 | 102 | auto& SceneTextureParameters = InOutInputs.SceneTextures.SceneTextures; 103 | 104 | { 105 | auto PredicateSource = GetPredicateSource(); 106 | 107 | FSMAAInputs PassInputs; 108 | //PassSequence.AcceptOverrideIfLastPass(EPass::SMAA, PassInputs.OverrideOutput); 109 | PassInputs.SceneColor = FScreenPassTexture(InOutInputs.GetInput(EPostProcessMaterialInput::SceneColor)); 110 | PassInputs.SceneVelocity = FScreenPassTexture(InOutInputs.GetInput(EPostProcessMaterialInput::Velocity)); 111 | PassInputs.Quality = GetSMAAPreset(); 112 | PassInputs.EdgeMode = GetSMAAEdgeDetectors(); 113 | PassInputs.PredicationSource = PredicateSource; 114 | PassInputs.MaxSearchSteps = GetSMAAMaxSearchSteps(); 115 | PassInputs.MaxDiagonalSearchSteps = GetSMAAMaxDiagonalSearchSteps(); 116 | PassInputs.CornerRounding = GetSMAACornerRounding(); 117 | PassInputs.AdaptationFactor = GetSMAAAdaptationFactor(); 118 | PassInputs.ReprojectionWeight = GetSMAAReprojectionWeight(); 119 | PassInputs.PredicationThreshold = GetSMAAPredicationThreshold(); 120 | PassInputs.PredicationScale = GetSMAAPredicationScale(); 121 | PassInputs.PredicationStrength = GetSMAAPredicationStrength(); 122 | PassInputs.TemporalHistoryBias = GetSMAATemporalHistoryBias(); 123 | 124 | check(View.bIsViewInfo); 125 | 126 | auto ViewData = GetOrCreateViewData(View); 127 | if (!ViewData.IsValid()) 128 | { 129 | return FScreenPassTexture(InOutInputs.GetInput(EPostProcessMaterialInput::SceneColor)); 130 | } 131 | 132 | if (CVarSMAAVisualizeEnabled.GetValueOnAnyThread() == 1) 133 | { 134 | auto SceneColorSlice = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, AddVisualizeSMAAPasses(GraphBuilder, (const FViewInfo&)View, PassInputs, InOutInputs, ViewData.ToSharedRef())); 135 | return FScreenPassTexture(SceneColorSlice); 136 | } 137 | else 138 | { 139 | auto SceneColorSlice = FScreenPassTextureSlice::CreateFromScreenPassTexture(GraphBuilder, AddSMAAPasses(GraphBuilder, (const FViewInfo&)View, PassInputs, InOutInputs, ViewData.ToSharedRef())); 140 | return FScreenPassTexture(SceneColorSlice); 141 | } 142 | } 143 | 144 | return FScreenPassTexture(InOutInputs.GetInput(EPostProcessMaterialInput::SceneColor)); 145 | } 146 | 147 | void FSMAASceneExtension::ApplyJitter(FViewInfo& View, FSceneViewState* ViewState, FIntRect ViewRect, TSharedRef ViewData) 148 | { 149 | float EffectivePrimaryResolutionFraction = 1.f;// float(ViewRect.Width()) / float(View.GetSecondaryViewRectSize().X); 150 | 151 | // Compute number of TAA samples. 152 | int32 TemporalAASamples = 2; 153 | { 154 | //if (TAAConfig == EMainTAAPassConfig::TSR) 155 | //{ 156 | // // Force the number of AA sample to make sure the quality doesn't get 157 | // // compromised by previously set settings for Gen4 TAA 158 | // TemporalAASamples = 8; 159 | //} 160 | //else 161 | //{ 162 | // TemporalAASamples = FMath::Clamp(CVarTemporalAASamplesValue, 1, 255); 163 | //} 164 | 165 | //if (bTemporalUpsampling) 166 | //{ 167 | // // When doing TAA upsample with screen percentage < 100%, we need extra temporal samples to have a 168 | // // constant temporal sample density for final output pixels to avoid output pixel aligned converging issues. 169 | // TemporalAASamples = FMath::RoundToInt(float(TemporalAASamples) * FMath::Max(1.f, 1.f / (EffectivePrimaryResolutionFraction * EffectivePrimaryResolutionFraction))); 170 | //} 171 | //else if (CVarTemporalAASamplesValue == 5) 172 | //{ 173 | // TemporalAASamples = 4; 174 | //} 175 | 176 | // Use immediately higher prime number to break up coherence between the TAA jitter sequence and any 177 | // other random signal that are power of two of View.StateFrameIndex 178 | //if (TAAConfig == EMainTAAPassConfig::TSR) 179 | //{ 180 | // static const uint8 kFirstPrimeNumbers[] = { 181 | // 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 182 | // 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 183 | // 211, 223, 227, 229, 233, 239, 241, 251, 184 | // }; 185 | 186 | // for (int32 PrimeNumberId = FMath::Max(4, (TemporalAASamples - 1) / 5); PrimeNumberId < UE_ARRAY_COUNT(kFirstPrimeNumbers); PrimeNumberId++) 187 | // { 188 | // if (int32(kFirstPrimeNumbers[PrimeNumberId]) >= TemporalAASamples) 189 | // { 190 | // TemporalAASamples = int32(kFirstPrimeNumbers[PrimeNumberId]); 191 | // break; 192 | // } 193 | // } 194 | //} 195 | } 196 | 197 | // Compute the new sample index in the temporal sequence. 198 | int32 TemporalSampleIndex = ViewData->JitterIndex + 1; 199 | if (TemporalSampleIndex >= TemporalAASamples || View.bCameraCut) 200 | { 201 | TemporalSampleIndex = 0; 202 | } 203 | 204 | //#if !UE_BUILD_SHIPPING 205 | // if (CVarTAADebugOverrideTemporalIndex.GetValueOnRenderThread() >= 0) 206 | // { 207 | // TemporalSampleIndex = CVarTAADebugOverrideTemporalIndex.GetValueOnRenderThread(); 208 | // } 209 | //#endif 210 | 211 | // Updates view state. 212 | //if (!View.bStatePrevViewInfoIsReadOnly)// && !bFreezeTemporalSequences) 213 | { 214 | ViewState->TemporalAASampleIndex = TemporalSampleIndex; 215 | ViewData->JitterIndex = TemporalSampleIndex; 216 | } 217 | 218 | // Choose sub pixel sample coordinate in the temporal sequence. 219 | float SampleX, SampleY; 220 | //if (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::TemporalUpscale) 221 | //{ 222 | // // Uniformly distribute temporal jittering in [-.5; .5], because there is no longer any alignement of input and output pixels. 223 | // SampleX = Halton(TemporalSampleIndex + 1, 2) - 0.5f; 224 | // SampleY = Halton(TemporalSampleIndex + 1, 3) - 0.5f; 225 | 226 | // View.MaterialTextureMipBias = -(FMath::Max(-FMath::Log2(EffectivePrimaryResolutionFraction), 0.0f)) + CVarMinAutomaticViewMipBiasOffset.GetValueOnRenderThread(); 227 | // View.MaterialTextureMipBias = FMath::Max(View.MaterialTextureMipBias, CVarMinAutomaticViewMipBias.GetValueOnRenderThread()); 228 | //} 229 | //else if (CVarTemporalAASamplesValue == 2) 230 | { 231 | // 2xMSAA 232 | // Pattern docs: http://msdn.microsoft.com/en-us/library/windows/desktop/ff476218(v=vs.85).aspx 233 | // N. 234 | // .S 235 | float SamplesX[] = { -4.0f / 16.0f, 4.0 / 16.0f }; 236 | float SamplesY[] = { -4.0f / 16.0f, 4.0 / 16.0f }; 237 | check(TemporalAASamples == UE_ARRAY_COUNT(SamplesX)); 238 | SampleX = SamplesX[TemporalSampleIndex]; 239 | SampleY = SamplesY[TemporalSampleIndex]; 240 | } 241 | //else if (CVarTemporalAASamplesValue == 3) 242 | //{ 243 | // // 3xMSAA 244 | // // A.. 245 | // // ..B 246 | // // .C. 247 | // // Rolling circle pattern (A,B,C). 248 | // float SamplesX[] = { -2.0f / 3.0f, 2.0 / 3.0f, 0.0 / 3.0f }; 249 | // float SamplesY[] = { -2.0f / 3.0f, 0.0 / 3.0f, 2.0 / 3.0f }; 250 | // check(TemporalAASamples == UE_ARRAY_COUNT(SamplesX)); 251 | // SampleX = SamplesX[TemporalSampleIndex]; 252 | // SampleY = SamplesY[TemporalSampleIndex]; 253 | //} 254 | //else if (CVarTemporalAASamplesValue == 4) 255 | //{ 256 | // // 4xMSAA 257 | // // Pattern docs: http://msdn.microsoft.com/en-us/library/windows/desktop/ff476218(v=vs.85).aspx 258 | // // .N.. 259 | // // ...E 260 | // // W... 261 | // // ..S. 262 | // // Rolling circle pattern (N,E,S,W). 263 | // float SamplesX[] = { -2.0f / 16.0f, 6.0 / 16.0f, 2.0 / 16.0f, -6.0 / 16.0f }; 264 | // float SamplesY[] = { -6.0f / 16.0f, -2.0 / 16.0f, 6.0 / 16.0f, 2.0 / 16.0f }; 265 | // check(TemporalAASamples == UE_ARRAY_COUNT(SamplesX)); 266 | // SampleX = SamplesX[TemporalSampleIndex]; 267 | // SampleY = SamplesY[TemporalSampleIndex]; 268 | //} 269 | //else if (CVarTemporalAASamplesValue == 5) 270 | //{ 271 | // // Compressed 4 sample pattern on same vertical and horizontal line (less temporal flicker). 272 | // // Compressed 1/2 works better than correct 2/3 (reduced temporal flicker). 273 | // // . N . 274 | // // W . E 275 | // // . S . 276 | // // Rolling circle pattern (N,E,S,W). 277 | // float SamplesX[] = { 0.0f / 2.0f, 1.0 / 2.0f, 0.0 / 2.0f, -1.0 / 2.0f }; 278 | // float SamplesY[] = { -1.0f / 2.0f, 0.0 / 2.0f, 1.0 / 2.0f, 0.0 / 2.0f }; 279 | // check(TemporalAASamples == UE_ARRAY_COUNT(SamplesX)); 280 | // SampleX = SamplesX[TemporalSampleIndex]; 281 | // SampleY = SamplesY[TemporalSampleIndex]; 282 | //} 283 | //else 284 | //{ 285 | // float u1 = Halton(TemporalSampleIndex + 1, 2); 286 | // float u2 = Halton(TemporalSampleIndex + 1, 3); 287 | 288 | // // Generates samples in normal distribution 289 | // // exp( x^2 / Sigma^2 ) 290 | 291 | // static auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.TemporalAAFilterSize")); 292 | // float FilterSize = CVar->GetFloat(); 293 | 294 | // // Scale distribution to set non-unit variance 295 | // // Variance = Sigma^2 296 | // float Sigma = 0.47f * FilterSize; 297 | 298 | // // Window to [-0.5, 0.5] output 299 | // // Without windowing we could generate samples far away on the infinite tails. 300 | // float OutWindow = 0.5f; 301 | // float InWindow = FMath::Exp(-0.5 * FMath::Square(OutWindow / Sigma)); 302 | 303 | // // Box-Muller transform 304 | // float Theta = 2.0f * PI * u2; 305 | // float r = Sigma * FMath::Sqrt(-2.0f * FMath::Loge((1.0f - u1) * InWindow + u1)); 306 | 307 | // SampleX = r * FMath::Cos(Theta); 308 | // SampleY = r * FMath::Sin(Theta); 309 | //} 310 | 311 | View.TemporalJitterSequenceLength = TemporalAASamples; 312 | View.TemporalJitterIndex = TemporalSampleIndex; 313 | //View.TemporalJitterPixels.X = SampleX; 314 | //View.TemporalJitterPixels.Y = SampleY; 315 | 316 | View.ViewMatrices.HackAddTemporalAAProjectionJitter(FVector2D(SampleX * 2.0f / ViewRect.Width(), SampleY * -2.0f / ViewRect.Height())); 317 | } 318 | 319 | -------------------------------------------------------------------------------- /Shaders/Private/SMAA_UE5.usf: -------------------------------------------------------------------------------- 1 | #include "/Engine/Public/Platform.ush" 2 | #include "/Engine/Private/Common.ush" 3 | 4 | //#include "/Engine/Private/ScreenPass.ush" 5 | //#include "/Engine/Private/DeferredShadingCommon.ush" 6 | #include "/Engine/Private/TemporalSuperResolution/TSRCommon.ush" 7 | //------------------------------------------------------------------------ 8 | 9 | // Shader Functions 10 | // Missing Function: Luma4 11 | // Ensure Luma4 is defined. Placeholder implementation below: 12 | float3 Luma4(float3 rgb) 13 | { 14 | return dot(rgb, float3(0.2126, 0.7152, 0.0722)); // Standard luminance calculation 15 | } 16 | // FSMAAEdgeDetectionCS: Main edge detection shader 17 | void FSMAAEdgeDetectionCS( 18 | Texture2D Texture, 19 | SamplerState PointTextureSampler, 20 | float2 UV, 21 | out float3 Output 22 | ) 23 | { 24 | // Utilize Luma4 for edge detection 25 | Output = Luma4(Texture2DSample(Texture, PointTextureSampler, UV).rgb); 26 | } 27 | 28 | 29 | // FSMAATemporalResolveCS: Temporal resolve shader 30 | // Missing Function: ComputeStaticVelocity 31 | // Ensure ComputeStaticVelocity is defined. Placeholder implementation below: 32 | float2 ComputeStaticVelocity(float2 AsScreen, float Depth) 33 | { 34 | // Example implementation 35 | // Placeholder velocity computation logic 36 | return float2(AsScreen.x * Depth, AsScreen.y * Depth); 37 | } 38 | 39 | // Temporal Resolve Shader Implementation 40 | void FSMAATemporalResolveCS( 41 | Texture2D Texture, 42 | SamplerState PointTextureSampler, 43 | float2 AsScreen, 44 | float Depth, 45 | out float2 Velocity 46 | ) 47 | { 48 | Velocity = ComputeStaticVelocity(AsScreen, Depth).xy; 49 | } 50 | 51 | //------------------------------------------------------------------------ 52 | ///// ///// ////////// ///// ///// 53 | // SMAA Macros from Leszek Godlewski 54 | // 55 | 56 | /** 57 | * Copyright (C) 2015-2016 Leszek Godlewski 58 | * Copyright (C) 2022 Acinonyx Ltd. 59 | * 60 | * Permission is hereby granted, free of charge, to any person obtaining a copy 61 | * this software and associated documentation files (the "Software"), to deal in 62 | * the Software without restriction, including without limitation the rights to 63 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 64 | * of the Software, and to permit persons to whom the Software is furnished to 65 | * do so, subject to the following conditions: 66 | * 67 | * The above copyright notice and this permission notice shall be included in 68 | * all copies or substantial portions of the Software. As clarification, there 69 | * is no requirement that the copyright notice and permission be included in 70 | * binary distributions of the Software. 71 | * 72 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 73 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 74 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 75 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 76 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 77 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 78 | * SOFTWARE. 79 | */ 80 | SamplerState BilinearTextureSampler; 81 | SamplerState PointTextureSampler; 82 | 83 | // Porting macros 84 | #define SMAA_CUSTOM_SL 85 | #define SMAATexture2D(tex) Texture2D tex 86 | // Don't define multisampled textures, we can't really use them in UE4. 87 | //#define SMAATexture2DMS2(tex) Texture2DMS tex 88 | #define SMAATexturePass2D(tex) tex 89 | #define SMAASampleLevelZero(tex, coord) Texture2DSampleLevel(tex, BilinearTextureSampler, coord, 0) 90 | #define SMAASampleLevelZeroPoint(tex, coord) Texture2DSampleLevel(tex, PointTextureSampler, coord, 0) 91 | #define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(BilinearTextureSampler, coord, 0, offset) 92 | #define SMAASample(tex, coord) Texture2DSample(tex, BilinearTextureSampler, coord) 93 | #define SMAASamplePoint(tex, coord) Texture2DSample(tex, PointTextureSampler, coord) 94 | #define SMAASampleOffset(tex, coord, offset) tex.Sample(BilinearTextureSampler, coord, offset) 95 | #define SMAA_FLATTEN FLATTEN 96 | #define SMAA_BRANCH BRANCH 97 | //#define SMAALoad(tex, pos, sample) tex.Load(pos, sample) 98 | #if FEATURE_LEVEL >= FEATURE_LEVEL_SM5 99 | #define SMAAGather(tex, coord) tex.Gather(BilinearTextureSampler, coord, 0) 100 | #endif 101 | 102 | // Viewport Metrics 103 | float4 ViewportMetrics; 104 | #define SMAA_RT_METRICS ViewportMetrics 105 | 106 | // Switching from UE Macros to SMAA Macros 107 | #if SMAA_PRESET == 0 108 | #define SMAA_PRESET_LOW 1 109 | #elif SMAA_PRESET == 1 110 | #define SMAA_PRESET_MEDIUM 1 111 | #elif SMAA_PRESET == 2 112 | #define SMAA_PRESET_HIGH 1 113 | #elif SMAA_PRESET == 3 114 | #define SMAA_PRESET_ULTRA 1 115 | #elif SMAA_PRESET > 3 116 | #define SMAA_PRESET_ULTRA 1 117 | #endif 118 | 119 | 120 | #ifndef SMAA_PREDICATION 121 | #define SMAA_PREDICATION 0 122 | #endif 123 | 124 | #ifndef WORLD_MAX 125 | #define WORLD_MAX 2097152.0 126 | #endif 127 | 128 | #ifndef SMAA_REPROJECTION_WEIGHT_SCALE 129 | #define SMAA_REPROJECTION_WEIGHT_SCALE 0.f 130 | #endif 131 | 132 | #ifndef SMAA_REPROJECTION_WEIGHT_BASE 133 | #define SMAA_REPROJECTION_WEIGHT_BASE 0.5f 134 | #endif 135 | 136 | // Modified to take a texture 137 | float2 GetVelocity(SMAATexture2D(DepthTexture2D), SMAATexture2D(VelocityTexture2D), float2 UV) 138 | { 139 | float2 Velocity; 140 | float4 Foreground = Texture2DSampleLevel(VelocityTexture2D, PointTextureSampler, UV, 0); 141 | 142 | bool bHasForegroundVelocity = Foreground.x > 0; 143 | if (bHasForegroundVelocity) 144 | { 145 | // Decode dynamic velocity from texture 146 | Velocity = DecodeVelocityFromTexture(Foreground).xy; 147 | } 148 | else 149 | { 150 | // Velocity texture has foreground (dynamic: movable or materials with WPO) object velocities only, 151 | // so use temporal reprojection to compute background velocity 152 | 153 | float Depth = Texture2DSampleLevel(DepthTexture2D, PointTextureSampler, UV, 0).r; 154 | float2 AsScreen = ViewportUVToScreenPos(UV); 155 | Velocity = ComputeStaticVelocity(AsScreen, Depth); 156 | } 157 | 158 | return Velocity; 159 | } 160 | 161 | #define AA_CROSS 2 162 | // Epic's GetVelocity 163 | float2 GetVelocityTAA(SMAATexture2D(DepthTexture2D), SMAATexture2D(VelocityTexture2D), float2 UV) 164 | { 165 | float2 Velocity; 166 | float2 VelocityOffset = float2(0.0, 0.0); 167 | float Depth = DepthTexture2D.SampleLevel(PointTextureSampler, UV, 0, int2(0,0)).r; 168 | //float Depth = Texture2DSampleLevel(DepthTexture2D, PointTextureSampler, UV, 0).r; 169 | #if AA_CROSS 170 | { 171 | // For motion vector, use camera/dynamic motion from min depth pixel in pattern around pixel. 172 | // This enables better quality outline on foreground against different motion background. 173 | // Larger 2 pixel distance "x" works best (because AA dilates surface). 174 | float4 Depths; 175 | Depths.x = DepthTexture2D.SampleLevel(PointTextureSampler, UV, 0, int2(-AA_CROSS, -AA_CROSS)).r; 176 | Depths.y = DepthTexture2D.SampleLevel(PointTextureSampler, UV, 0, int2(AA_CROSS, -AA_CROSS)).r; 177 | Depths.z = DepthTexture2D.SampleLevel(PointTextureSampler, UV, 0, int2(-AA_CROSS, AA_CROSS)).r; 178 | Depths.w = DepthTexture2D.SampleLevel(PointTextureSampler, UV, 0, int2(AA_CROSS, AA_CROSS)).r; 179 | 180 | 181 | float2 DepthOffset = float2(AA_CROSS, AA_CROSS); 182 | float DepthOffsetXx = float(AA_CROSS); 183 | #if HAS_INVERTED_Z_BUFFER 184 | // Nearest depth is the largest depth (depth surface 0=far, 1=near). 185 | if(Depths.x > Depths.y) 186 | { 187 | DepthOffsetXx = -AA_CROSS; 188 | } 189 | if(Depths.z > Depths.w) 190 | { 191 | DepthOffset.x = -AA_CROSS; 192 | } 193 | float DepthsXY = max(Depths.x, Depths.y); 194 | float DepthsZW = max(Depths.z, Depths.w); 195 | if(DepthsXY > DepthsZW) 196 | { 197 | DepthOffset.y = -AA_CROSS; 198 | DepthOffset.x = DepthOffsetXx; 199 | } 200 | float DepthsXYZW = max(DepthsXY, DepthsZW); 201 | if(DepthsXYZW > Depth) 202 | { 203 | VelocityOffset = DepthOffset * ViewportMetrics.xy; 204 | Depth = DepthsXYZW; 205 | } 206 | #else // !HAS_INVERTED_Z_BUFFER 207 | #error Fix me! 208 | #endif // !HAS_INVERTED_Z_BUFFER 209 | } 210 | #endif // AA_CROSS 211 | 212 | float4 Foreground = Texture2DSampleLevel(VelocityTexture2D, PointTextureSampler, UV + VelocityOffset, 0); 213 | 214 | bool bHasForegroundVelocity = Foreground.x > 0; 215 | if (bHasForegroundVelocity) 216 | { 217 | // Decode dynamic velocity from texture 218 | Velocity = DecodeVelocityFromTexture(Foreground).xy; 219 | } 220 | else 221 | { 222 | // Velocity texture has foreground (dynamic: movable or materials with WPO) object velocities only, 223 | // so use temporal reprojection to compute background velocity 224 | 225 | float2 AsScreen = ViewportUVToScreenPos(UV); 226 | Velocity = ComputeStaticVelocity(AsScreen, Depth).xy; 227 | } 228 | 229 | return Velocity; 230 | } 231 | 232 | 233 | // Define before reference implementation by Jimenez et al. 234 | // [https://dl.acm.org/doi/abs/10.1111/j.1467-8659.2012.03014.x] 235 | #include "SMAAReference.usf" 236 | 237 | /** 238 | * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) 239 | * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) 240 | * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) 241 | * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) 242 | * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) 243 | * Copyright (C) 2022 Acinonyx Ltd. 244 | * 245 | * Permission is hereby granted, free of charge, to any person obtaining a copy 246 | * this software and associated documentation files (the "Software"), to deal in 247 | * the Software without restriction, including without limitation the rights to 248 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 249 | * of the Software, and to permit persons to whom the Software is furnished to 250 | * do so, subject to the following conditions: 251 | * 252 | * The above copyright notice and this permission notice shall be included in 253 | * all copies or substantial portions of the Software. As clarification, there 254 | * is no requirement that the copyright notice and permission be included in 255 | * binary distributions of the Software. 256 | * 257 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 258 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 259 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 260 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 261 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 262 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 263 | * SOFTWARE. 264 | */ 265 | 266 | 267 | float GetLuma(SMAATexture2D(Texture), float2 UV) 268 | { 269 | #if 0 270 | float3 CentreTap = SMAASamplePoint(Texture, UV).rgb; 271 | float CenterLuma = dot(CentreTap, float3(0.299f, 0.587f, 0.114f)); 272 | float L = CenterLuma / (0.5 + CenterLuma); 273 | 274 | return L; 275 | #else 276 | return Luma4(SMAASamplePoint(Texture, UV).rgb); 277 | #endif 278 | 279 | 280 | } 281 | 282 | 283 | float2 SMAALumaEdgeDetectionCS(float2 texcoord, 284 | SMAATexture2D(colorTex) 285 | #if SMAA_PREDICATION 286 | , SMAATexture2D(predicationTex) 287 | #endif 288 | ) 289 | { 290 | float4 offset[3]; 291 | // We need to modify the entry functions since we're in compute shader 292 | offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); 293 | offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); 294 | offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); 295 | 296 | // Calculate the threshold: 297 | #if SMAA_PREDICATION 298 | float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); 299 | #else 300 | float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); 301 | #endif 302 | 303 | // The default approach is to use REC709 primaries 304 | // Calculate lumas: 305 | // float3 weights = float3(0.2126, 0.7152, 0.0722); 306 | //float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); 307 | // float3 CentreTap = SMAASamplePoint(colorTex, texcoord).rgb; 308 | // float CenterLuma = dot(CentreTap, float3(0.299f, 0.587f, 0.114f)); 309 | // float L = CenterLuma / (0.5 + CenterLuma); 310 | float L = GetLuma(colorTex, texcoord); 311 | 312 | // float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); 313 | float Lleft = GetLuma(colorTex, offset[0].xy); 314 | // float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); 315 | float Ltop = GetLuma(colorTex, offset[0].zw); 316 | 317 | // We do the usual threshold: 318 | float4 delta; 319 | delta.xy = abs(L - float2(Lleft, Ltop)); 320 | float2 edges = step(threshold, delta.xy); 321 | 322 | // Then discard if there is no edge: 323 | if (dot(edges, float2(1.0, 1.0)) == 0.0) 324 | return float2(0,0); 325 | 326 | // Calculate right and bottom deltas: 327 | // float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); 328 | // float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); 329 | 330 | float Lright = GetLuma(colorTex, offset[1].xy); 331 | float Lbottom = GetLuma(colorTex, offset[1].zw); 332 | 333 | delta.zw = abs(L - float2(Lright, Lbottom)); 334 | 335 | // Calculate the maximum delta in the direct neighborhood: 336 | float2 maxDelta = max(delta.xy, delta.zw); 337 | 338 | // Calculate left-left and top-top deltas: 339 | // float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); 340 | // float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); 341 | float Lleftleft = GetLuma(colorTex, offset[2].xy); 342 | float Ltoptop = GetLuma(colorTex, offset[2].zw); 343 | delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); 344 | 345 | // Calculate the final maximum delta: 346 | maxDelta = max(maxDelta.xy, delta.zw); 347 | float finalDelta = max(maxDelta.x, maxDelta.y); 348 | 349 | // Local contrast adaptation: 350 | edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); 351 | 352 | return edges; 353 | } 354 | 355 | float2 SMAADepthEdgeDetectionCS(float2 texcoord, 356 | SMAATexture2D(depthTex)) 357 | { 358 | float4 offset[3]; 359 | // We need to modify the entry functions since we're in compute shader 360 | offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); 361 | offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); 362 | offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); 363 | 364 | float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); 365 | float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); 366 | float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); 367 | 368 | if (dot(edges, float2(1.0, 1.0)) == 0.0) 369 | return float2(0,0); 370 | 371 | return edges; 372 | } 373 | 374 | float2 SMAAColorEdgeDetectionCS(float2 texcoord, 375 | SMAATexture2D(colorTex) 376 | #if SMAA_PREDICATION 377 | , SMAATexture2D(predicationTex) 378 | #endif 379 | ) 380 | { 381 | float4 offset[3]; 382 | // We need to modify the entry functions since we're in compute shader 383 | offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); 384 | offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); 385 | offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); 386 | 387 | // Calculate the threshold: 388 | #if SMAA_PREDICATION 389 | float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); 390 | #else 391 | float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); 392 | #endif 393 | 394 | // Calculate color deltas: 395 | float4 delta; 396 | float3 C = SMAASamplePoint(colorTex, texcoord).rgb; 397 | 398 | float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; 399 | float3 t = abs(C - Cleft); 400 | delta.x = max(max(t.r, t.g), t.b); 401 | 402 | float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; 403 | t = abs(C - Ctop); 404 | delta.y = max(max(t.r, t.g), t.b); 405 | 406 | // We do the usual threshold: 407 | float2 edges = step(threshold, delta.xy); 408 | 409 | // Then discard if there is no edge: 410 | if (dot(edges, float2(1.0, 1.0)) == 0.0) 411 | return float2(0,0); 412 | 413 | // Calculate right and bottom deltas: 414 | float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; 415 | t = abs(C - Cright); 416 | delta.z = max(max(t.r, t.g), t.b); 417 | 418 | float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; 419 | t = abs(C - Cbottom); 420 | delta.w = max(max(t.r, t.g), t.b); 421 | 422 | // Calculate the maximum delta in the direct neighborhood: 423 | float2 maxDelta = max(delta.xy, delta.zw); 424 | 425 | // Calculate left-left and top-top deltas: 426 | float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; 427 | t = abs(C - Cleftleft); 428 | delta.z = max(max(t.r, t.g), t.b); 429 | 430 | float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; 431 | t = abs(C - Ctoptop); 432 | delta.w = max(max(t.r, t.g), t.b); 433 | 434 | // Calculate the final maximum delta: 435 | maxDelta = max(maxDelta.xy, delta.zw); 436 | float finalDelta = max(maxDelta.x, maxDelta.y); 437 | 438 | // Local contrast adaptation: 439 | edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); 440 | 441 | return edges; 442 | } 443 | 444 | 445 | 446 | 447 | 448 | float4 SMAABlendingWeightCalculationCS(float2 texcoord, 449 | SMAATexture2D(edgesTex), 450 | SMAATexture2D(areaTex), 451 | SMAATexture2D(searchTex), 452 | float4 subsampleIndices) 453 | { 454 | float2 pixcoord = texcoord * SMAA_RT_METRICS.zw; 455 | float4 offset[3]; 456 | 457 | // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): 458 | offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); 459 | offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); 460 | 461 | // And these for the searches, they indicate the ends of the loops: 462 | offset[2] = mad(SMAA_RT_METRICS.xxyy, 463 | float4(-2.0, 2.0, -2.0, 2.0) * MaxSearchSteps, 464 | float4(offset[0].xz, offset[1].yw)); 465 | 466 | 467 | // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. 468 | float4 weights = float4(0.0, 0.0, 0.0, 0.0); 469 | 470 | float2 e = SMAASample(edgesTex, texcoord).rg; 471 | 472 | SMAA_BRANCH 473 | if (e.g > 0.0) { // Edge at north 474 | #if !defined(SMAA_DISABLE_DIAG_DETECTION) 475 | // Diagonals have both north and west edges, so searching for them in 476 | // one of the boundaries is enough. 477 | weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); 478 | 479 | // We give priority to diagonals, so if we find a diagonal we skip 480 | // horizontal/vertical processing. 481 | SMAA_BRANCH 482 | if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 483 | #endif 484 | 485 | float2 d; 486 | 487 | // Find the distance to the left: 488 | float3 coords; 489 | coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); 490 | coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) 491 | d.x = coords.x; 492 | 493 | // Now fetch the left crossing edges, two at a time using bilinear 494 | // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to 495 | // discern what value each edge has: 496 | float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; 497 | 498 | // Find the distance to the right: 499 | coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); 500 | d.y = coords.z; 501 | 502 | // We want the distances to be in pixel units (doing this here allow to 503 | // better interleave arithmetic and memory accesses): 504 | d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); 505 | 506 | // SMAAArea below needs a sqrt, as the areas texture is compressed 507 | // quadratically: 508 | float2 sqrt_d = sqrt(d); 509 | 510 | // Fetch the right crossing edges: 511 | float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; 512 | 513 | // Ok, we know how this pattern looks like, now it is time for getting 514 | // the actual area: 515 | weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); 516 | 517 | // Fix corners: 518 | coords.y = texcoord.y; 519 | SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); 520 | 521 | #if !defined(SMAA_DISABLE_DIAG_DETECTION) 522 | } else 523 | e.r = 0.0; // Skip vertical processing. 524 | #endif 525 | } 526 | 527 | SMAA_BRANCH 528 | if (e.r > 0.0) { // Edge at west 529 | float2 d; 530 | 531 | // Find the distance to the top: 532 | float3 coords; 533 | coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); 534 | coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; 535 | d.x = coords.y; 536 | 537 | // Fetch the top crossing edges: 538 | float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; 539 | 540 | // Find the distance to the bottom: 541 | coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); 542 | d.y = coords.z; 543 | 544 | // We want the distances to be in pixel units: 545 | d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); 546 | 547 | // SMAAArea below needs a sqrt, as the areas texture is compressed 548 | // quadratically: 549 | float2 sqrt_d = sqrt(d); 550 | 551 | // Fetch the bottom crossing edges: 552 | float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; 553 | 554 | // Get the area for this direction: 555 | weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); 556 | 557 | // Fix corners: 558 | coords.x = texcoord.x; 559 | SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); 560 | } 561 | 562 | return weights; 563 | } 564 | 565 | float4 SMAANeighborhoodBlendingCS(float2 texcoord, 566 | SMAATexture2D(colorTex), 567 | SMAATexture2D(blendTex) 568 | #if SMAA_REPROJECTION 569 | , SMAATexture2D(velocityTex) 570 | , SMAATexture2D(depthTex) 571 | #endif 572 | ) 573 | { 574 | // Fetch the blending weights for current pixel: 575 | float4 offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); 576 | float4 a; 577 | a.x = SMAASample(blendTex, offset.xy).a; // Right 578 | a.y = SMAASample(blendTex, offset.zw).g; // Top 579 | a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left 580 | 581 | // Is there any blending weight with a value greater than 0.0? 582 | SMAA_BRANCH 583 | if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { 584 | float4 color = SMAASampleLevelZero(colorTex, texcoord); 585 | 586 | #if SMAA_REPROJECTION 587 | #if 1 588 | float2 velocity = GetVelocityTAA(depthTex, velocityTex, texcoord); 589 | #else 590 | float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); 591 | #endif 592 | 593 | // Pack velocity into the alpha channel: 594 | color.a = sqrt(5.0 * length(velocity)); 595 | #endif 596 | 597 | return color; 598 | } else { 599 | bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) 600 | 601 | // Calculate the blending offsets: 602 | float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); 603 | float2 blendingWeight = a.yw; 604 | SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); 605 | SMAAMovc(bool2(h, h), blendingWeight, a.xz); 606 | blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); 607 | 608 | // Calculate the texture coordinates: 609 | float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); 610 | 611 | // We exploit bilinear filtering to mix current pixel with the chosen 612 | // neighbor: 613 | float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); 614 | color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); 615 | 616 | #if SMAA_REPROJECTION 617 | // Antialias velocity for proper reprojection in a later stage: 618 | #if 1 619 | float2 velocity = GetVelocityTAA(depthTex, velocityTex, blendingCoord.xy); 620 | velocity += GetVelocityTAA(depthTex, velocityTex, blendingCoord.zw); 621 | #else 622 | float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); 623 | velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); 624 | #endif 625 | 626 | // Pack velocity into the alpha channel: 627 | color.a = sqrt(5.0 * length(velocity)); 628 | #endif 629 | 630 | return color; 631 | } 632 | } 633 | 634 | //----------------------------------------------------------------------------- 635 | // Temporal Resolve Shader (Optional Pass) 636 | 637 | float4 SMAAResolveCS(float2 texcoord, 638 | SMAATexture2D(currentColorTex), 639 | SMAATexture2D(previousColorTex) 640 | #if SMAA_REPROJECTION 641 | , SMAATexture2D(velocityTex), 642 | SMAATexture2D(depthTex) 643 | #endif 644 | ) { 645 | #if SMAA_REPROJECTION 646 | // Velocity is assumed to be calculated for motion blur, so we need to 647 | // inverse it for reprojection: 648 | #if 1 649 | float2 velocity = float2(-0.5, 0.5f) * GetVelocityTAA(depthTex, velocityTex, texcoord); 650 | #else 651 | float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); 652 | #endif 653 | 654 | // Fetch current pixel: 655 | float4 current = SMAASamplePoint(currentColorTex, texcoord); 656 | 657 | // Uncomment to reuse the velocity calculated above rather than the AA'd 658 | // velocity in the alpha channel 659 | //current.a = sqrt(5.0 * length(velocity)); 660 | 661 | // Reproject current coordinates and fetch previous pixel: 662 | float2 Limit = min(float2(1,1), max(float2(0,0), texcoord + velocity)); 663 | float4 previous = SMAASamplePoint(previousColorTex, Limit); 664 | 665 | // Check offscreen. Don't project if we are 666 | float2 ScreenPos = ViewportUVToScreenPos(texcoord) + velocity; 667 | bool OffScreen = max(abs(ScreenPos.x), abs(ScreenPos.y)) >= 1.0; 668 | 669 | // Attenuate the previous pixel if the velocity is different: 670 | float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; 671 | float weight = SMAA_REPROJECTION_WEIGHT_BASE * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); 672 | 673 | // Blend the pixels according to the calculated weight: 674 | SMAA_FLATTEN if (OffScreen) weight = 0; 675 | 676 | return lerp(current, previous, weight); 677 | 678 | #else 679 | // Just blend the pixels: 680 | float4 current = SMAASamplePoint(currentColorTex, texcoord); 681 | float4 previous = SMAASamplePoint(previousColorTex, texcoord); 682 | return lerp(current, previous, SMAA_REPROJECTION_WEIGHT_BASE); 683 | #endif 684 | } -------------------------------------------------------------------------------- /Source/SMAAPlugin/Private/PostProcess/PostProcessSMAA.cpp: -------------------------------------------------------------------------------- 1 | #include "PostProcess/PostProcessSMAA.h" 2 | 3 | #include "PostProcess/PostProcessing.h" 4 | #include "PostProcess/PostProcessMaterialInputs.h" 5 | #include "Rendering/Texture2DResource.h" 6 | #include "ScenePrivate.h" 7 | #include "SMAASceneExtension.h" 8 | #include "SceneViewExtension.h" 9 | 10 | #include "SceneView.h" 11 | #include "ScreenPass.h" 12 | #include "CommonRenderResources.h" 13 | #include "Engine/TextureRenderTarget2D.h" 14 | #include "DynamicResolutionState.h" 15 | #include "FXRenderingUtils.h" 16 | 17 | UE_DISABLE_OPTIMIZATION 18 | DECLARE_GPU_STAT(SMAAPass) 19 | DECLARE_GPU_STAT_NAMED(SMAADispatch, TEXT("SMAA Dispatch")); 20 | 21 | TAutoConsoleVariable CVarSMAAQuality( 22 | TEXT("r.SMAA.Quality"), 3, 23 | TEXT("Selects the quality permutation of SMAA.\n") 24 | TEXT(" 0: Low Preset \n") 25 | TEXT(" 1: Medium Preset \n") 26 | TEXT(" 2: High Preset \n") 27 | TEXT(" 3: Ultra Preset (Default) \n"), 28 | ECVF_Scalability | ECVF_RenderThreadSafe); 29 | 30 | TAutoConsoleVariable CVarSMAAEdgeMode( 31 | TEXT("r.SMAA.EdgeDetector"), 3, 32 | TEXT("Data used by SMAA's Edge Detector\n") 33 | TEXT(" 0 - Depth\n") 34 | TEXT(" 1 - Luminance\n") 35 | TEXT(" 2 - Colour\n") 36 | TEXT(" 3 - World Normal (Default)\n"), 37 | ECVF_RenderThreadSafe); 38 | 39 | TAutoConsoleVariable CVarSMAAPredicationSource( 40 | TEXT("r.SMAA.Predicate"), 0, 41 | TEXT("Predication Source\n") 42 | TEXT(" 0 - None (Default)\n") 43 | TEXT(" 1 - Depth\n") 44 | TEXT(" 2 - World Normal\n") 45 | TEXT(" 3 - Spec, Rough, Metal\n"), 46 | ECVF_RenderThreadSafe); 47 | 48 | TAutoConsoleVariable CVarSMAAMaxSearchSteps(TEXT("r.SMAA.MaxSearchSteps"), 8, 49 | TEXT("Maximum steps performed in Horizontal/Vert patterns [0 - 112]"), 50 | ECVF_Scalability | ECVF_RenderThreadSafe); 51 | 52 | TAutoConsoleVariable CVarSMAAMaxDiagonalSearchSteps(TEXT("r.SMAA.MaxSearchStepsDiagonal"), 16, 53 | TEXT("Maximum steps performed in Diagonal patterns [0 - 20]"), 54 | ECVF_Scalability | ECVF_RenderThreadSafe); 55 | 56 | TAutoConsoleVariable CVarSMAACornerRounding(TEXT("r.SMAA.CornerRounding"), 25, 57 | TEXT("Specifies how much sharp corners will be rounded [0 - 100]"), 58 | ECVF_Scalability | ECVF_RenderThreadSafe); 59 | 60 | TAutoConsoleVariable CVarSMAAAdaptationFactor(TEXT("r.SMAA.AdaptationFactor"), 2.0, 61 | TEXT("Controls Adaptation Factor for edge discard\n") 62 | TEXT(""), 63 | ECVF_Scalability | ECVF_RenderThreadSafe); 64 | 65 | TAutoConsoleVariable CVarSMAAReprojectionWeight(TEXT("r.SMAA.ReprojectionWeight"), 25, 66 | TEXT("Controls Reprojection [0 - 100]\n") 67 | TEXT("Using low values will exhibit ghosting, while using high values will disable temporal supersampling under motion"), 68 | ECVF_Scalability | ECVF_RenderThreadSafe); 69 | 70 | TAutoConsoleVariable CVarSMAAPredicationThreshold(TEXT("r.SMAA.PredicationThreshold"), 0.04, 71 | TEXT("Controls Reprojection [0 - 1] (Default 0.04) \n") 72 | TEXT("Threshold to be used in the additional predication buffer."), 73 | ECVF_Scalability | ECVF_RenderThreadSafe); 74 | 75 | TAutoConsoleVariable CVarSMAAPredicationScale(TEXT("r.SMAA.PredicationScale"), 2.0, 76 | TEXT("Controls Reprojection [1 - 5] (Default 2.0) \n") 77 | TEXT("How much to scale the global threshold used for luma or color edge detection when using predication"), 78 | ECVF_Scalability | ECVF_RenderThreadSafe); 79 | 80 | TAutoConsoleVariable CVarSMAAPredicationStrength(TEXT("r.SMAA.PredicationStrength"), 0.4, 81 | TEXT("Controls Reprojection [0 - 1] (Default 0.4) \n") 82 | TEXT("How much to locally decrease the threshold."), 83 | ECVF_Scalability | ECVF_RenderThreadSafe); 84 | 85 | TAutoConsoleVariable CVarSMAATemporalHistoryBias(TEXT("r.SMAA.TemporalHistoryBias"), 0.4, 86 | TEXT("Controls base weight from prior frames [0 - 1) (Default 0.4)"), 87 | ECVF_Scalability | ECVF_RenderThreadSafe); 88 | 89 | ///// ///// ////////// ///// ///// 90 | // SMAA Shaders 91 | // 92 | 93 | /** 94 | * SMAA Edge Detection 95 | */ 96 | class FSMAAEdgeDetectionCS : public FGlobalShader 97 | { 98 | public: 99 | static const int ThreadgroupSizeX = 8; 100 | static const int ThreadgroupSizeY = 8; 101 | static const int ThreadgroupSizeZ = 1; 102 | 103 | DECLARE_GLOBAL_SHADER(FSMAAEdgeDetectionCS); 104 | SHADER_USE_PARAMETER_STRUCT(FSMAAEdgeDetectionCS, FGlobalShader); 105 | 106 | class FSMAAPresetConfigDim : SHADER_PERMUTATION_ENUM_CLASS("SMAA_PRESET", ESMAAPreset); 107 | class FSMAAEdgeModeConfigDim : SHADER_PERMUTATION_ENUM_CLASS("SMAA_EDMODE", ESMAAEdgeDetectors); 108 | class FSMAAPredicateConfigDim : SHADER_PERMUTATION_BOOL("SMAA_PREDICATION"); 109 | 110 | using FPermutationDomain = 111 | TShaderPermutationDomain; 112 | 113 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 114 | RDG_TEXTURE_ACCESS(DepthTexture, ERHIAccess::SRVCompute) 115 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputDepth) 116 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputSceneColor) 117 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, Predicate) 118 | SHADER_PARAMETER(float, AdaptationFactor) 119 | SHADER_PARAMETER(float, PredicationThreshold) 120 | SHADER_PARAMETER(float, PredicationScale) 121 | SHADER_PARAMETER(float, PredicationStrength) 122 | SHADER_PARAMETER_SAMPLER(SamplerState, PointTextureSampler) 123 | SHADER_PARAMETER_SAMPLER(SamplerState, BilinearTextureSampler) 124 | SHADER_PARAMETER(FVector4f, ViewportMetrics) 125 | SHADER_PARAMETER(float, NormalisedCornerRounding) 126 | SHADER_PARAMETER(float, MaxDiagonalSearchSteps) 127 | SHADER_PARAMETER(float, MaxSearchSteps) 128 | SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) 129 | SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, EdgesTexture) 130 | END_SHADER_PARAMETER_STRUCT() 131 | 132 | static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) 133 | { 134 | return true; 135 | 136 | //TODO: Kory 137 | //FPermutationDomain PermutationVector(Parameters.PermutationId); 138 | //if (PermutationVector.Get() == ESMAAEdgeDetectors::Luminance) 139 | //{ 140 | // return true; 141 | //} 142 | //return false; 143 | } 144 | 145 | static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, 146 | FShaderCompilerEnvironment& OutEnvironment) 147 | { 148 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), ThreadgroupSizeX); 149 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), ThreadgroupSizeY); 150 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEZ"), ThreadgroupSizeZ); 151 | OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1); 152 | } 153 | }; 154 | IMPLEMENT_GLOBAL_SHADER(FSMAAEdgeDetectionCS, "/SMAAPlugin/Private/SMAA_EdgeDetection.usf", "EdgeDetectionCS", 155 | SF_Compute); 156 | 157 | /** 158 | * SMAA Blending Weight Calculation 159 | */ 160 | class FSMAABlendingWeightsCS : public FGlobalShader 161 | { 162 | public: 163 | static const int ThreadgroupSizeX = 8; 164 | static const int ThreadgroupSizeY = 8; 165 | static const int ThreadgroupSizeZ = 1; 166 | 167 | DECLARE_GLOBAL_SHADER(FSMAABlendingWeightsCS); 168 | SHADER_USE_PARAMETER_STRUCT(FSMAABlendingWeightsCS, FGlobalShader); 169 | 170 | class FSMAAPresetConfigDim : SHADER_PERMUTATION_ENUM_CLASS("SMAA_PRESET", ESMAAPreset); 171 | 172 | using FPermutationDomain = TShaderPermutationDomain; 173 | 174 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 175 | RDG_TEXTURE_ACCESS(DepthTexture, ERHIAccess::SRVCompute) 176 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputEdges) 177 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, AreaTexture) 178 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SearchTexture) 179 | SHADER_PARAMETER(FVector2f, TemporalJitterPixels) 180 | // SHADER_PARAMETER_SRV(Texture2D, AreaTexture) 181 | SHADER_PARAMETER_SAMPLER(SamplerState, PointTextureSampler) 182 | SHADER_PARAMETER_SAMPLER(SamplerState, BilinearTextureSampler) 183 | SHADER_PARAMETER(FVector4f, ViewportMetrics) 184 | SHADER_PARAMETER(FVector4f, SubpixelWeights) 185 | SHADER_PARAMETER(float, NormalisedCornerRounding) 186 | SHADER_PARAMETER(float, MaxDiagonalSearchSteps) 187 | SHADER_PARAMETER(float, MaxSearchSteps) 188 | SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) 189 | SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, BlendTexture) 190 | END_SHADER_PARAMETER_STRUCT() 191 | 192 | static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) 193 | { 194 | return true; 195 | } 196 | static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, 197 | FShaderCompilerEnvironment& OutEnvironment) 198 | { 199 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), ThreadgroupSizeX); 200 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), ThreadgroupSizeY); 201 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEZ"), ThreadgroupSizeZ); 202 | OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1); 203 | } 204 | }; 205 | IMPLEMENT_GLOBAL_SHADER(FSMAABlendingWeightsCS, "/SMAAPlugin/Private/SMAA_BlendWeighting.usf", 206 | "BlendWeightingCS", SF_Compute); 207 | 208 | /** 209 | * SMAA Neighbour Blending 210 | */ 211 | class FSMAANeighbourhoodBlendingCS : public FGlobalShader 212 | { 213 | public: 214 | static const int ThreadgroupSizeX = 8; 215 | static const int ThreadgroupSizeY = 8; 216 | static const int ThreadgroupSizeZ = 1; 217 | 218 | DECLARE_GLOBAL_SHADER(FSMAANeighbourhoodBlendingCS); 219 | SHADER_USE_PARAMETER_STRUCT(FSMAANeighbourhoodBlendingCS, FGlobalShader); 220 | 221 | class FSMAAPresetConfigDim : SHADER_PERMUTATION_ENUM_CLASS("SMAA_PRESET", ESMAAPreset); 222 | class FSMAAReprojectionDim : SHADER_PERMUTATION_BOOL("SMAA_REPROJECTION"); 223 | 224 | using FPermutationDomain = 225 | TShaderPermutationDomain; 226 | 227 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 228 | RDG_TEXTURE_ACCESS(DepthTexture, ERHIAccess::SRVCompute) 229 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SceneColour) 230 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, VelocityTexture) 231 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, InputBlend) 232 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SceneDepth) 233 | SHADER_PARAMETER_SAMPLER(SamplerState, PointTextureSampler) 234 | SHADER_PARAMETER_SAMPLER(SamplerState, BilinearTextureSampler) 235 | SHADER_PARAMETER(FVector4f, ViewportMetrics) 236 | SHADER_PARAMETER(float, NormalisedCornerRounding) 237 | SHADER_PARAMETER(float, MaxDiagonalSearchSteps) 238 | SHADER_PARAMETER(float, MaxSearchSteps) 239 | SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) 240 | SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, FinalFrame) 241 | END_SHADER_PARAMETER_STRUCT() 242 | 243 | static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) 244 | { 245 | return true; 246 | } 247 | static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, 248 | FShaderCompilerEnvironment& OutEnvironment) 249 | { 250 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), ThreadgroupSizeX); 251 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), ThreadgroupSizeY); 252 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEZ"), ThreadgroupSizeZ); 253 | OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1); 254 | } 255 | }; 256 | IMPLEMENT_GLOBAL_SHADER(FSMAANeighbourhoodBlendingCS, "/SMAAPlugin/Private/SMAA_NeighbourhoodBlend.usf", 257 | "NeighbourhoodBlendingCS", SF_Compute); 258 | 259 | /** 260 | * SMAA Blending Weight Calculation 261 | */ 262 | class FSMAATemporalResolveCS : public FGlobalShader 263 | { 264 | public: 265 | static const int ThreadgroupSizeX = 8; 266 | static const int ThreadgroupSizeY = 8; 267 | static const int ThreadgroupSizeZ = 1; 268 | 269 | DECLARE_GLOBAL_SHADER(FSMAATemporalResolveCS); 270 | SHADER_USE_PARAMETER_STRUCT(FSMAATemporalResolveCS, FGlobalShader); 271 | 272 | class FSMAAPresetConfigDim : SHADER_PERMUTATION_ENUM_CLASS("SMAA_PRESET", ESMAAPreset); 273 | class FSMAAReprojectionDim : SHADER_PERMUTATION_BOOL("SMAA_REPROJECTION"); 274 | 275 | using FPermutationDomain = 276 | TShaderPermutationDomain; 277 | 278 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 279 | RDG_TEXTURE_ACCESS(DepthTexture, ERHIAccess::SRVCompute) 280 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, CurrentSceneColour) 281 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, PastSceneColour) 282 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, SceneDepth) 283 | SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, VelocityTexture) 284 | 285 | SHADER_PARAMETER_SAMPLER(SamplerState, PointTextureSampler) 286 | SHADER_PARAMETER_SAMPLER(SamplerState, BilinearTextureSampler) 287 | SHADER_PARAMETER(FVector4f, ViewportMetrics) 288 | SHADER_PARAMETER(FVector4f, LimitedViewportSize) 289 | SHADER_PARAMETER(float, NormalisedCornerRounding) 290 | SHADER_PARAMETER(float, MaxDiagonalSearchSteps) 291 | SHADER_PARAMETER(float, MaxSearchSteps) 292 | SHADER_PARAMETER(float, ReprojectionWeight) 293 | SHADER_PARAMETER(float, TemporalHistoryBias) 294 | SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) 295 | SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, Resolved) 296 | END_SHADER_PARAMETER_STRUCT() 297 | 298 | static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) 299 | { 300 | return true; 301 | } 302 | static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, 303 | FShaderCompilerEnvironment& OutEnvironment) 304 | { 305 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), ThreadgroupSizeX); 306 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), ThreadgroupSizeY); 307 | OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEZ"), ThreadgroupSizeZ); 308 | OutEnvironment.SetDefine(TEXT("COMPUTE_SHADER"), 1); 309 | } 310 | }; 311 | 312 | IMPLEMENT_GLOBAL_SHADER(FSMAATemporalResolveCS, "/SMAAPlugin/Private/SMAA_T2XResolve.usf", "TemporalResolveCS", SF_Compute); 313 | 314 | ESMAAPreset GetSMAAPreset() 315 | { 316 | return ESMAAPreset(FMath::Clamp(CVarSMAAQuality.GetValueOnRenderThread(), 0, 3)); 317 | } 318 | 319 | ESMAAEdgeDetectors GetSMAAEdgeDetectors() 320 | { 321 | return ESMAAEdgeDetectors(FMath::Clamp(CVarSMAAEdgeMode.GetValueOnRenderThread(), 0, 3)); 322 | } 323 | 324 | ESMAAPredicationTexture GetPredicateSource() 325 | { 326 | return ESMAAPredicationTexture(FMath::Clamp(CVarSMAAPredicationSource.GetValueOnRenderThread(), 0, 3)); 327 | } 328 | 329 | uint8 GetSMAAMaxSearchSteps() 330 | { 331 | return FMath::Clamp(CVarSMAAMaxSearchSteps.GetValueOnRenderThread(), 0, 112); 332 | } 333 | 334 | uint8 GetSMAAMaxDiagonalSearchSteps() 335 | { 336 | return FMath::Clamp(CVarSMAAMaxDiagonalSearchSteps.GetValueOnRenderThread(), 0, 20); 337 | } 338 | 339 | uint8 GetSMAACornerRounding() 340 | { 341 | return FMath::Clamp(CVarSMAACornerRounding.GetValueOnRenderThread(), 0, 100); 342 | } 343 | 344 | float GetSMAAAdaptationFactor() 345 | { 346 | return FMath::Clamp(CVarSMAAAdaptationFactor.GetValueOnRenderThread(), 0.f, 10.f); 347 | } 348 | 349 | float GetSMAAReprojectionWeight() 350 | { 351 | return FMath::Clamp(CVarSMAAReprojectionWeight.GetValueOnRenderThread(), 0.f, 100.f); 352 | } 353 | 354 | float GetSMAAPredicationThreshold() 355 | { 356 | return FMath::Clamp(CVarSMAAPredicationThreshold.GetValueOnRenderThread(), 0.f, 1.f); 357 | } 358 | 359 | float GetSMAAPredicationScale() 360 | { 361 | return FMath::Clamp(CVarSMAAPredicationScale.GetValueOnRenderThread(), 1.f, 5.f); 362 | } 363 | 364 | float GetSMAAPredicationStrength() 365 | { 366 | return FMath::Clamp(CVarSMAAPredicationStrength.GetValueOnRenderThread(), 0.f, 1.f); 367 | } 368 | 369 | float GetSMAATemporalHistoryBias() 370 | { 371 | return FMath::Clamp(CVarSMAATemporalHistoryBias.GetValueOnRenderThread(), 0.f, (1.f - SMALL_NUMBER)); 372 | } 373 | 374 | //// FlipNames 375 | //TCHAR* FlipNames[2] = { 376 | // TEXT("SMAA0"), 377 | // TEXT("SMAA1") 378 | //}; 379 | 380 | FVector4f SubpixelJitterWeights[2] = { 381 | FVector4f(1, 1, 1, 0), 382 | FVector4f(2, 2, 2, 0) 383 | }; 384 | 385 | FScreenPassTexture AddSMAAPasses(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSMAAInputs& Inputs, const FPostProcessMaterialInputs& InOutInputs, TSharedRef ViewData) 386 | { 387 | check(Inputs.SceneColor.IsValid()); 388 | check(Inputs.Quality != ESMAAPreset::MAX); 389 | check(Inputs.EdgeMode != ESMAAEdgeDetectors::MAX); 390 | RDG_EVENT_SCOPE(GraphBuilder, "SMAA T2x"); 391 | 392 | FIntPoint InputExtents = Inputs.SceneColor.Texture->Desc.Extent; // View.ViewRect.Size(); 393 | FIntRect InputRect = View.ViewRect; 394 | InputRect.Min = FIntPoint(0, 0); 395 | InputRect.Min = InputExtents; 396 | FIntPoint BackingSize = InputExtents; 397 | QuantizeSceneBufferSize(InputExtents, BackingSize); 398 | 399 | FIntRect OutputRect = View.ViewRect; 400 | FIntPoint OutputExtents = View.ViewRect.Size(); 401 | 402 | FScreenPassTexture Output = Inputs.OverrideOutput; 403 | 404 | if (!Output.IsValid()) 405 | { 406 | FRDGTextureDesc WriteOutTextureDesc = 407 | FRDGTextureDesc::Create2D(BackingSize, PF_FloatRGBA, FClearValueBinding::Black, 408 | TexCreate_ShaderResource | TexCreate_UAV | TexCreate_RenderTargetable); 409 | 410 | Output = FScreenPassTexture( 411 | GraphBuilder.CreateTexture(WriteOutTextureDesc, TEXT("SMAA.Output")), 412 | OutputRect); 413 | } 414 | 415 | FRHISamplerState* BilinearClampSampler = TStaticSamplerState::GetRHI(); 416 | FRHISamplerState* PointClampSampler = TStaticSamplerState::GetRHI(); 417 | 418 | auto RTMetrics = FVector4f(1.0 / BackingSize.X, 1.0 / BackingSize.Y, BackingSize.X, BackingSize.Y); 419 | 420 | const FTexture2DResource* AreaResource = ViewData->SMAAAreaTexture; 421 | if (!AreaResource) 422 | { 423 | // Bail 424 | return Inputs.SceneColor; 425 | } 426 | 427 | const FTexture2DResource* SearchResource = ViewData->SMAASearchTexture; 428 | if (!SearchResource) 429 | { 430 | // Bail 431 | return Inputs.SceneColor; 432 | } 433 | 434 | FRHITexture* AreaTextureRHI = AreaResource->GetTexture2DRHI(); 435 | if (!AreaTextureRHI) 436 | { 437 | // Bail 438 | return Inputs.SceneColor; 439 | } 440 | FRDGTextureRef AreaTexture = RegisterExternalTexture(GraphBuilder, AreaTextureRHI, TEXT("SMAA.AreaTexture")); 441 | 442 | FRHITexture* SearchTextureRHI = SearchResource->GetTexture2DRHI(); 443 | if (!SearchTextureRHI) 444 | { 445 | // Bail 446 | return Inputs.SceneColor; 447 | } 448 | FRDGTextureRef SearchTexture = RegisterExternalTexture(GraphBuilder, SearchTextureRHI, TEXT("SMAA.SearchTexture")); 449 | 450 | // Create Textures for SMAA 451 | FRDGTextureDesc EdgesTextureDesc = 452 | FRDGTextureDesc::Create2D(BackingSize, PF_FloatRGBA, FClearValueBinding::Black, 453 | TexCreate_ShaderResource | TexCreate_UAV | TexCreate_RenderTargetable); 454 | 455 | FRDGTextureRef EdgesTexture = GraphBuilder.CreateTexture(EdgesTextureDesc, TEXT("SMAA.EdgesTexture")); 456 | 457 | // Blend Texture 458 | FRDGTextureDesc BlendTextureDesc = 459 | FRDGTextureDesc::Create2D(BackingSize, PF_FloatRGBA, FClearValueBinding::Black, 460 | TexCreate_ShaderResource | TexCreate_UAV | TexCreate_RenderTargetable); 461 | 462 | FRDGTextureRef BlendTexture = GraphBuilder.CreateTexture(BlendTextureDesc, TEXT("SMAA.BlendTexture")); 463 | 464 | // Modification! 465 | // Fall back to SMAA 1x? 466 | bool bCameraCut = false; 467 | FRDGTextureRef LastRGBA = GSystemTextures.GetBlackDummy(GraphBuilder); 468 | 469 | //if (View.PrevViewInfo.SMAAHistory.IsValid()) 470 | if (ViewData->SMAAHistory.IsValid()) 471 | { 472 | //LastRGBA = GraphBuilder.RegisterExternalTexture(View.PrevViewInfo.SMAAHistory.PastFrame); 473 | LastRGBA = GraphBuilder.RegisterExternalTexture(ViewData->SMAAHistory.PastFrame); 474 | bCameraCut = View.bCameraCut; 475 | } 476 | 477 | //InOutInputs.SceneTextures.SceneTextures->GetContents()->SceneColorTexture; 478 | FRDGTextureRef SceneColor = Inputs.SceneColor.Texture; 479 | //FRDGTextureRef SceneDepth = Inputs.SceneDepth.Texture; 480 | FRDGTextureRef Velocity = Inputs.SceneVelocity.Texture; 481 | 482 | // Create Depth SRV Desc 483 | //FRDGTextureSRVDesc DepthSRVDesc = FRDGTextureSRVDesc::Create(SceneDepth); 484 | FRDGTextureSRVDesc AreaTextureSRVDesc = FRDGTextureSRVDesc::Create(AreaTexture); 485 | FRDGTextureSRVDesc SearchTextureSRVDesc = FRDGTextureSRVDesc::Create(SearchTexture); 486 | FRDGTextureSRVDesc SceneColourSRVDesc = FRDGTextureSRVDesc::Create(SceneColor); 487 | FRDGTextureSRVDesc PrevSceneColourSRVDesc = FRDGTextureSRVDesc::Create(LastRGBA); 488 | FRDGTextureSRVDesc EdgesSRVDesc = FRDGTextureSRVDesc::Create(EdgesTexture); 489 | FRDGTextureSRVDesc BlendSRVDesc = FRDGTextureSRVDesc::Create(BlendTexture); 490 | FRDGTextureSRVDesc WriteOutTextureSRVDesc = FRDGTextureSRVDesc::Create(Output.Texture); 491 | FRDGTextureSRVDesc VelocityDesc = FRDGTextureSRVDesc::Create(Velocity); 492 | 493 | FRDGTextureSRVRef AreaTextureSRV = GraphBuilder.CreateSRV(AreaTextureSRVDesc); 494 | FRDGTextureSRVRef SearchTextureSRV = GraphBuilder.CreateSRV(SearchTextureSRVDesc); 495 | FRDGTextureSRVRef DepthSRV = GraphBuilder.CreateSRV(InOutInputs.SceneTextures.SceneTextures->GetContents()->SceneDepthTexture); 496 | FRDGTextureSRVRef ColourSRV = GraphBuilder.CreateSRV(SceneColourSRVDesc); 497 | 498 | TRDGTextureAccess SceneDepth = InOutInputs.SceneTextures.SceneTextures->GetContents()->SceneDepthTexture; 499 | 500 | // Permutations 501 | ESMAAPreset Preset = Inputs.Quality; 502 | ESMAAEdgeDetectors EdgeDetectorMode = Inputs.EdgeMode; 503 | auto Rounding = Inputs.CornerRounding * 0.01f; 504 | auto MaxStepDiag = Inputs.MaxDiagonalSearchSteps; 505 | auto MaxStepOrth = Inputs.MaxSearchSteps; 506 | auto ProjectionWeight = Inputs.ReprojectionWeight; 507 | ESMAAPredicationTexture PredicateSource = Inputs.PredicationSource; 508 | auto AdaptationFactor = Inputs.AdaptationFactor; 509 | auto PredicationThreshold = Inputs.PredicationThreshold; 510 | auto PredicationScale = Inputs.PredicationScale; 511 | auto PredicationStrength = Inputs.PredicationStrength; 512 | auto TemporalHistoryBias = Inputs.TemporalHistoryBias; 513 | 514 | // Wanted Predicate Texture 515 | FRDGTextureSRVRef PredicateTexture = GraphBuilder.CreateSRV(GSystemTextures.GetWhiteDummy(GraphBuilder)); 516 | switch (PredicateSource) 517 | { 518 | case ESMAAPredicationTexture::Depth: 519 | PredicateTexture = DepthSRV; 520 | break; 521 | case ESMAAPredicationTexture::WorldNormal: 522 | PredicateTexture = GraphBuilder.CreateSRV(InOutInputs.SceneTextures.SceneTextures->GetContents()->GBufferATexture); 523 | break; 524 | case ESMAAPredicationTexture::MRS: 525 | PredicateTexture = GraphBuilder.CreateSRV(InOutInputs.SceneTextures.SceneTextures->GetContents()->GBufferBTexture); 526 | //PredicateTexture = GraphBuilder.CreateSRV(Inputs.PredicateTexture.Texture); 527 | break; 528 | case ESMAAPredicationTexture::None:; 529 | case ESMAAPredicationTexture::MAX:; 530 | default:; 531 | } 532 | 533 | { 534 | FSMAAEdgeDetectionCS::FPermutationDomain PermutationVector; 535 | 536 | PermutationVector.Set(Preset); 537 | PermutationVector.Set(EdgeDetectorMode); 538 | PermutationVector.Set(ESMAAPredicationTexture::None != PredicateSource); 539 | 540 | FSMAAEdgeDetectionCS::FParameters* PassParameters = 541 | GraphBuilder.AllocParameters(); 542 | FRDGTextureUAVDesc OutputDesc(EdgesTexture); 543 | 544 | PassParameters->DepthTexture = SceneDepth; 545 | PassParameters->PointTextureSampler = PointClampSampler; 546 | PassParameters->BilinearTextureSampler = BilinearClampSampler; 547 | 548 | // Pass colour for Depth, Luma, and Colour 549 | if (EdgeDetectorMode < ESMAAEdgeDetectors::Normal) 550 | { 551 | PassParameters->InputSceneColor = ColourSRV; 552 | } 553 | else if (ESMAAEdgeDetectors::Normal == EdgeDetectorMode) 554 | { 555 | //PassParameters->InputSceneColor = GraphBuilder.CreateSRV(Inputs.WorldNormal.Texture); 556 | PassParameters->InputSceneColor = GraphBuilder.CreateSRV(InOutInputs.SceneTextures.SceneTextures->GetContents()->GBufferATexture); 557 | } 558 | 559 | PassParameters->InputDepth = DepthSRV; 560 | PassParameters->ViewportMetrics = RTMetrics; 561 | PassParameters->View = View.ViewUniformBuffer; 562 | PassParameters->NormalisedCornerRounding = Rounding; 563 | PassParameters->MaxSearchSteps = MaxStepOrth; 564 | PassParameters->MaxDiagonalSearchSteps = MaxStepDiag; 565 | PassParameters->Predicate = PredicateTexture; 566 | PassParameters->AdaptationFactor = AdaptationFactor; 567 | PassParameters->PredicationThreshold = PredicationThreshold; 568 | PassParameters->PredicationScale = PredicationScale; 569 | PassParameters->PredicationStrength = PredicationStrength; 570 | PassParameters->EdgesTexture = GraphBuilder.CreateUAV(OutputDesc); 571 | 572 | TShaderMapRef ComputeShaderSMAAED(View.ShaderMap, PermutationVector); 573 | FComputeShaderUtils::AddPass( 574 | GraphBuilder, RDG_EVENT_NAME("SMAA/EdgeDetection (CS)"), ComputeShaderSMAAED, PassParameters, 575 | FComputeShaderUtils::GetGroupCount(FIntVector(EdgesTexture->Desc.Extent.X, EdgesTexture->Desc.Extent.Y, 1), 576 | FIntVector(FSMAAEdgeDetectionCS::ThreadgroupSizeX, 577 | FSMAAEdgeDetectionCS::ThreadgroupSizeY, 578 | FSMAAEdgeDetectionCS::ThreadgroupSizeZ))); 579 | } 580 | 581 | // Blend 582 | { 583 | FSMAABlendingWeightsCS::FPermutationDomain PermutationVector; 584 | 585 | PermutationVector.Set(Preset); 586 | 587 | FSMAABlendingWeightsCS::FParameters* PassParameters = 588 | GraphBuilder.AllocParameters(); 589 | FRDGTextureUAVDesc OutputDesc(BlendTexture); 590 | 591 | PassParameters->DepthTexture = SceneDepth; 592 | PassParameters->PointTextureSampler = TStaticSamplerState::GetRHI(); 593 | PassParameters->BilinearTextureSampler = TStaticSamplerState::GetRHI(); 594 | PassParameters->AreaTexture = AreaTextureSRV; 595 | PassParameters->InputEdges = GraphBuilder.CreateSRV(EdgesSRVDesc); 596 | PassParameters->TemporalJitterPixels = FVector2f(View.TemporalJitterPixels); 597 | PassParameters->SubpixelWeights = SubpixelJitterWeights[ViewData->JitterIndex & 1]; 598 | PassParameters->SearchTexture = SearchTextureSRV; 599 | PassParameters->ViewportMetrics = RTMetrics; 600 | PassParameters->View = View.ViewUniformBuffer; 601 | PassParameters->NormalisedCornerRounding = Rounding; 602 | PassParameters->MaxSearchSteps = MaxStepOrth; 603 | PassParameters->MaxDiagonalSearchSteps = MaxStepDiag; 604 | PassParameters->BlendTexture = GraphBuilder.CreateUAV(OutputDesc); 605 | 606 | TShaderMapRef ComputeShaderSMAABW(View.ShaderMap, PermutationVector); 607 | FComputeShaderUtils::AddPass( 608 | GraphBuilder, RDG_EVENT_NAME("SMAA/BlendWeights (CS)"), ComputeShaderSMAABW, PassParameters, 609 | FComputeShaderUtils::GetGroupCount(FIntVector(EdgesTexture->Desc.Extent.X, EdgesTexture->Desc.Extent.Y, 1), 610 | FIntVector(FSMAABlendingWeightsCS::ThreadgroupSizeX, 611 | FSMAABlendingWeightsCS::ThreadgroupSizeY, 612 | FSMAABlendingWeightsCS::ThreadgroupSizeZ))); 613 | } 614 | 615 | // Neighbourhood Blending 616 | { 617 | FSMAANeighbourhoodBlendingCS::FPermutationDomain PermutationVector; 618 | 619 | PermutationVector.Set(Preset); 620 | PermutationVector.Set(true); 621 | 622 | FSMAANeighbourhoodBlendingCS::FParameters* PassParameters = 623 | GraphBuilder.AllocParameters(); 624 | FRDGTextureUAVDesc OutputDesc(EdgesTexture); 625 | 626 | PassParameters->DepthTexture = SceneDepth; 627 | PassParameters->PointTextureSampler = PointClampSampler; 628 | PassParameters->BilinearTextureSampler = BilinearClampSampler; 629 | PassParameters->SceneColour = GraphBuilder.CreateSRV(SceneColourSRVDesc); 630 | PassParameters->InputBlend = GraphBuilder.CreateSRV(BlendSRVDesc); 631 | PassParameters->SceneDepth = DepthSRV; 632 | PassParameters->VelocityTexture = InOutInputs.GetInput(EPostProcessMaterialInput::Velocity).TextureSRV; 633 | PassParameters->ViewportMetrics = RTMetrics; 634 | PassParameters->View = View.ViewUniformBuffer; 635 | PassParameters->NormalisedCornerRounding = Rounding; 636 | PassParameters->MaxSearchSteps = MaxStepOrth; 637 | PassParameters->MaxDiagonalSearchSteps = MaxStepDiag; 638 | 639 | // Write out to Final if bCameraCut 640 | if (bCameraCut) 641 | { 642 | PassParameters->FinalFrame = GraphBuilder.CreateUAV(Output.Texture); 643 | } 644 | else 645 | { 646 | PassParameters->FinalFrame = GraphBuilder.CreateUAV(OutputDesc); 647 | } 648 | 649 | TShaderMapRef ComputeShaderSMAANB(View.ShaderMap, PermutationVector); 650 | FComputeShaderUtils::AddPass( 651 | GraphBuilder, RDG_EVENT_NAME("SMAA/NeighbourhoodBlending (CS)"), ComputeShaderSMAANB, PassParameters, 652 | FComputeShaderUtils::GetGroupCount(FIntVector(EdgesTexture->Desc.Extent.X, EdgesTexture->Desc.Extent.Y, 1), 653 | FIntVector(FSMAANeighbourhoodBlendingCS::ThreadgroupSizeX, 654 | FSMAANeighbourhoodBlendingCS::ThreadgroupSizeY, 655 | FSMAANeighbourhoodBlendingCS::ThreadgroupSizeZ))); 656 | } 657 | 658 | // Temporal Resolve 659 | if (!bCameraCut) 660 | { 661 | FSMAATemporalResolveCS::FPermutationDomain PermutationVector; 662 | 663 | PermutationVector.Set(Preset); 664 | PermutationVector.Set(true); 665 | 666 | FSMAATemporalResolveCS::FParameters* PassParameters = 667 | GraphBuilder.AllocParameters(); 668 | 669 | PassParameters->DepthTexture = SceneDepth; 670 | PassParameters->PointTextureSampler = PointClampSampler; 671 | PassParameters->BilinearTextureSampler = BilinearClampSampler; 672 | PassParameters->CurrentSceneColour = GraphBuilder.CreateSRV(EdgesSRVDesc); 673 | PassParameters->PastSceneColour = GraphBuilder.CreateSRV(PrevSceneColourSRVDesc); 674 | PassParameters->VelocityTexture = GraphBuilder.CreateSRV(VelocityDesc); 675 | PassParameters->SceneDepth = DepthSRV; 676 | PassParameters->ViewportMetrics = RTMetrics; 677 | PassParameters->View = View.ViewUniformBuffer; 678 | PassParameters->NormalisedCornerRounding = Rounding; 679 | PassParameters->MaxSearchSteps = MaxStepOrth; 680 | PassParameters->MaxDiagonalSearchSteps = MaxStepDiag; 681 | PassParameters->ReprojectionWeight = ProjectionWeight; 682 | PassParameters->TemporalHistoryBias = TemporalHistoryBias; 683 | PassParameters->Resolved = GraphBuilder.CreateUAV(Output.Texture); 684 | 685 | TShaderMapRef ComputeShaderSMAATR(View.ShaderMap, PermutationVector); 686 | FComputeShaderUtils::AddPass( 687 | GraphBuilder, RDG_EVENT_NAME("SMAA/TemporalResolve (CS)"), ComputeShaderSMAATR, PassParameters, 688 | FComputeShaderUtils::GetGroupCount(FIntVector(EdgesTexture->Desc.Extent.X, EdgesTexture->Desc.Extent.Y, 1), 689 | FIntVector(FSMAATemporalResolveCS::ThreadgroupSizeX, 690 | FSMAATemporalResolveCS::ThreadgroupSizeY, 691 | FSMAATemporalResolveCS::ThreadgroupSizeZ))); 692 | } 693 | 694 | if (!View.bStatePrevViewInfoIsReadOnly) 695 | { 696 | //FSMAAHistory& History = View.ViewState->PrevFrameViewInfo.SMAAHistory; 697 | FSMAAHistory& History = ViewData->SMAAHistory; 698 | History.SafeRelease(); 699 | 700 | GraphBuilder.QueueTextureExtraction(Output.Texture, &History.PastFrame); 701 | History.ViewportRect = InputRect; 702 | } 703 | 704 | return Output; 705 | } 706 | 707 | FScreenPassTexture AddVisualizeSMAAPasses(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSMAAInputs& Inputs, const FPostProcessMaterialInputs& InOutInputs, TSharedRef ViewData) 708 | { 709 | check(Inputs.SceneColor.IsValid()); 710 | check(Inputs.Quality != ESMAAPreset::MAX); 711 | check(Inputs.EdgeMode != ESMAAEdgeDetectors::MAX); 712 | RDG_EVENT_SCOPE(GraphBuilder, "SMAA T2x Visualizer"); 713 | 714 | FIntPoint InputExtents = Inputs.SceneColor.Texture->Desc.Extent; // View.ViewRect.Size(); 715 | FIntRect InputRect = View.ViewRect; 716 | InputRect.Min = FIntPoint(0, 0); 717 | InputRect.Min = InputExtents; 718 | FIntPoint BackingSize = InputExtents; 719 | QuantizeSceneBufferSize(InputExtents, BackingSize); 720 | 721 | FIntRect OutputRect = View.ViewRect; 722 | FIntPoint OutputExtents = View.ViewRect.Size(); 723 | 724 | FScreenPassTexture Output = Inputs.OverrideOutput; 725 | 726 | if (!Output.IsValid()) 727 | { 728 | FRDGTextureDesc WriteOutTextureDesc = 729 | FRDGTextureDesc::Create2D(BackingSize, PF_FloatRGBA, FClearValueBinding::Black, 730 | TexCreate_ShaderResource | TexCreate_UAV | TexCreate_RenderTargetable); 731 | 732 | Output = FScreenPassTexture( 733 | GraphBuilder.CreateTexture(WriteOutTextureDesc, TEXT("SMAA.Output")), 734 | OutputRect); 735 | } 736 | 737 | FRHISamplerState* BilinearClampSampler = TStaticSamplerState::GetRHI(); 738 | FRHISamplerState* PointClampSampler = TStaticSamplerState::GetRHI(); 739 | 740 | auto RTMetrics = FVector4f(1.0 / BackingSize.X, 1.0 / BackingSize.Y, BackingSize.X, BackingSize.Y); 741 | 742 | const FTexture2DResource* AreaResource = ViewData->SMAAAreaTexture; 743 | if (!AreaResource) 744 | { 745 | // Bail 746 | return Inputs.SceneColor; 747 | } 748 | 749 | const FTexture2DResource* SearchResource = ViewData->SMAASearchTexture; 750 | if (!SearchResource) 751 | { 752 | // Bail 753 | return Inputs.SceneColor; 754 | } 755 | 756 | FRHITexture* AreaTextureRHI = AreaResource->GetTexture2DRHI(); 757 | if (!AreaTextureRHI) 758 | { 759 | // Bail 760 | return Inputs.SceneColor; 761 | } 762 | FRDGTextureRef AreaTexture = RegisterExternalTexture(GraphBuilder, AreaTextureRHI, TEXT("SMAA.AreaTexture")); 763 | 764 | FRHITexture* SearchTextureRHI = SearchResource->GetTexture2DRHI(); 765 | if (!SearchTextureRHI) 766 | { 767 | // Bail 768 | return Inputs.SceneColor; 769 | } 770 | FRDGTextureRef SearchTexture = RegisterExternalTexture(GraphBuilder, SearchTextureRHI, TEXT("SMAA.SearchTexture")); 771 | 772 | // Create only Edges texture. We're writing straight out to output on blend 773 | FRDGTextureDesc EdgesTextureDesc = 774 | FRDGTextureDesc::Create2D(BackingSize, PF_FloatRGBA, FClearValueBinding::Black, 775 | TexCreate_ShaderResource | TexCreate_UAV | TexCreate_RenderTargetable); 776 | 777 | FRDGTextureRef EdgesTexture = GraphBuilder.CreateTexture(EdgesTextureDesc, TEXT("SMAA.EdgesTexture")); 778 | 779 | FRDGTextureRef SceneColor = Inputs.SceneColor.Texture; 780 | //FRDGTextureRef SceneDepth = Inputs.SceneDepth.Texture; 781 | FRDGTextureRef Velocity = Inputs.SceneVelocity.Texture; 782 | 783 | // Create Depth SRV Desc 784 | //FRDGTextureSRVDesc DepthSRVDesc = FRDGTextureSRVDesc::Create(SceneDepth); 785 | FRDGTextureSRVDesc AreaTextureSRVDesc = FRDGTextureSRVDesc::Create(AreaTexture); 786 | FRDGTextureSRVDesc SearchTextureSRVDesc = FRDGTextureSRVDesc::Create(SearchTexture); 787 | FRDGTextureSRVDesc SceneColourSRVDesc = FRDGTextureSRVDesc::Create(SceneColor); 788 | FRDGTextureSRVDesc EdgesSRVDesc = FRDGTextureSRVDesc::Create(EdgesTexture); 789 | FRDGTextureSRVDesc WriteOutTextureSRVDesc = FRDGTextureSRVDesc::Create(Output.Texture); 790 | FRDGTextureSRVDesc VelocityDesc = FRDGTextureSRVDesc::Create(Velocity); 791 | 792 | FRDGTextureSRVRef AreaTextureSRV = GraphBuilder.CreateSRV(AreaTextureSRVDesc); 793 | FRDGTextureSRVRef SearchTextureSRV = GraphBuilder.CreateSRV(SearchTextureSRVDesc); 794 | FRDGTextureSRVRef DepthSRV = GraphBuilder.CreateSRV(InOutInputs.SceneTextures.SceneTextures->GetContents()->SceneDepthTexture); 795 | FRDGTextureSRVRef ColourSRV = GraphBuilder.CreateSRV(SceneColourSRVDesc); 796 | 797 | TRDGTextureAccess SceneDepth = InOutInputs.SceneTextures.SceneTextures->GetContents()->SceneDepthTexture; 798 | 799 | // Permutations 800 | ESMAAPreset Preset = Inputs.Quality; 801 | ESMAAEdgeDetectors EdgeDetectorMode = Inputs.EdgeMode; 802 | auto Rounding = Inputs.CornerRounding * 0.01f; 803 | auto MaxStepDiag = Inputs.MaxDiagonalSearchSteps; 804 | auto MaxStepOrth = Inputs.MaxSearchSteps; 805 | ESMAAPredicationTexture PredicateSource = Inputs.PredicationSource; 806 | auto AdaptationFactor = Inputs.AdaptationFactor; 807 | auto PredicationThreshold = Inputs.PredicationThreshold; 808 | auto PredicationScale = Inputs.PredicationScale; 809 | auto PredicationStrength = Inputs.PredicationStrength; 810 | 811 | // Wanted Predicate Texture 812 | FRDGTextureSRVRef PredicateTexture = GraphBuilder.CreateSRV(GSystemTextures.GetWhiteDummy(GraphBuilder)); 813 | switch (PredicateSource) 814 | { 815 | case ESMAAPredicationTexture::Depth: 816 | PredicateTexture = DepthSRV; 817 | break; 818 | case ESMAAPredicationTexture::WorldNormal: 819 | PredicateTexture = GraphBuilder.CreateSRV(InOutInputs.SceneTextures.SceneTextures->GetContents()->GBufferATexture); 820 | break; 821 | case ESMAAPredicationTexture::MRS: 822 | PredicateTexture = GraphBuilder.CreateSRV(InOutInputs.SceneTextures.SceneTextures->GetContents()->GBufferBTexture); 823 | break; 824 | case ESMAAPredicationTexture::None:; 825 | case ESMAAPredicationTexture::MAX:; 826 | default:; 827 | } 828 | 829 | { 830 | FSMAAEdgeDetectionCS::FPermutationDomain PermutationVector; 831 | 832 | PermutationVector.Set(Preset); 833 | PermutationVector.Set(EdgeDetectorMode); 834 | PermutationVector.Set(ESMAAPredicationTexture::None != PredicateSource); 835 | 836 | FSMAAEdgeDetectionCS::FParameters* PassParameters = 837 | GraphBuilder.AllocParameters(); 838 | FRDGTextureUAVDesc OutputDesc(EdgesTexture); 839 | 840 | PassParameters->DepthTexture = SceneDepth; 841 | PassParameters->PointTextureSampler = PointClampSampler; 842 | PassParameters->BilinearTextureSampler = BilinearClampSampler; 843 | 844 | // Pass colour for Depth, Luma, and Colour 845 | if (EdgeDetectorMode < ESMAAEdgeDetectors::Normal) 846 | { 847 | PassParameters->InputSceneColor = ColourSRV; 848 | } 849 | else if (ESMAAEdgeDetectors::Normal == EdgeDetectorMode) 850 | { 851 | PassParameters->InputSceneColor = GraphBuilder.CreateSRV(InOutInputs.SceneTextures.SceneTextures->GetContents()->GBufferATexture); 852 | } 853 | 854 | PassParameters->InputDepth = DepthSRV; 855 | PassParameters->ViewportMetrics = RTMetrics; 856 | PassParameters->View = View.ViewUniformBuffer; 857 | PassParameters->NormalisedCornerRounding = Rounding; 858 | PassParameters->MaxSearchSteps = MaxStepOrth; 859 | PassParameters->MaxDiagonalSearchSteps = MaxStepDiag; 860 | PassParameters->Predicate = PredicateTexture; 861 | PassParameters->AdaptationFactor = AdaptationFactor; 862 | PassParameters->PredicationThreshold = PredicationThreshold; 863 | PassParameters->PredicationScale = PredicationScale; 864 | PassParameters->PredicationStrength = PredicationStrength; 865 | PassParameters->EdgesTexture = GraphBuilder.CreateUAV(OutputDesc); 866 | 867 | TShaderMapRef ComputeShaderSMAAED(View.ShaderMap, PermutationVector); 868 | FComputeShaderUtils::AddPass( 869 | GraphBuilder, RDG_EVENT_NAME("SMAA/EdgeDetection (CS)"), ComputeShaderSMAAED, PassParameters, 870 | FComputeShaderUtils::GetGroupCount(FIntVector(EdgesTexture->Desc.Extent.X, EdgesTexture->Desc.Extent.Y, 1), 871 | FIntVector(FSMAAEdgeDetectionCS::ThreadgroupSizeX, 872 | FSMAAEdgeDetectionCS::ThreadgroupSizeY, 873 | FSMAAEdgeDetectionCS::ThreadgroupSizeZ))); 874 | } 875 | 876 | // Blend 877 | { 878 | FSMAABlendingWeightsCS::FPermutationDomain PermutationVector; 879 | 880 | PermutationVector.Set(Preset); 881 | 882 | FSMAABlendingWeightsCS::FParameters* PassParameters = 883 | GraphBuilder.AllocParameters(); 884 | 885 | PassParameters->DepthTexture = SceneDepth; 886 | PassParameters->PointTextureSampler = TStaticSamplerState::GetRHI(); 887 | PassParameters->BilinearTextureSampler = TStaticSamplerState::GetRHI(); 888 | PassParameters->AreaTexture = AreaTextureSRV; 889 | PassParameters->InputEdges = GraphBuilder.CreateSRV(EdgesSRVDesc); 890 | PassParameters->TemporalJitterPixels = FVector2f(View.TemporalJitterPixels); 891 | PassParameters->SubpixelWeights = SubpixelJitterWeights[View.TemporalJitterIndex & 1]; 892 | PassParameters->SearchTexture = SearchTextureSRV; 893 | PassParameters->ViewportMetrics = RTMetrics; 894 | PassParameters->View = View.ViewUniformBuffer; 895 | PassParameters->NormalisedCornerRounding = Rounding; 896 | PassParameters->MaxSearchSteps = MaxStepOrth; 897 | PassParameters->MaxDiagonalSearchSteps = MaxStepDiag; 898 | PassParameters->BlendTexture = GraphBuilder.CreateUAV(Output.Texture); 899 | 900 | TShaderMapRef ComputeShaderSMAABW(View.ShaderMap, PermutationVector); 901 | FComputeShaderUtils::AddPass( 902 | GraphBuilder, RDG_EVENT_NAME("SMAA/BlendWeights (CS)"), ComputeShaderSMAABW, PassParameters, 903 | FComputeShaderUtils::GetGroupCount(FIntVector(EdgesTexture->Desc.Extent.X, EdgesTexture->Desc.Extent.Y, 1), 904 | FIntVector(FSMAABlendingWeightsCS::ThreadgroupSizeX, 905 | FSMAABlendingWeightsCS::ThreadgroupSizeY, 906 | FSMAABlendingWeightsCS::ThreadgroupSizeZ))); 907 | } 908 | 909 | return Output; 910 | } 911 | UE_ENABLE_OPTIMIZATION -------------------------------------------------------------------------------- /Shaders/Private/SMAAReference.usf: -------------------------------------------------------------------------------- 1 | // We want to maintain the original SMAA implementation, but the way some things are defined is 2 | // problematic 3 | float NormalisedCornerRounding; 4 | float MaxSearchSteps; 5 | float MaxDiagonalSearchSteps; 6 | 7 | /** 8 | * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) 9 | * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) 10 | * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) 11 | * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) 12 | * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) 13 | * 14 | * Permission is hereby granted, free of charge, to any person obtaining a copy 15 | * this software and associated documentation files (the "Software"), to deal in 16 | * the Software without restriction, including without limitation the rights to 17 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 18 | * of the Software, and to permit persons to whom the Software is furnished to 19 | * do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be included in 22 | * all copies or substantial portions of the Software. As clarification, there 23 | * is no requirement that the copyright notice and permission be included in 24 | * binary distributions of the Software. 25 | * 26 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | * SOFTWARE. 33 | */ 34 | 35 | 36 | /** 37 | * _______ ___ ___ ___ ___ 38 | * / || \/ | / \ / \ 39 | * | (---- | \ / | / ^ \ / ^ \ 40 | * \ \ | |\/| | / /_\ \ / /_\ \ 41 | * ----) | | | | | / _____ \ / _____ \ 42 | * |_______/ |__| |__| /__/ \__\ /__/ \__\ 43 | * 44 | * E N H A N C E D 45 | * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G 46 | * 47 | * http://www.iryoku.com/smaa/ 48 | * 49 | * Hi, welcome aboard! 50 | * 51 | * Here you'll find instructions to get the shader up and running as fast as 52 | * possible. 53 | * 54 | * IMPORTANTE NOTICE: when updating, remember to update both this file and the 55 | * precomputed textures! They may change from version to version. 56 | * 57 | * The shader has three passes, chained together as follows: 58 | * 59 | * |input|------------------� 60 | * v | 61 | * [ SMAA*EdgeDetection ] | 62 | * v | 63 | * |edgesTex| | 64 | * v | 65 | * [ SMAABlendingWeightCalculation ] | 66 | * v | 67 | * |blendTex| | 68 | * v | 69 | * [ SMAANeighborhoodBlending ] <------� 70 | * v 71 | * |output| 72 | * 73 | * Note that each [pass] has its own vertex and pixel shader. Remember to use 74 | * oversized triangles instead of quads to avoid overshading along the 75 | * diagonal. 76 | * 77 | * You've three edge detection methods to choose from: luma, color or depth. 78 | * They represent different quality/performance and anti-aliasing/sharpness 79 | * tradeoffs, so our recommendation is for you to choose the one that best 80 | * suits your particular scenario: 81 | * 82 | * - Depth edge detection is usually the fastest but it may miss some edges. 83 | * 84 | * - Luma edge detection is usually more expensive than depth edge detection, 85 | * but catches visible edges that depth edge detection can miss. 86 | * 87 | * - Color edge detection is usually the most expensive one but catches 88 | * chroma-only edges. 89 | * 90 | * For quickstarters: just use luma edge detection. 91 | * 92 | * The general advice is to not rush the integration process and ensure each 93 | * step is done correctly (don't try to integrate SMAA T2x with predicated edge 94 | * detection from the start!). Ok then, let's go! 95 | * 96 | * 1. The first step is to create two RGBA temporal render targets for holding 97 | * |edgesTex| and |blendTex|. 98 | * 99 | * In DX10 or DX11, you can use a RG render target for the edges texture. 100 | * In the case of NVIDIA GPUs, using RG render targets seems to actually be 101 | * slower. 102 | * 103 | * On the Xbox 360, you can use the same render target for resolving both 104 | * |edgesTex| and |blendTex|, as they aren't needed simultaneously. 105 | * 106 | * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared 107 | * each frame. Do not forget to clear the alpha channel! 108 | * 109 | * 3. The next step is loading the two supporting precalculated textures, 110 | * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as 111 | * C++ headers, and also as regular DDS files. They'll be needed for the 112 | * 'SMAABlendingWeightCalculation' pass. 113 | * 114 | * If you use the C++ headers, be sure to load them in the format specified 115 | * inside of them. 116 | * 117 | * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 118 | * respectively, if you have that option in your content processor pipeline. 119 | * When compressing then, you get a non-perceptible quality decrease, and a 120 | * marginal performance increase. 121 | * 122 | * 4. All samplers must be set to linear filtering and clamp. 123 | * 124 | * After you get the technique working, remember that 64-bit inputs have 125 | * half-rate linear filtering on GCN. 126 | * 127 | * If SMAA is applied to 64-bit color buffers, switching to point filtering 128 | * when accesing them will increase the performance. Search for 129 | * 'SMAASamplePoint' to see which textures may benefit from point 130 | * filtering, and where (which is basically the color input in the edge 131 | * detection and resolve passes). 132 | * 133 | * 5. All texture reads and buffer writes must be non-sRGB, with the exception 134 | * of the input read and the output write in 135 | * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in 136 | * this last pass are not possible, the technique will work anyway, but 137 | * will perform antialiasing in gamma space. 138 | * 139 | * IMPORTANT: for best results the input read for the color/luma edge 140 | * detection should *NOT* be sRGB. 141 | * 142 | * 6. Before including SMAA.h you'll have to setup the render target metrics, 143 | * the target and any optional configuration defines. Optionally you can 144 | * use a preset. 145 | * 146 | * You have the following targets available: 147 | * SMAA_HLSL_3 148 | * SMAA_HLSL_4 149 | * SMAA_HLSL_4_1 150 | * SMAA_GLSL_3 * 151 | * SMAA_GLSL_4 * 152 | * 153 | * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). 154 | * 155 | * And four presets: 156 | * SMAA_PRESET_LOW (%60 of the quality) 157 | * SMAA_PRESET_MEDIUM (%80 of the quality) 158 | * SMAA_PRESET_HIGH (%95 of the quality) 159 | * SMAA_PRESET_ULTRA (%99 of the quality) 160 | * 161 | * For example: 162 | * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) 163 | * #define SMAA_HLSL_4 164 | * #define SMAA_PRESET_HIGH 165 | * include "SMAA.h" 166 | * 167 | * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a 168 | * uniform variable. The code is designed to minimize the impact of not 169 | * using a constant value, but it is still better to hardcode it. 170 | * 171 | * Depending on how you encoded 'areaTex' and 'searchTex', you may have to 172 | * add (and customize) the following defines before including SMAA.h: 173 | * #define SMAA_AREATEX_SELECT(sample) sample.rg 174 | * #define SMAA_SEARCHTEX_SELECT(sample) sample.r 175 | * 176 | * If your engine is already using porting macros, you can define 177 | * SMAA_CUSTOM_SL, and define the porting functions by yourself. 178 | * 179 | * 7. Then, you'll have to setup the passes as indicated in the scheme above. 180 | * You can take a look into SMAA.fx, to see how we did it for our demo. 181 | * Checkout the function wrappers, you may want to copy-paste them! 182 | * 183 | * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. 184 | * You can use a screenshot from your engine to compare the |edgesTex| 185 | * and |blendTex| produced inside of the engine with the results obtained 186 | * with the reference demo. 187 | * 188 | * 9. After you get the last pass to work, it's time to optimize. You'll have 189 | * to initialize a stencil buffer in the first pass (discard is already in 190 | * the code), then mask execution by using it the second pass. The last 191 | * pass should be executed in all pixels. 192 | * 193 | * 194 | * After this point you can choose to enable predicated thresholding, 195 | * temporal supersampling and motion blur integration: 196 | * 197 | * a) If you want to use predicated thresholding, take a look into 198 | * SMAA_PREDICATION; you'll need to pass an extra texture in the edge 199 | * detection pass. 200 | * 201 | * b) If you want to enable temporal supersampling (SMAA T2x): 202 | * 203 | * 1. The first step is to render using subpixel jitters. I won't go into 204 | * detail, but it's as simple as moving each vertex position in the 205 | * vertex shader, you can check how we do it in our DX10 demo. 206 | * 207 | * 2. Then, you must setup the temporal resolve. You may want to take a look 208 | * into SMAAResolve for resolving 2x modes. After you get it working, you'll 209 | * probably see ghosting everywhere. But fear not, you can enable the 210 | * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. 211 | * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. 212 | * 213 | * 3. The next step is to apply SMAA to each subpixel jittered frame, just as 214 | * done for 1x. 215 | * 216 | * 4. At this point you should already have something usable, but for best 217 | * results the proper area textures must be set depending on current jitter. 218 | * For this, the parameter 'subsampleIndices' of 219 | * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x 220 | * mode: 221 | * 222 | * @SUBSAMPLE_INDICES 223 | * 224 | * | S# | Camera Jitter | subsampleIndices | 225 | * +----+------------------+---------------------+ 226 | * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | 227 | * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | 228 | * 229 | * These jitter positions assume a bottom-to-top y axis. S# stands for the 230 | * sample number. 231 | * 232 | * More information about temporal supersampling here: 233 | * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf 234 | * 235 | * c) If you want to enable spatial multisampling (SMAA S2x): 236 | * 237 | * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be 238 | * created with: 239 | * - DX10: see below (*) 240 | * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or 241 | * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN 242 | * 243 | * This allows to ensure that the subsample order matches the table in 244 | * @SUBSAMPLE_INDICES. 245 | * 246 | * (*) In the case of DX10, we refer the reader to: 247 | * - SMAA::detectMSAAOrder and 248 | * - SMAA::msaaReorder 249 | * 250 | * These functions allow to match the standard multisample patterns by 251 | * detecting the subsample order for a specific GPU, and reordering 252 | * them appropriately. 253 | * 254 | * 2. A shader must be run to output each subsample into a separate buffer 255 | * (DX10 is required). You can use SMAASeparate for this purpose, or just do 256 | * it in an existing pass (for example, in the tone mapping pass, which has 257 | * the advantage of feeding tone mapped subsamples to SMAA, which will yield 258 | * better results). 259 | * 260 | * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing 261 | * the results in the final buffer. The second run should alpha blend with 262 | * the existing final buffer using a blending factor of 0.5. 263 | * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point 264 | * b). 265 | * 266 | * d) If you want to enable temporal supersampling on top of SMAA S2x 267 | * (which actually is SMAA 4x): 268 | * 269 | * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is 270 | * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' 271 | * must be set as follows: 272 | * 273 | * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | 274 | * +----+----+--------------------+-------------------+----------------------+ 275 | * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | 276 | * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | 277 | * +----+----+--------------------+-------------------+----------------------+ 278 | * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | 279 | * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | 280 | * 281 | * These jitter positions assume a bottom-to-top y axis. F# stands for the 282 | * frame number. S# stands for the sample number. 283 | * 284 | * 2. After calculating SMAA S2x for current frame (with the new subsample 285 | * indices), previous frame must be reprojected as in SMAA T2x mode (see 286 | * point b). 287 | * 288 | * e) If motion blur is used, you may want to do the edge detection pass 289 | * together with motion blur. This has two advantages: 290 | * 291 | * 1. Pixels under heavy motion can be omitted from the edge detection process. 292 | * For these pixels we can just store "no edge", as motion blur will take 293 | * care of them. 294 | * 2. The center pixel tap is reused. 295 | * 296 | * Note that in this case depth testing should be used instead of stenciling, 297 | * as we have to write all the pixels in the motion blur pass. 298 | * 299 | * That's it! 300 | */ 301 | 302 | //----------------------------------------------------------------------------- 303 | // SMAA Presets 304 | 305 | /** 306 | * Note that if you use one of these presets, the following configuration 307 | * macros will be ignored if set in the "Configurable Defines" section. 308 | */ 309 | 310 | #if defined(SMAA_PRESET_LOW) 311 | 312 | #ifndef SMAA_THRESHOLD 313 | #define SMAA_THRESHOLD 0.15 314 | #endif // SMAA_THRESHOLD 315 | 316 | #ifndef SMAA_MAX_SEARCH_STEPS 317 | #define SMAA_MAX_SEARCH_STEPS 4 318 | #endif // SMAA_MAX_SEARCH_STEPS 319 | 320 | #ifndef SMAA_DISABLE_DIAG_DETECTION 321 | #define SMAA_DISABLE_DIAG_DETECTION 322 | #endif // SMAA_DISABLE_DIAG_DETECTION 323 | 324 | #ifndef SMAA_DISABLE_CORNER_DETECTION 325 | #define SMAA_DISABLE_CORNER_DETECTION 326 | #endif // SMAA_DISABLE_CORNER_DETECTION 327 | 328 | 329 | #elif defined(SMAA_PRESET_MEDIUM) 330 | 331 | #ifndef SMAA_THRESHOLD 332 | #define SMAA_THRESHOLD 0.1 333 | #endif // SMAA_THRESHOLD 334 | 335 | #ifndef SMAA_MAX_SEARCH_STEPS 336 | #define SMAA_MAX_SEARCH_STEPS 8 337 | #endif // SMAA_MAX_SEARCH_STEPS 338 | 339 | #ifndef SMAA_DISABLE_DIAG_DETECTION 340 | #define SMAA_DISABLE_DIAG_DETECTION 341 | #endif // SMAA_DISABLE_DIAG_DETECTION 342 | 343 | #ifndef SMAA_DISABLE_CORNER_DETECTION 344 | #define SMAA_DISABLE_CORNER_DETECTION 345 | #endif // SMAA_DISABLE_CORNER_DETECTION 346 | 347 | 348 | #elif defined(SMAA_PRESET_HIGH) 349 | 350 | #ifndef SMAA_THRESHOLD 351 | #define SMAA_THRESHOLD 0.1 352 | #endif // SMAA_THRESHOLD 353 | 354 | #ifndef SMAA_MAX_SEARCH_STEPS 355 | #define SMAA_MAX_SEARCH_STEPS 16 356 | #endif // SMAA_MAX_SEARCH_STEPS 357 | 358 | #ifndef SMAA_MAX_SEARCH_STEPS_DIAG 359 | #define SMAA_MAX_SEARCH_STEPS_DIAG 8 360 | #endif // SMAA_MAX_SEARCH_STEPS_DIAG 361 | 362 | #ifndef SMAA_CORNER_ROUNDING 363 | #define SMAA_CORNER_ROUNDING 25 364 | #endif // SMAA_CORNER_ROUNDING 365 | 366 | 367 | #elif defined(SMAA_PRESET_ULTRA) 368 | 369 | #ifndef SMAA_THRESHOLD 370 | #define SMAA_THRESHOLD 0.05 371 | #endif // SMAA_THRESHOLD 372 | 373 | #ifndef SMAA_MAX_SEARCH_STEPS 374 | #define SMAA_MAX_SEARCH_STEPS 32 375 | #endif // SMAA_MAX_SEARCH_STEPS 376 | 377 | #ifndef SMAA_MAX_SEARCH_STEPS_DIAG 378 | #define SMAA_MAX_SEARCH_STEPS_DIAG 16 379 | #endif // SMAA_MAX_SEARCH_STEPS_DIAG 380 | 381 | #ifndef SMAA_CORNER_ROUNDING 382 | #define SMAA_CORNER_ROUNDING 25 383 | #endif // SMAA_CORNER_ROUNDING 384 | 385 | #endif 386 | 387 | //----------------------------------------------------------------------------- 388 | // Configurable Defines 389 | 390 | /** 391 | * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. 392 | * Lowering this value you will be able to detect more edges at the expense of 393 | * performance. 394 | * 395 | * Range: [0, 0.5] 396 | * 0.1 is a reasonable value, and allows to catch most visible edges. 397 | * 0.05 is a rather overkill value, that allows to catch 'em all. 398 | * 399 | * If temporal supersampling is used, 0.2 could be a reasonable value, as low 400 | * contrast edges are properly filtered by just 2x. 401 | */ 402 | #ifndef SMAA_THRESHOLD 403 | #define SMAA_THRESHOLD 0.1 404 | #endif 405 | 406 | /** 407 | * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. 408 | * 409 | * Range: depends on the depth range of the scene. 410 | */ 411 | #ifndef SMAA_DEPTH_THRESHOLD 412 | #define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) 413 | #endif 414 | 415 | /** 416 | * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the 417 | * horizontal/vertical pattern searches, at each side of the pixel. 418 | * 419 | * In number of pixels, it's actually the double. So the maximum line length 420 | * perfectly handled by, for example 16, is 64 (by perfectly, we meant that 421 | * longer lines won't look as good, but still antialiased). 422 | * 423 | * Range: [0, 112] 424 | */ 425 | #ifndef SMAA_MAX_SEARCH_STEPS 426 | #define SMAA_MAX_SEARCH_STEPS 16 427 | #endif 428 | 429 | /** 430 | * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the 431 | * diagonal pattern searches, at each side of the pixel. In this case we jump 432 | * one pixel at time, instead of two. 433 | * 434 | * Range: [0, 20] 435 | * 436 | * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 437 | * steps), but it can have a significant impact on older machines. 438 | * 439 | * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. 440 | */ 441 | #ifndef SMAA_MAX_SEARCH_STEPS_DIAG 442 | #define SMAA_MAX_SEARCH_STEPS_DIAG 8 443 | #endif 444 | 445 | /** 446 | * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. 447 | * 448 | * Range: [0, 100] 449 | * 450 | * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. 451 | */ 452 | #ifndef SMAA_CORNER_ROUNDING 453 | #define SMAA_CORNER_ROUNDING 25 454 | #endif 455 | 456 | /** 457 | * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times 458 | * bigger contrast than current edge, current edge will be discarded. 459 | * 460 | * This allows to eliminate spurious crossing edges, and is based on the fact 461 | * that, if there is too much contrast in a direction, that will hide 462 | * perceptually contrast in the other neighbors. 463 | */ 464 | #ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 465 | #define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 466 | #endif 467 | 468 | /** 469 | * Predicated thresholding allows to better preserve texture details and to 470 | * improve performance, by decreasing the number of detected edges using an 471 | * additional buffer like the light accumulation buffer, object ids or even the 472 | * depth buffer (the depth buffer usage may be limited to indoor or short range 473 | * scenes). 474 | * 475 | * It locally decreases the luma or color threshold if an edge is found in an 476 | * additional buffer (so the global threshold can be higher). 477 | * 478 | * This method was developed by Playstation EDGE MLAA team, and used in 479 | * Killzone 3, by using the light accumulation buffer. More information here: 480 | * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx 481 | */ 482 | #ifndef SMAA_PREDICATION 483 | #define SMAA_PREDICATION 0 484 | #endif 485 | 486 | /** 487 | * Threshold to be used in the additional predication buffer. 488 | * 489 | * Range: depends on the input, so you'll have to find the magic number that 490 | * works for you. 491 | */ 492 | #ifndef SMAA_PREDICATION_THRESHOLD 493 | #define SMAA_PREDICATION_THRESHOLD 0.01 494 | #endif 495 | 496 | /** 497 | * How much to scale the global threshold used for luma or color edge 498 | * detection when using predication. 499 | * 500 | * Range: [1, 5] 501 | */ 502 | #ifndef SMAA_PREDICATION_SCALE 503 | #define SMAA_PREDICATION_SCALE 2.0 504 | #endif 505 | 506 | /** 507 | * How much to locally decrease the threshold. 508 | * 509 | * Range: [0, 1] 510 | */ 511 | #ifndef SMAA_PREDICATION_STRENGTH 512 | #define SMAA_PREDICATION_STRENGTH 0.4 513 | #endif 514 | 515 | /** 516 | * Temporal reprojection allows to remove ghosting artifacts when using 517 | * temporal supersampling. We use the CryEngine 3 method which also introduces 518 | * velocity weighting. This feature is of extreme importance for totally 519 | * removing ghosting. More information here: 520 | * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf 521 | * 522 | * Note that you'll need to setup a velocity buffer for enabling reprojection. 523 | * For static geometry, saving the previous depth buffer is a viable 524 | * alternative. 525 | */ 526 | #ifndef SMAA_REPROJECTION 527 | #define SMAA_REPROJECTION 0 528 | #endif 529 | 530 | /** 531 | * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to 532 | * remove ghosting trails behind the moving object, which are not removed by 533 | * just using reprojection. Using low values will exhibit ghosting, while using 534 | * high values will disable temporal supersampling under motion. 535 | * 536 | * Behind the scenes, velocity weighting removes temporal supersampling when 537 | * the velocity of the subsamples differs (meaning they are different objects). 538 | * 539 | * Range: [0, 80] 540 | */ 541 | #ifndef SMAA_REPROJECTION_WEIGHT_SCALE 542 | #define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 543 | #endif 544 | 545 | /** 546 | * On some compilers, discard cannot be used in vertex shaders. Thus, they need 547 | * to be compiled separately. 548 | */ 549 | #ifndef SMAA_INCLUDE_VS 550 | #define SMAA_INCLUDE_VS 1 551 | #endif 552 | #ifndef SMAA_INCLUDE_PS 553 | #define SMAA_INCLUDE_PS 1 554 | #endif 555 | 556 | //----------------------------------------------------------------------------- 557 | // Texture Access Defines 558 | 559 | #ifndef SMAA_AREATEX_SELECT 560 | #if defined(SMAA_HLSL_3) 561 | #define SMAA_AREATEX_SELECT(sample) sample.ra 562 | #else 563 | #define SMAA_AREATEX_SELECT(sample) sample.rg 564 | #endif 565 | #endif 566 | 567 | #ifndef SMAA_SEARCHTEX_SELECT 568 | #define SMAA_SEARCHTEX_SELECT(sample) sample.r 569 | #endif 570 | 571 | #ifndef SMAA_DECODE_VELOCITY 572 | #define SMAA_DECODE_VELOCITY(sample) sample.rg 573 | #endif 574 | 575 | //----------------------------------------------------------------------------- 576 | // Non-Configurable Defines 577 | 578 | #define SMAA_AREATEX_MAX_DISTANCE 16 579 | #define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 580 | #define SMAA_AREATEX_PIXEL_SIZE (1.0 / float2(160.0, 560.0)) 581 | #define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) 582 | #define SMAA_SEARCHTEX_SIZE float2(66.0, 33.0) 583 | #define SMAA_SEARCHTEX_PACKED_SIZE float2(64.0, 16.0) 584 | #define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0) 585 | 586 | //----------------------------------------------------------------------------- 587 | // Porting Functions 588 | 589 | #if defined(SMAA_HLSL_3) 590 | #define SMAATexture2D(tex) sampler2D tex 591 | #define SMAATexturePass2D(tex) tex 592 | #define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) 593 | #define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) 594 | #define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) 595 | #define SMAASample(tex, coord) tex2D(tex, coord) 596 | #define SMAASamplePoint(tex, coord) tex2D(tex, coord) 597 | #define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) 598 | #define SMAA_FLATTEN [flatten] 599 | #define SMAA_BRANCH [branch] 600 | #endif 601 | #if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) 602 | SamplerState LinearSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; 603 | SamplerState PointSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; 604 | #define SMAATexture2D(tex) Texture2D tex 605 | #define SMAATexturePass2D(tex) tex 606 | #define SMAASampleLevelZero(tex, coord) tex.SampleLevel(LinearSampler, coord, 0) 607 | #define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(PointSampler, coord, 0) 608 | #define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(LinearSampler, coord, 0, offset) 609 | #define SMAASample(tex, coord) tex.Sample(LinearSampler, coord) 610 | #define SMAASamplePoint(tex, coord) tex.Sample(PointSampler, coord) 611 | #define SMAASampleOffset(tex, coord, offset) tex.Sample(LinearSampler, coord, offset) 612 | #define SMAA_FLATTEN [flatten] 613 | #define SMAA_BRANCH [branch] 614 | #define SMAATexture2DMS2(tex) Texture2DMS tex 615 | #define SMAALoad(tex, pos, sample) tex.Load(pos, sample) 616 | #if defined(SMAA_HLSL_4_1) 617 | #define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0) 618 | #endif 619 | #endif 620 | #if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) 621 | #define SMAATexture2D(tex) sampler2D tex 622 | #define SMAATexturePass2D(tex) tex 623 | #define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) 624 | #define SMAASampleLevelZeroPoint(tex, coord) textureLod(tex, coord, 0.0) 625 | #define SMAASampleLevelZeroOffset(tex, coord, offset) textureLodOffset(tex, coord, 0.0, offset) 626 | #define SMAASample(tex, coord) texture(tex, coord) 627 | #define SMAASamplePoint(tex, coord) texture(tex, coord) 628 | #define SMAASampleOffset(tex, coord, offset) texture(tex, coord, offset) 629 | #define SMAA_FLATTEN 630 | #define SMAA_BRANCH 631 | #define lerp(a, b, t) mix(a, b, t) 632 | #define saturate(a) clamp(a, 0.0, 1.0) 633 | #if defined(SMAA_GLSL_4) 634 | #define mad(a, b, c) fma(a, b, c) 635 | #define SMAAGather(tex, coord) textureGather(tex, coord) 636 | #else 637 | #define mad(a, b, c) (a * b + c) 638 | #endif 639 | #define float2 vec2 640 | #define float3 vec3 641 | #define float4 vec4 642 | #define int2 ivec2 643 | #define int3 ivec3 644 | #define int4 ivec4 645 | #define bool2 bvec2 646 | #define bool3 bvec3 647 | #define bool4 bvec4 648 | #endif 649 | 650 | #if !defined(SMAA_HLSL_3) && !defined(SMAA_HLSL_4) && !defined(SMAA_HLSL_4_1) && !defined(SMAA_GLSL_3) && !defined(SMAA_GLSL_4) && !defined(SMAA_CUSTOM_SL) 651 | #error you must define the shading language: SMAA_HLSL_*, SMAA_GLSL_* or SMAA_CUSTOM_SL 652 | #endif 653 | 654 | //----------------------------------------------------------------------------- 655 | // Misc functions 656 | 657 | /** 658 | * Gathers current pixel, and the top-left neighbors. 659 | */ 660 | float3 SMAAGatherNeighbours(float2 texcoord, 661 | float4 offset[3], 662 | SMAATexture2D(tex)) { 663 | #ifdef SMAAGather 664 | return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, -0.5)).grb; 665 | #else 666 | float P = SMAASamplePoint(tex, texcoord).r; 667 | float Pleft = SMAASamplePoint(tex, offset[0].xy).r; 668 | float Ptop = SMAASamplePoint(tex, offset[0].zw).r; 669 | return float3(P, Pleft, Ptop); 670 | #endif 671 | } 672 | 673 | /** 674 | * Adjusts the threshold by means of predication. 675 | */ 676 | float2 SMAACalculatePredicatedThreshold(float2 texcoord, 677 | float4 offset[3], 678 | SMAATexture2D(predicationTex)) { 679 | float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(predicationTex)); 680 | float2 delta = abs(neighbours.xx - neighbours.yz); 681 | float2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); 682 | return SMAA_PREDICATION_SCALE * SMAA_THRESHOLD * (1.0 - SMAA_PREDICATION_STRENGTH * edges); 683 | } 684 | 685 | /** 686 | * Conditional move: 687 | */ 688 | void SMAAMovc(bool2 cond, inout float2 variable, float2 value) { 689 | SMAA_FLATTEN if (cond.x) variable.x = value.x; 690 | SMAA_FLATTEN if (cond.y) variable.y = value.y; 691 | } 692 | 693 | void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { 694 | SMAAMovc(cond.xy, variable.xy, value.xy); 695 | SMAAMovc(cond.zw, variable.zw, value.zw); 696 | } 697 | 698 | 699 | #if SMAA_INCLUDE_VS 700 | //----------------------------------------------------------------------------- 701 | // Vertex Shaders 702 | 703 | /** 704 | * Edge Detection Vertex Shader 705 | */ 706 | void SMAAEdgeDetectionVS(float2 texcoord, 707 | out float4 offset[3]) { 708 | offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), texcoord.xyxy); 709 | offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); 710 | offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), texcoord.xyxy); 711 | } 712 | 713 | /** 714 | * Blend Weight Calculation Vertex Shader 715 | */ 716 | void SMAABlendingWeightCalculationVS(float2 texcoord, 717 | out float2 pixcoord, 718 | out float4 offset[3]) { 719 | pixcoord = texcoord * SMAA_RT_METRICS.zw; 720 | 721 | // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): 722 | offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), texcoord.xyxy); 723 | offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), texcoord.xyxy); 724 | 725 | // And these for the searches, they indicate the ends of the loops: 726 | // Modified for UE5 727 | // MaxSearchSteps is now a parameter 728 | offset[2] = mad(SMAA_RT_METRICS.xxyy, 729 | float4(-2.0, 2.0, -2.0, 2.0) * MaxSearchSteps, 730 | float4(offset[0].xz, offset[1].yw)); 731 | } 732 | 733 | /** 734 | * Neighborhood Blending Vertex Shader 735 | */ 736 | void SMAANeighborhoodBlendingVS(float2 texcoord, 737 | out float4 offset) { 738 | offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), texcoord.xyxy); 739 | } 740 | #endif // SMAA_INCLUDE_VS 741 | 742 | #if SMAA_INCLUDE_PS 743 | //----------------------------------------------------------------------------- 744 | // Edge Detection Pixel Shaders (First Pass) 745 | 746 | /** 747 | * Luma Edge Detection 748 | * 749 | * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and 750 | * thus 'colorTex' should be a non-sRGB texture. 751 | */ 752 | float2 SMAALumaEdgeDetectionPS(float2 texcoord, 753 | float4 offset[3], 754 | SMAATexture2D(colorTex) 755 | #if SMAA_PREDICATION 756 | , SMAATexture2D(predicationTex) 757 | #endif 758 | ) { 759 | // Calculate the threshold: 760 | #if SMAA_PREDICATION 761 | float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); 762 | #else 763 | float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); 764 | #endif 765 | 766 | // Calculate lumas: 767 | float3 weights = float3(0.2126, 0.7152, 0.0722); 768 | float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); 769 | 770 | float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); 771 | float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); 772 | 773 | // We do the usual threshold: 774 | float4 delta; 775 | delta.xy = abs(L - float2(Lleft, Ltop)); 776 | float2 edges = step(threshold, delta.xy); 777 | 778 | // Then discard if there is no edge: 779 | if (dot(edges, float2(1.0, 1.0)) == 0.0) 780 | discard; 781 | 782 | // Calculate right and bottom deltas: 783 | float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); 784 | float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); 785 | delta.zw = abs(L - float2(Lright, Lbottom)); 786 | 787 | // Calculate the maximum delta in the direct neighborhood: 788 | float2 maxDelta = max(delta.xy, delta.zw); 789 | 790 | // Calculate left-left and top-top deltas: 791 | float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); 792 | float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); 793 | delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); 794 | 795 | // Calculate the final maximum delta: 796 | maxDelta = max(maxDelta.xy, delta.zw); 797 | float finalDelta = max(maxDelta.x, maxDelta.y); 798 | 799 | // Local contrast adaptation: 800 | edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); 801 | 802 | return edges; 803 | } 804 | 805 | /** 806 | * Color Edge Detection 807 | * 808 | * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and 809 | * thus 'colorTex' should be a non-sRGB texture. 810 | */ 811 | float2 SMAAColorEdgeDetectionPS(float2 texcoord, 812 | float4 offset[3], 813 | SMAATexture2D(colorTex) 814 | #if SMAA_PREDICATION 815 | , SMAATexture2D(predicationTex) 816 | #endif 817 | ) { 818 | // Calculate the threshold: 819 | #if SMAA_PREDICATION 820 | float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); 821 | #else 822 | float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); 823 | #endif 824 | 825 | // Calculate color deltas: 826 | float4 delta; 827 | float3 C = SMAASamplePoint(colorTex, texcoord).rgb; 828 | 829 | float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; 830 | float3 t = abs(C - Cleft); 831 | delta.x = max(max(t.r, t.g), t.b); 832 | 833 | float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; 834 | t = abs(C - Ctop); 835 | delta.y = max(max(t.r, t.g), t.b); 836 | 837 | // We do the usual threshold: 838 | float2 edges = step(threshold, delta.xy); 839 | 840 | // Then discard if there is no edge: 841 | if (dot(edges, float2(1.0, 1.0)) == 0.0) 842 | discard; 843 | 844 | // Calculate right and bottom deltas: 845 | float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; 846 | t = abs(C - Cright); 847 | delta.z = max(max(t.r, t.g), t.b); 848 | 849 | float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; 850 | t = abs(C - Cbottom); 851 | delta.w = max(max(t.r, t.g), t.b); 852 | 853 | // Calculate the maximum delta in the direct neighborhood: 854 | float2 maxDelta = max(delta.xy, delta.zw); 855 | 856 | // Calculate left-left and top-top deltas: 857 | float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; 858 | t = abs(C - Cleftleft); 859 | delta.z = max(max(t.r, t.g), t.b); 860 | 861 | float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; 862 | t = abs(C - Ctoptop); 863 | delta.w = max(max(t.r, t.g), t.b); 864 | 865 | // Calculate the final maximum delta: 866 | maxDelta = max(maxDelta.xy, delta.zw); 867 | float finalDelta = max(maxDelta.x, maxDelta.y); 868 | 869 | // Local contrast adaptation: 870 | edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); 871 | 872 | return edges; 873 | } 874 | 875 | /** 876 | * Depth Edge Detection 877 | */ 878 | float2 SMAADepthEdgeDetectionPS(float2 texcoord, 879 | float4 offset[3], 880 | SMAATexture2D(depthTex)) { 881 | float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); 882 | float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); 883 | float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); 884 | 885 | if (dot(edges, float2(1.0, 1.0)) == 0.0) 886 | discard; 887 | 888 | return edges; 889 | } 890 | 891 | //----------------------------------------------------------------------------- 892 | // Diagonal Search Functions 893 | 894 | #if !defined(SMAA_DISABLE_DIAG_DETECTION) 895 | 896 | /** 897 | * Allows to decode two binary values from a bilinear-filtered access. 898 | */ 899 | float2 SMAADecodeDiagBilinearAccess(float2 e) { 900 | // Bilinear access for fetching 'e' have a 0.25 offset, and we are 901 | // interested in the R and G edges: 902 | // 903 | // +---G---+-------+ 904 | // | x o R x | 905 | // +-------+-------+ 906 | // 907 | // Then, if one of these edge is enabled: 908 | // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 909 | // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 910 | // 911 | // This function will unpack the values (mad + mul + round): 912 | // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 913 | e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); 914 | return round(e); 915 | } 916 | 917 | float4 SMAADecodeDiagBilinearAccess(float4 e) { 918 | e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); 919 | return round(e); 920 | } 921 | 922 | /** 923 | * These functions allows to perform diagonal pattern searches. 924 | */ 925 | float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { 926 | float4 coord = float4(texcoord, -1.0, 1.0); 927 | float3 t = float3(SMAA_RT_METRICS.xy, 1.0); 928 | while (coord.z < (MaxDiagonalSearchSteps - 1) && 929 | coord.w > 0.9) { 930 | coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); 931 | e = SMAASampleLevelZero(edgesTex, coord.xy).rg; 932 | coord.w = dot(e, float2(0.5, 0.5)); 933 | } 934 | return coord.zw; 935 | } 936 | 937 | float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { 938 | float4 coord = float4(texcoord, -1.0, 1.0); 939 | coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization 940 | float3 t = float3(SMAA_RT_METRICS.xy, 1.0); 941 | while (coord.z < (MaxDiagonalSearchSteps - 1) && 942 | coord.w > 0.9) { 943 | coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); 944 | 945 | // @SearchDiag2Optimization 946 | // Fetch both edges at once using bilinear filtering: 947 | e = SMAASampleLevelZero(edgesTex, coord.xy).rg; 948 | e = SMAADecodeDiagBilinearAccess(e); 949 | 950 | // Non-optimized version: 951 | // e.g = SMAASampleLevelZero(edgesTex, coord.xy).g; 952 | // e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0)).r; 953 | 954 | coord.w = dot(e, float2(0.5, 0.5)); 955 | } 956 | return coord.zw; 957 | } 958 | 959 | /** 960 | * Similar to SMAAArea, this calculates the area corresponding to a certain 961 | * diagonal distance and crossing edges 'e'. 962 | */ 963 | float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { 964 | float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); 965 | 966 | // We do a scale and bias for mapping to texel space: 967 | texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); 968 | 969 | // Diagonal areas are on the second half of the texture: 970 | texcoord.x += 0.5; 971 | 972 | // Move to proper place, according to the subpixel offset: 973 | texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; 974 | 975 | // Do it! 976 | return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); 977 | } 978 | 979 | /** 980 | * This searches for diagonal patterns and returns the corresponding weights. 981 | */ 982 | float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { 983 | float2 weights = float2(0.0, 0.0); 984 | 985 | // Search for the line ends: 986 | float4 d; 987 | float2 end; 988 | if (e.r > 0.0) { 989 | d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); 990 | d.x += float(end.y > 0.9); 991 | } else 992 | d.xz = float2(0.0, 0.0); 993 | d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); 994 | 995 | SMAA_BRANCH 996 | if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 997 | // Fetch the crossing edges: 998 | float4 coords = mad(float4(-d.x + 0.25, d.x, d.y, -d.y - 0.25), SMAA_RT_METRICS.xyxy, texcoord.xyxy); 999 | float4 c; 1000 | c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; 1001 | c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; 1002 | c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); 1003 | 1004 | // Non-optimized version: 1005 | // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); 1006 | // float4 c; 1007 | // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; 1008 | // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; 1009 | // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; 1010 | // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; 1011 | 1012 | // Merge crossing edges at each side into a single value: 1013 | float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); 1014 | 1015 | // Remove the crossing edge if we didn't found the end of the line: 1016 | SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); 1017 | 1018 | // Fetch the areas for this line: 1019 | weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); 1020 | } 1021 | 1022 | // Search for the line ends: 1023 | d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); 1024 | if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { 1025 | d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); 1026 | d.y += float(end.y > 0.9); 1027 | } else 1028 | d.yw = float2(0.0, 0.0); 1029 | 1030 | SMAA_BRANCH 1031 | if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 1032 | // Fetch the crossing edges: 1033 | float4 coords = mad(float4(-d.x, -d.x, d.y, d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); 1034 | float4 c; 1035 | c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; 1036 | c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, -1)).r; 1037 | c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; 1038 | float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); 1039 | 1040 | // Remove the crossing edge if we didn't found the end of the line: 1041 | SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); 1042 | 1043 | // Fetch the areas for this line: 1044 | weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; 1045 | } 1046 | 1047 | return weights; 1048 | } 1049 | #endif 1050 | 1051 | //----------------------------------------------------------------------------- 1052 | // Horizontal/Vertical Search Functions 1053 | 1054 | /** 1055 | * This allows to determine how much length should we add in the last step 1056 | * of the searches. It takes the bilinearly interpolated edge (see 1057 | * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and 1058 | * crossing edges are active. 1059 | */ 1060 | float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { 1061 | // The texture is flipped vertically, with left and right cases taking half 1062 | // of the space horizontally: 1063 | float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); 1064 | float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); 1065 | 1066 | // Scale and bias to access texel centers: 1067 | scale += float2(-1.0, 1.0); 1068 | bias += float2( 0.5, -0.5); 1069 | 1070 | // Convert from pixel coordinates to texcoords: 1071 | // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) 1072 | scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; 1073 | bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; 1074 | 1075 | // Lookup the search texture: 1076 | return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, mad(scale, e, bias))); 1077 | } 1078 | 1079 | /** 1080 | * Horizontal/vertical search functions for the 2nd pass. 1081 | */ 1082 | float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { 1083 | /** 1084 | * @PSEUDO_GATHER4 1085 | * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to 1086 | * sample between edge, thus fetching four edges in a row. 1087 | * Sampling with different offsets in each direction allows to disambiguate 1088 | * which edges are active from the four fetched ones. 1089 | */ 1090 | float2 e = float2(0.0, 1.0); 1091 | while (texcoord.x > end && 1092 | e.g > 0.8281 && // Is there some edge not activated? 1093 | e.r == 0.0) { // Or is there a crossing edge that breaks the line? 1094 | e = SMAASampleLevelZero(edgesTex, texcoord).rg; 1095 | texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); 1096 | } 1097 | 1098 | float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0), 3.25); 1099 | return mad(SMAA_RT_METRICS.x, offset, texcoord.x); 1100 | 1101 | // Non-optimized version: 1102 | // We correct the previous (-0.25, -0.125) offset we applied: 1103 | // texcoord.x += 0.25 * SMAA_RT_METRICS.x; 1104 | 1105 | // The searches are bias by 1, so adjust the coords accordingly: 1106 | // texcoord.x += SMAA_RT_METRICS.x; 1107 | 1108 | // Disambiguate the length added by the last step: 1109 | // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step 1110 | // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); 1111 | // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); 1112 | } 1113 | 1114 | float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { 1115 | float2 e = float2(0.0, 1.0); 1116 | while (texcoord.x < end && 1117 | e.g > 0.8281 && // Is there some edge not activated? 1118 | e.r == 0.0) { // Or is there a crossing edge that breaks the line? 1119 | e = SMAASampleLevelZero(edgesTex, texcoord).rg; 1120 | texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); 1121 | } 1122 | float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.5), 3.25); 1123 | return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); 1124 | } 1125 | 1126 | float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { 1127 | float2 e = float2(1.0, 0.0); 1128 | while (texcoord.y > end && 1129 | e.r > 0.8281 && // Is there some edge not activated? 1130 | e.g == 0.0) { // Or is there a crossing edge that breaks the line? 1131 | e = SMAASampleLevelZero(edgesTex, texcoord).rg; 1132 | texcoord = mad(-float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); 1133 | } 1134 | float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.0), 3.25); 1135 | return mad(SMAA_RT_METRICS.y, offset, texcoord.y); 1136 | } 1137 | 1138 | float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { 1139 | float2 e = float2(1.0, 0.0); 1140 | while (texcoord.y < end && 1141 | e.r > 0.8281 && // Is there some edge not activated? 1142 | e.g == 0.0) { // Or is there a crossing edge that breaks the line? 1143 | e = SMAASampleLevelZero(edgesTex, texcoord).rg; 1144 | texcoord = mad(float2(0.0, 2.0), SMAA_RT_METRICS.xy, texcoord); 1145 | } 1146 | float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.5), 3.25); 1147 | return mad(-SMAA_RT_METRICS.y, offset, texcoord.y); 1148 | } 1149 | 1150 | /** 1151 | * Ok, we have the distance and both crossing edges. So, what are the areas 1152 | * at each side of current edge? 1153 | */ 1154 | float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { 1155 | // Rounding prevents precision errors of bilinear filtering: 1156 | float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); 1157 | 1158 | // We do a scale and bias for mapping to texel space: 1159 | texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); 1160 | 1161 | // Move to proper place, according to the subpixel offset: 1162 | texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); 1163 | 1164 | // Do it! 1165 | return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); 1166 | } 1167 | 1168 | //----------------------------------------------------------------------------- 1169 | // Corner Detection Functions 1170 | 1171 | // Modification for UE5 1172 | // SMAA_CORNER_ROUNDING_NORM replaced by parameter "float NormalisedCornerRounding" 1173 | // 1174 | void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { 1175 | #if !defined(SMAA_DISABLE_CORNER_DETECTION) 1176 | float2 leftRight = step(d.xy, d.yx); 1177 | float2 rounding = (1.0 - NormalisedCornerRounding) * leftRight; 1178 | 1179 | rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. 1180 | 1181 | float2 factor = float2(1.0, 1.0); 1182 | factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, 1)).r; 1183 | factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, 1)).r; 1184 | factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, -2)).r; 1185 | factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, -2)).r; 1186 | 1187 | weights *= saturate(factor); 1188 | #endif 1189 | } 1190 | 1191 | void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { 1192 | #if !defined(SMAA_DISABLE_CORNER_DETECTION) 1193 | float2 leftRight = step(d.xy, d.yx); 1194 | float2 rounding = (1.0 - NormalisedCornerRounding) * leftRight; 1195 | 1196 | rounding /= leftRight.x + leftRight.y; 1197 | 1198 | float2 factor = float2(1.0, 1.0); 1199 | factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; 1200 | factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, 1)).g; 1201 | factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; 1202 | factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, 1)).g; 1203 | 1204 | weights *= saturate(factor); 1205 | #endif 1206 | } 1207 | 1208 | //----------------------------------------------------------------------------- 1209 | // Blending Weight Calculation Pixel Shader (Second Pass) 1210 | 1211 | float4 SMAABlendingWeightCalculationPS(float2 texcoord, 1212 | float2 pixcoord, 1213 | float4 offset[3], 1214 | SMAATexture2D(edgesTex), 1215 | SMAATexture2D(areaTex), 1216 | SMAATexture2D(searchTex), 1217 | float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. 1218 | float4 weights = float4(0.0, 0.0, 0.0, 0.0); 1219 | 1220 | float2 e = SMAASample(edgesTex, texcoord).rg; 1221 | 1222 | SMAA_BRANCH 1223 | if (e.g > 0.0) { // Edge at north 1224 | #if !defined(SMAA_DISABLE_DIAG_DETECTION) 1225 | // Diagonals have both north and west edges, so searching for them in 1226 | // one of the boundaries is enough. 1227 | weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); 1228 | 1229 | // We give priority to diagonals, so if we find a diagonal we skip 1230 | // horizontal/vertical processing. 1231 | SMAA_BRANCH 1232 | if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 1233 | #endif 1234 | 1235 | float2 d; 1236 | 1237 | // Find the distance to the left: 1238 | float3 coords; 1239 | coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); 1240 | coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) 1241 | d.x = coords.x; 1242 | 1243 | // Now fetch the left crossing edges, two at a time using bilinear 1244 | // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to 1245 | // discern what value each edge has: 1246 | float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; 1247 | 1248 | // Find the distance to the right: 1249 | coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); 1250 | d.y = coords.z; 1251 | 1252 | // We want the distances to be in pixel units (doing this here allow to 1253 | // better interleave arithmetic and memory accesses): 1254 | d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); 1255 | 1256 | // SMAAArea below needs a sqrt, as the areas texture is compressed 1257 | // quadratically: 1258 | float2 sqrt_d = sqrt(d); 1259 | 1260 | // Fetch the right crossing edges: 1261 | float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; 1262 | 1263 | // Ok, we know how this pattern looks like, now it is time for getting 1264 | // the actual area: 1265 | weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); 1266 | 1267 | // Fix corners: 1268 | coords.y = texcoord.y; 1269 | SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); 1270 | 1271 | #if !defined(SMAA_DISABLE_DIAG_DETECTION) 1272 | } else 1273 | e.r = 0.0; // Skip vertical processing. 1274 | #endif 1275 | } 1276 | 1277 | SMAA_BRANCH 1278 | if (e.r > 0.0) { // Edge at west 1279 | float2 d; 1280 | 1281 | // Find the distance to the top: 1282 | float3 coords; 1283 | coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); 1284 | coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; 1285 | d.x = coords.y; 1286 | 1287 | // Fetch the top crossing edges: 1288 | float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; 1289 | 1290 | // Find the distance to the bottom: 1291 | coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); 1292 | d.y = coords.z; 1293 | 1294 | // We want the distances to be in pixel units: 1295 | d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); 1296 | 1297 | // SMAAArea below needs a sqrt, as the areas texture is compressed 1298 | // quadratically: 1299 | float2 sqrt_d = sqrt(d); 1300 | 1301 | // Fetch the bottom crossing edges: 1302 | float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, 1)).g; 1303 | 1304 | // Get the area for this direction: 1305 | weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); 1306 | 1307 | // Fix corners: 1308 | coords.x = texcoord.x; 1309 | SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); 1310 | } 1311 | 1312 | return weights; 1313 | } 1314 | 1315 | //----------------------------------------------------------------------------- 1316 | // Neighborhood Blending Pixel Shader (Third Pass) 1317 | 1318 | float4 SMAANeighborhoodBlendingPS(float2 texcoord, 1319 | float4 offset, 1320 | SMAATexture2D(colorTex), 1321 | SMAATexture2D(blendTex) 1322 | #if SMAA_REPROJECTION 1323 | , SMAATexture2D(velocityTex) 1324 | #endif 1325 | ) { 1326 | // Fetch the blending weights for current pixel: 1327 | float4 a; 1328 | a.x = SMAASample(blendTex, offset.xy).a; // Right 1329 | a.y = SMAASample(blendTex, offset.zw).g; // Top 1330 | a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left 1331 | 1332 | // Is there any blending weight with a value greater than 0.0? 1333 | SMAA_BRANCH 1334 | if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { 1335 | float4 color = SMAASampleLevelZero(colorTex, texcoord); 1336 | 1337 | #if SMAA_REPROJECTION 1338 | float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); 1339 | 1340 | // Pack velocity into the alpha channel: 1341 | color.a = sqrt(5.0 * length(velocity)); 1342 | #endif 1343 | 1344 | return color; 1345 | } else { 1346 | bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) 1347 | 1348 | // Calculate the blending offsets: 1349 | float4 blendingOffset = float4(0.0, a.y, 0.0, a.w); 1350 | float2 blendingWeight = a.yw; 1351 | SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); 1352 | SMAAMovc(bool2(h, h), blendingWeight, a.xz); 1353 | blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); 1354 | 1355 | // Calculate the texture coordinates: 1356 | float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); 1357 | 1358 | // We exploit bilinear filtering to mix current pixel with the chosen 1359 | // neighbor: 1360 | float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); 1361 | color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); 1362 | 1363 | #if SMAA_REPROJECTION 1364 | // Antialias velocity for proper reprojection in a later stage: 1365 | float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); 1366 | velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); 1367 | 1368 | // Pack velocity into the alpha channel: 1369 | color.a = sqrt(5.0 * length(velocity)); 1370 | #endif 1371 | 1372 | return color; 1373 | } 1374 | } 1375 | 1376 | //----------------------------------------------------------------------------- 1377 | // Temporal Resolve Pixel Shader (Optional Pass) 1378 | 1379 | float4 SMAAResolvePS(float2 texcoord, 1380 | SMAATexture2D(currentColorTex), 1381 | SMAATexture2D(previousColorTex) 1382 | #if SMAA_REPROJECTION 1383 | , SMAATexture2D(velocityTex) 1384 | #endif 1385 | ) { 1386 | #if SMAA_REPROJECTION 1387 | // Velocity is assumed to be calculated for motion blur, so we need to 1388 | // inverse it for reprojection: 1389 | float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); 1390 | 1391 | // Fetch current pixel: 1392 | float4 current = SMAASamplePoint(currentColorTex, texcoord); 1393 | 1394 | // Reproject current coordinates and fetch previous pixel: 1395 | float4 previous = SMAASamplePoint(previousColorTex, texcoord + velocity); 1396 | 1397 | // Attenuate the previous pixel if the velocity is different: 1398 | float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; 1399 | float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); 1400 | 1401 | // Blend the pixels according to the calculated weight: 1402 | return lerp(current, previous, weight); 1403 | #else 1404 | // Just blend the pixels: 1405 | float4 current = SMAASamplePoint(currentColorTex, texcoord); 1406 | float4 previous = SMAASamplePoint(previousColorTex, texcoord); 1407 | return lerp(current, previous, 0.5); 1408 | #endif 1409 | } 1410 | 1411 | //----------------------------------------------------------------------------- 1412 | // Separate Multisamples Pixel Shader (Optional Pass) 1413 | 1414 | #ifdef SMAALoad 1415 | void SMAASeparatePS(float4 position, 1416 | float2 texcoord, 1417 | out float4 target0, 1418 | out float4 target1, 1419 | SMAATexture2DMS2(colorTexMS)) { 1420 | int2 pos = int2(position.xy); 1421 | target0 = SMAALoad(colorTexMS, pos, 0); 1422 | target1 = SMAALoad(colorTexMS, pos, 1); 1423 | } 1424 | #endif 1425 | 1426 | //----------------------------------------------------------------------------- 1427 | #endif // SMAA_INCLUDE_PS --------------------------------------------------------------------------------