├── .gitattributes ├── .gitignore ├── Config ├── DefaultEditor.ini ├── DefaultEngine.ini └── DefaultGame.ini ├── Content └── TestLevel.umap ├── Plugins └── SceneVETestPlugin │ ├── SceneVETestPlugin.uplugin │ ├── Shaders │ ├── SceneVETestShaderCS.usf │ ├── SceneVETestShaderPS.usf │ └── SceneVETestShaderVS.usf │ └── Source │ └── SceneVETesting │ ├── Private │ ├── SceneVEComponent.cpp │ ├── SceneVEComponent.h │ ├── SceneVEProcess.cpp │ ├── SceneVEProcess.h │ └── SceneVETesting.cpp │ ├── Public │ └── SceneVETesting.h │ └── SceneVETesting.Build.cs ├── README.md ├── SceneViewExtTest.uproject └── Source ├── SceneViewExtTest.Target.cs ├── SceneViewExtTest ├── SceneViewExtTest.Build.cs ├── SceneViewExtTest.cpp ├── SceneViewExtTest.h ├── SceneViewExtTestGameModeBase.cpp └── SceneViewExtTestGameModeBase.h └── SceneViewExtTestEditor.Target.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.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 | *.VC.db 42 | *.VC.opendb 43 | 44 | # Precompiled Assets 45 | SourceArt/**/*.png 46 | SourceArt/**/*.tga 47 | 48 | # Binary Files 49 | Binaries/* 50 | Plugins/*/Binaries/* 51 | 52 | # Builds 53 | Build/* 54 | 55 | # Whitelist PakBlacklist-.txt files 56 | !Build/*/ 57 | Build/*/** 58 | !Build/*/PakBlacklist*.txt 59 | 60 | # Don't ignore icon files in Build 61 | !Build/**/*.ico 62 | 63 | # Built data for maps 64 | *_BuiltData.uasset 65 | 66 | # Configuration files generated by the Editor 67 | Saved/* 68 | 69 | # Compiled source files for the engine to use 70 | Intermediate/* 71 | Plugins/*/Intermediate/* 72 | 73 | # Cache files for the editor to use 74 | DerivedDataCache/* 75 | -------------------------------------------------------------------------------- /Config/DefaultEditor.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/A57R4L/SceneViewExtTest/4e56576e8b4f2baa53277e3efa2d4bc96cb71940/Config/DefaultEditor.ini -------------------------------------------------------------------------------- /Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | [/Script/EngineSettings.GameMapsSettings] 4 | GameDefaultMap=/Game/TestLevel.TestLevel 5 | EditorStartupMap=/Game/TestLevel.TestLevel 6 | 7 | [/Script/HardwareTargeting.HardwareTargetingSettings] 8 | TargetedHardwareClass=Desktop 9 | AppliedTargetedHardwareClass=Desktop 10 | DefaultGraphicsPerformance=Maximum 11 | AppliedDefaultGraphicsPerformance=Maximum 12 | 13 | [/Script/Engine.Engine] 14 | +ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/SceneViewExtTest") 15 | +ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/SceneViewExtTest") 16 | +ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="SceneViewExtTestGameModeBase") 17 | 18 | -------------------------------------------------------------------------------- /Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | 2 | [/Script/EngineSettings.GeneralProjectSettings] 3 | ProjectID=79009D1844733DB285C8AE984AAC6819 4 | CopyrightNotice=(c) 2021 Ossi Luoto 5 | CompanyName=Ossi Luoto 6 | Description=Simple Test for Scene View Extension Base Class 7 | 8 | -------------------------------------------------------------------------------- /Content/TestLevel.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/A57R4L/SceneViewExtTest/4e56576e8b4f2baa53277e3efa2d4bc96cb71940/Content/TestLevel.umap -------------------------------------------------------------------------------- /Plugins/SceneVETestPlugin/SceneVETestPlugin.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "SceneViewTestPlugin", 6 | "Description": "Test plugin for SceneViewExtension and RDG Custom Shaders", 7 | "Category": "Code Tools", 8 | "CreatedBy": "Ossi Luoto", 9 | "CanContainContent": true, 10 | "Modules": [ 11 | 12 | { 13 | "Name": "SceneVETesting", 14 | "Type": "Runtime", 15 | "LoadingPhase": "PostConfigInit", 16 | "WhitelistPlatforms": [ "Win64"] 17 | } 18 | ], 19 | "EnabledByDefault": false 20 | } -------------------------------------------------------------------------------- /Plugins/SceneVETestPlugin/Shaders/SceneVETestShaderCS.usf: -------------------------------------------------------------------------------- 1 | // Test project fo SceneViewExtension / RDG Shader Basic setup 2 | // Copyright 2022 Ossi Luoto 3 | // 4 | // Very basic Compute Shader utilizing the Engine basics 5 | 6 | #include "/Engine/Public/Platform.ush" 7 | #include "/Engine/Private/Common.ush" 8 | 9 | // Default border size for group. 10 | #define DEFAULT_GROUP_BORDER_SIZE 8 11 | #define GROUP_BORDER_SIZE (DEFAULT_GROUP_BORDER_SIZE) 12 | 13 | // Parameters 14 | Texture2D OriginalSceneColor; 15 | uint4 ViewportRect; 16 | // We should be able to use View.BufferSizeAndInvSize here, but currently it is not possible/easy to get consistent results due to different scaling applications 17 | float2 SceneColorBufferInvSize; 18 | // Output 19 | RWTexture2D Output; 20 | 21 | // Reference method parameter 22 | //float UVScale; 23 | 24 | // Compute Shader code 25 | [numthreads(GROUP_BORDER_SIZE, GROUP_BORDER_SIZE, 1)] 26 | void MainCS(uint2 DispatchThreadId : SV_DispatchThreadID) 27 | { 28 | // Check that we are in scope 29 | if (any(DispatchThreadId >= ViewportRect.zw)) 30 | { 31 | return; 32 | } 33 | 34 | // If we have the Original Scene Color still here, we need to use the original offset as well 35 | // Due to UniformBuffer refering to the Viewport before TAA (this might change in the future?) we need to apply scale to the UV 36 | float2 sampleUV = (float2(View.ViewRectMin.xy) + (DispatchThreadId + 0.5)) * SceneColorBufferInvSize; 37 | 38 | // Another method here for referene - scaling introduced in 5.0> made it super difficult to get consistent results 39 | // float2 sampleUV = (float2(View.ViewRectMin.xy) + (DispatchThreadId + 0.5)) * View.BufferSizeAndInvSize.zw * UVScale; 40 | 41 | // sample OriginalColor 42 | float4 OriginalColor = OriginalSceneColor.SampleLevel(GlobalPointClampedSampler, sampleUV, 0); 43 | 44 | // switch Red and blue 45 | float4 OutputColor = float4(OriginalColor.z, OriginalColor.y, OriginalColor.x, OriginalColor.w); 46 | 47 | // Write output 48 | Output[View.ViewRectMin.xy + DispatchThreadId] = OutputColor; 49 | 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /Plugins/SceneVETestPlugin/Shaders/SceneVETestShaderPS.usf: -------------------------------------------------------------------------------- 1 | // Test project fo SceneViewExtension / RDG Shader Basic setup 2 | // Copyright 2021 Ossi Luoto 3 | // 4 | // Very basic Pixel Shader utilizing the Engine basics 5 | // We'll switch the colors just to test that the shader is working 6 | 7 | #include "/Engine/Public/Platform.ush" 8 | 9 | Texture2D InputTexture; 10 | SamplerState InputSampler; 11 | 12 | void MainPS( 13 | float4 InPosition : SV_POSITION, 14 | float2 InUV : TEXCOORD0, 15 | out float4 OutColor : SV_Target0) 16 | { 17 | float4 InputColor = InputTexture.Sample(InputSampler, InUV.xy); 18 | InputColor = max(0.0, InputColor); 19 | InputColor = pow(InputColor, 1.0); 20 | OutColor = float4(InputColor.x, InputColor.z, InputColor.y, InputColor.w); 21 | } 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Plugins/SceneVETestPlugin/Shaders/SceneVETestShaderVS.usf: -------------------------------------------------------------------------------- 1 | // Test project fo SceneViewExtension / RDG Shader Basic setup 2 | // Copyright 2021 Ossi Luoto 3 | // 4 | // Very basic Vertex Shader utilizing the Engine basics 5 | 6 | #include "/Engine/Public/Platform.ush" 7 | #include "/Engine/Private/Common.ush" 8 | 9 | 10 | // vertex shader entry point 11 | void MainVS( 12 | in float4 InPosition : ATTRIBUTE0, 13 | in float2 InTexCoord : ATTRIBUTE1, 14 | out float4 OutPosition : SV_POSITION, 15 | out float2 OutUVAndScreenPos : TEXCOORD0 16 | ) 17 | { 18 | DrawRectangle(InPosition, InTexCoord, OutPosition, OutUVAndScreenPos); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Plugins/SceneVETestPlugin/Source/SceneVETesting/Private/SceneVEComponent.cpp: -------------------------------------------------------------------------------- 1 | // Test project fo SceneViewExtension / RDG Shader Basic setup 2 | // Copyright 2021 Ossi Luoto 3 | // 4 | // SceneVEComponent - The SceneViewExtensionBase. Place this in the editor to an empty actor 5 | 6 | #include "SceneVEComponent.h" 7 | #include "SceneVEProcess.h" 8 | 9 | // Functions needed for SceneViewExtension 10 | FTestSceneExtension::FTestSceneExtension(const FAutoRegister& AutoRegister) : FSceneViewExtensionBase(AutoRegister) 11 | { 12 | UE_LOG(LogTemp, Log, TEXT("TestSceneViewExtension: Autoregister")); 13 | } 14 | 15 | void FTestSceneExtension::BeginRenderViewFamily(FSceneViewFamily& InViewFamily) 16 | { 17 | UE_LOG(LogTemp, Log, TEXT("TestSceneViewExtension: Begin Render View Family")); 18 | } 19 | 20 | // This is called every frame, use to subscribe where needed - left the possible PassId's just for quick testing, you actually need to test what you need 21 | 22 | void FTestSceneExtension::SubscribeToPostProcessingPass(EPostProcessingPass PassId, FAfterPassCallbackDelegateArray& InOutPassCallbacks, bool bIsPassEnabled) 23 | { 24 | 25 | if (PassId == EPostProcessingPass::MotionBlur) 26 | { 27 | // UE_LOG(LogTemp, Warning, TEXT("TestSceneViewExtension: Pass is MotionBlur!")); 28 | InOutPassCallbacks.Add(FAfterPassCallbackDelegate::CreateRaw(this, &FTestSceneExtension::TestPostProcessPass_RT)); 29 | } 30 | 31 | if (PassId == EPostProcessingPass::Tonemap) 32 | { 33 | // UE_LOG(LogTemp, Warning, TEXT("TestSceneViewExtension: Pass is Tonemap!")); 34 | } 35 | 36 | if (PassId == EPostProcessingPass::FXAA) 37 | { 38 | // UE_LOG(LogTemp, Warning, TEXT("TestSceneViewExtension: Pass is FXAA!")); 39 | 40 | } 41 | 42 | if (PassId == EPostProcessingPass::VisualizeDepthOfField) 43 | { 44 | // UE_LOG(LogTemp, Warning, TEXT("TestSceneViewExtension: Pass is VisualizeDepthOfField!")); 45 | } 46 | 47 | if (PassId == EPostProcessingPass::MAX) 48 | { 49 | // UE_LOG(LogTemp, Warning, TEXT("TestSceneViewExtension: Pass is MAX!")); 50 | } 51 | } 52 | 53 | // Extension comes with the Graphbuilder and SceneView plus set of PostProcessMaterialInputs - more on these at SceneViewExtension.h 54 | // This is just an empty helper function now if we need to validate data or make other alterations 55 | // I think otherwise we could directly subscribe the Process Function to the PassCallBack 56 | 57 | FScreenPassTexture FTestSceneExtension::TestPostProcessPass_RT(FRDGBuilder& GraphBuilder, const FSceneView& SceneView, const FPostProcessMaterialInputs& InOutInputs) 58 | { 59 | FScreenPassTexture SceneTexture = FSceneVEProcess::AddSceneVETestPass(GraphBuilder, SceneView, InOutInputs); 60 | return SceneTexture; 61 | } 62 | 63 | USceneVEComponent::USceneVEComponent(const FObjectInitializer& ObjectInitializer) 64 | { 65 | PrimaryComponentTick.bCanEverTick = true; 66 | } 67 | 68 | // Called when the game starts 69 | void USceneVEComponent::BeginPlay() 70 | { 71 | Super::BeginPlay(); 72 | UE_LOG(LogTemp, Warning, TEXT("TestSceneViewExtension: Component BeginPlay!")); 73 | CreateSceneViewExtension(); 74 | } 75 | 76 | // On a separate function to hook f.ex. for in editor creation etc. 77 | void USceneVEComponent::CreateSceneViewExtension() 78 | { 79 | TestSceneExtension = FSceneViewExtensions::NewExtension(); 80 | UE_LOG(LogTemp, Log, TEXT("TestSceneViewExtension: Scene Extension Created!")); 81 | } 82 | 83 | 84 | // Called every frame 85 | void USceneVEComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) 86 | { 87 | Super::TickComponent(DeltaTime, TickType, ThisTickFunction); 88 | 89 | } 90 | -------------------------------------------------------------------------------- /Plugins/SceneVETestPlugin/Source/SceneVETesting/Private/SceneVEComponent.h: -------------------------------------------------------------------------------- 1 | // Test project fo SceneViewExtension / RDG Shader Basic setup 2 | // Copyright 2021 Ossi Luoto 3 | // 4 | // SceneVEComponent - The SceneViewExtensionBase. Place this in the editor to an empty actor 5 | 6 | #pragma once 7 | 8 | #include "CoreMinimal.h" 9 | #include "Components/ActorComponent.h" 10 | 11 | #include "RenderGraphUtils.h" 12 | 13 | #include "SceneViewExtension.h" 14 | #include "PostProcess/PostProcessing.h" 15 | #include "PostProcess\PostProcessMaterial.h" 16 | 17 | #include "SceneVEComponent.generated.h" 18 | 19 | // Use FSceneViewExtensionBase to extend hook properly 20 | class FTestSceneExtension : public FSceneViewExtensionBase 21 | { 22 | public: 23 | FTestSceneExtension(const FAutoRegister& AutoRegister); 24 | 25 | // These must all be set 26 | public: 27 | virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override {}; 28 | virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override {}; 29 | virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override; // like component tick 30 | virtual void PreRenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& InViewFamily) override {}; 31 | virtual void PreRenderView_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& InView) override {}; 32 | virtual void SubscribeToPostProcessingPass(EPostProcessingPass PassId, FAfterPassCallbackDelegateArray& InOutPassCallbacks, bool bIsPassEnabled); 33 | 34 | // This is our actual hook function 35 | FScreenPassTexture TestPostProcessPass_RT(FRDGBuilder& GraphBuilder, const FSceneView& View, const FPostProcessMaterialInputs& InOutInputs); 36 | 37 | private: 38 | 39 | }; 40 | 41 | // SceneVEComponent. Simple spawnable component to be place in the editor to an empty or other actor 42 | 43 | UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent)) 44 | class SCENEVETESTING_API USceneVEComponent : public UActorComponent 45 | { 46 | GENERATED_BODY() 47 | 48 | public: 49 | USceneVEComponent(const FObjectInitializer& ObjectInitializer); 50 | 51 | protected: 52 | virtual void BeginPlay() override; 53 | void CreateSceneViewExtension(); 54 | 55 | public: 56 | virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; 57 | 58 | private: 59 | // this is the way to store the SceneExtensionView to keep it safe from not being destroyed - from: SceneViewExtension.h 60 | TSharedPtr TestSceneExtension; 61 | }; 62 | -------------------------------------------------------------------------------- /Plugins/SceneVETestPlugin/Source/SceneVETesting/Private/SceneVEProcess.cpp: -------------------------------------------------------------------------------- 1 | // Test project fo SceneViewExtension / RDG Shader Basic setup 2 | // Copyright 2021 - 2022 Ossi Luoto 3 | // 4 | // SceneVEProcess.cpp - Actual RDG hook and Shader Loading 5 | 6 | #include "SceneVEProcess.h" 7 | 8 | // Shader implementation Macro doesn't work on .h file so load them here 9 | IMPLEMENT_GLOBAL_SHADER(FSceneVETestShaderPS, "/Plugins/SceneVETestPlugin/SceneVETestShaderPS.usf", "MainPS", SF_Pixel); // point to the shader file, name of the main function in shader.usf 10 | IMPLEMENT_GLOBAL_SHADER(FSceneVETestShaderVS, "/Plugins/SceneVETestPlugin/SceneVETestShaderVS.usf", "MainVS", SF_Vertex); // point to the shader file, name of the main function in shader.usf 11 | IMPLEMENT_GLOBAL_SHADER(FSceneVETestShaderCS, "/Plugins/SceneVETestPlugin/SceneVETestShaderCS.usf", "MainCS", SF_Compute); // point to the shader file, name of the main function in shader.usf 12 | 13 | 14 | namespace 15 | { 16 | TAutoConsoleVariable CVarShaderSelector( 17 | TEXT("r.SceneVETest.Shader"), 18 | 0, 19 | TEXT("Select shader to use in the post processing \n") 20 | TEXT(" 0: Vertex & Pixel Shader (default);") 21 | TEXT(" 1: Computer Shader."), 22 | ECVF_RenderThreadSafe); 23 | } 24 | 25 | // Draw Screen Pass function copied directly from the engine OpenColorIODisplayExtension.cpp 26 | // copy start 27 | namespace { 28 | template 29 | void DrawScreenPass( 30 | FRHICommandListImmediate& RHICmdList, 31 | const FSceneView& View, 32 | const FScreenPassTextureViewport& OutputViewport, 33 | const FScreenPassTextureViewport& InputViewport, 34 | const FScreenPassPipelineState& PipelineState, 35 | TSetupFunction SetupFunction) 36 | { 37 | PipelineState.Validate(); 38 | 39 | const FIntRect InputRect = InputViewport.Rect; 40 | const FIntPoint InputSize = InputViewport.Extent; 41 | const FIntRect OutputRect = OutputViewport.Rect; 42 | const FIntPoint OutputSize = OutputRect.Size(); 43 | 44 | RHICmdList.SetViewport(OutputRect.Min.X, OutputRect.Min.Y, 0.0f, OutputRect.Max.X, OutputRect.Max.Y, 1.0f); 45 | 46 | SetScreenPassPipelineState(RHICmdList, PipelineState); 47 | 48 | // Setting up buffers. 49 | SetupFunction(RHICmdList); 50 | 51 | FIntPoint LocalOutputPos(FIntPoint::ZeroValue); 52 | FIntPoint LocalOutputSize(OutputSize); 53 | EDrawRectangleFlags DrawRectangleFlags = EDRF_UseTriangleOptimization; 54 | 55 | DrawPostProcessPass( 56 | RHICmdList, 57 | LocalOutputPos.X, LocalOutputPos.Y, LocalOutputSize.X, LocalOutputSize.Y, 58 | InputRect.Min.X, InputRect.Min.Y, InputRect.Width(), InputRect.Height(), 59 | OutputSize, 60 | InputSize, 61 | PipelineState.VertexShader, 62 | #if ENGINE_MAJOR_VERSION == 5 63 | View.StereoViewIndex, 64 | #else 65 | View.StereoPass, 66 | #endif 67 | false, 68 | DrawRectangleFlags); 69 | } 70 | } 71 | // copy end 72 | 73 | FScreenPassTexture FSceneVEProcess::AddSceneVETestPass(FRDGBuilder& GraphBuilder, const FSceneView& SceneView, const FPostProcessMaterialInputs& Inputs) 74 | { 75 | // SceneViewExtension gives SceneView, not ViewInfo so we need to setup some basics ourself 76 | const FSceneViewFamily& ViewFamily = *SceneView.Family; 77 | const ERHIFeatureLevel::Type FeatureLevel = SceneView.GetFeatureLevel(); 78 | 79 | const FScreenPassTexture& SceneColor = Inputs.Textures[(uint32)EPostProcessMaterialInput::SceneColor]; 80 | 81 | if (!SceneColor.IsValid()) 82 | { 83 | return SceneColor; 84 | } 85 | 86 | // Check which shader method to use VS/PS or CS 87 | 88 | // If you are f.ex. combining VS&PS and CS shaders and doing multiple passes, a lot of the setup code below works for both 89 | // Here both are declared independently just to make it easier to strip either one out if aiming for a simple setup 90 | // And also to point out a few different ways to do the setup 91 | 92 | if (CVarShaderSelector.GetValueOnRenderThread() == 0) 93 | { 94 | // Vertex/Pixel Shader version 95 | 96 | // Here starts the RDG stuff 97 | RDG_EVENT_SCOPE(GraphBuilder, "SceneVETestPass"); 98 | { 99 | // Accesspoint to our Shaders 100 | FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(ViewFamily.GetFeatureLevel()); 101 | 102 | // Template Render Target to use as main output 103 | FScreenPassRenderTarget templateRenderTarget; 104 | 105 | // Check if this destination is the last one on the post process pipeline 106 | if (Inputs.OverrideOutput.IsValid()) 107 | { 108 | templateRenderTarget = Inputs.OverrideOutput; 109 | } 110 | else 111 | // Otherwise make a template RenderTarget 112 | { 113 | FRDGTextureDesc OutputDesc = SceneColor.Texture->Desc; 114 | OutputDesc.Flags |= TexCreate_RenderTargetable; 115 | FLinearColor ClearColor(0., 0., 0., 0.); 116 | OutputDesc.ClearValue = FClearValueBinding(ClearColor); 117 | 118 | FRDGTexture* templateRenderTargetTexture = GraphBuilder.CreateTexture(OutputDesc, TEXT("templateRenderTargetTexture")); 119 | templateRenderTarget = FScreenPassRenderTarget(templateRenderTargetTexture, SceneColor.ViewRect, ERenderTargetLoadAction::EClear); 120 | } 121 | 122 | // The Viewport in source and destination might be different 123 | const FScreenPassTextureViewport SceneColorViewport(SceneColor); 124 | const FScreenPassTextureViewport tempRenderTargetViewport(templateRenderTarget); 125 | 126 | FScreenPassRenderTarget SceneColorRenderTarget(SceneColor, ERenderTargetLoadAction::ELoad); 127 | 128 | // We need these for the GraphBuilder AddPass 129 | FRHIBlendState* DefaultBlendState = FScreenPassPipelineState::FDefaultBlendState::GetRHI(); 130 | FRHIDepthStencilState* DepthStencilState = FScreenPassPipelineState::FDefaultDepthStencilState::GetRHI(); 131 | 132 | { 133 | // Get the assigned shaders 134 | TShaderMapRef VertexShader(GlobalShaderMap); 135 | TShaderMapRef PixelShader(GlobalShaderMap); 136 | 137 | // Pass the shader parameters 138 | FSceneVETestShaderParameters* PassParameters = GraphBuilder.AllocParameters(); 139 | PassParameters->InputTexture = SceneColorRenderTarget.Texture; 140 | PassParameters->InputSampler = TStaticSamplerState<>::GetRHI(); 141 | PassParameters->RenderTargets[0] = templateRenderTarget.GetRenderTargetBinding(); 142 | 143 | // Add Pass by submitting the shaders and parameters to the GraphBuilder that takes care of scheduling it to the Renderthread 144 | GraphBuilder.AddPass( 145 | RDG_EVENT_NAME("SceneVETestMainPass"), 146 | PassParameters, 147 | ERDGPassFlags::Raster, 148 | [&SceneView, 149 | VertexShader, 150 | PixelShader, 151 | DefaultBlendState, 152 | DepthStencilState, 153 | SceneColorViewport, 154 | tempRenderTargetViewport, 155 | PassParameters](FRHICommandListImmediate& RHICmdList) 156 | { 157 | DrawScreenPass( 158 | RHICmdList, 159 | SceneView, 160 | tempRenderTargetViewport, 161 | SceneColorViewport, 162 | FScreenPassPipelineState(VertexShader, PixelShader, DefaultBlendState, DepthStencilState), 163 | [&](FRHICommandListImmediate& RHICmdList) 164 | { 165 | SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters); 166 | }); 167 | }); 168 | 169 | } 170 | // Return the result 171 | return MoveTemp(templateRenderTarget); 172 | } 173 | } 174 | 175 | else 176 | 177 | { 178 | // Compute Shader version 179 | 180 | // Here starts the RDG stuff 181 | RDG_EVENT_SCOPE(GraphBuilder, "SceneVETestPass"); 182 | { 183 | // Accesspoint to our Shaders 184 | FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(ViewFamily.GetFeatureLevel()); 185 | 186 | // Setup all the desciptors. 187 | FRDGTextureDesc OutputDesc; 188 | { 189 | OutputDesc = SceneColor.Texture->Desc; 190 | 191 | OutputDesc.Reset(); 192 | OutputDesc.Flags |= TexCreate_UAV; 193 | OutputDesc.Flags &= ~(TexCreate_RenderTargetable | TexCreate_FastVRAM); 194 | 195 | FLinearColor ClearColor(0., 0., 0., 0.); 196 | OutputDesc.ClearValue = FClearValueBinding(ClearColor); 197 | 198 | } 199 | 200 | FRDGTextureRef templateRenderTargetTexture = GraphBuilder.CreateTexture(OutputDesc, TEXT("templateRenderTargetTexture")); 201 | 202 | // Set the shader parameters 203 | FSceneVETestShaderCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); 204 | 205 | // Input is the SceneColor from PostProcess Material Inputs 206 | PassParameters->OriginalSceneColor = SceneColor.Texture; 207 | 208 | // There are other ways to obtain this information, but for a reference this is a hack to make a FViewInfo from the SceneView that can be very useful 209 | checkSlow(SceneView->bIsViewInfo); 210 | const FViewInfo* View = (FViewInfo*)&SceneView; 211 | 212 | // Important! There are many places where UE sets and scales viewport resolution = what kind of scaling you might need to apply to get 213 | // your UV's right might depend where you place your custom post processing 214 | // With this example, we are doing the post processing AFTER TAA, which has already scaled up (if needed) the viewport 215 | // However, at least in UE 5.0 the UniformBuffer is populated with the initial View and Scale, meaning UV's and viewport Size there will be wrong 216 | // ie. below might work pre post processing chain, but not at this point 217 | // FIntPoint PassViewSize = View->ViewRect.Size(); 218 | // PassParameters->ViewportRect = View->ViewRect; 219 | // You could also try to use: const float ScreenPercentage = ViewFamily.GetPrimaryResolutionFractionUpperBound() * ViewFamily.SecondaryViewFraction; 220 | // To obtain a scaling factor for UniformViewBuffer UV's, but at least for 5.0.1 this doesn't seem to honor the changing of project settings manual scaling 221 | // If this is a bug or a wrong approach to scaling, we'll have to see 222 | 223 | // So we use this instead 224 | 225 | // Get the input size (do note that this is not the full extent but the part of the texture containing the SceneColor information) 226 | FIntPoint PassViewSize = SceneView.UnconstrainedViewRect.Size(); 227 | PassParameters->ViewportRect = SceneView.UnconstrainedViewRect; 228 | 229 | // As the previously mentioned parameters doesn't seem to honor the changes fully, let's just calculate BufferInv from the texture we just got 230 | #if ENGINE_MAJOR_VERSION == 5 231 | PassParameters->SceneColorBufferInvSize = FVector2f(1.0f / SceneColor.Texture->Desc.Extent.X, 1.0f / SceneColor.Texture->Desc.Extent.Y); 232 | #else 233 | PassParameters->SceneColorBufferInvSize = FVector2D(1.0f / SceneColor.Texture->Desc.Extent.X, 1.0f / SceneColor.Texture->Desc.Extent.Y); 234 | #endif 235 | 236 | // Method to setup common parameters, we use this to pass ViewUniformBuffer data 237 | // There is a lot of useful stuff in the ViewUniformBuffer, do note that when getting this from the SceneView, a lot them seem to be unpopulated 238 | FCommonShaderParameters CommonParameters; 239 | CommonParameters.ViewUniformBuffer = SceneView.ViewUniformBuffer; 240 | PassParameters->CommonParameters = CommonParameters; 241 | 242 | // Create UAV from Target Texture 243 | PassParameters->Output = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(templateRenderTargetTexture)); 244 | 245 | // Set groupcount and execute pass 246 | const int32 kDefaultGroupSize = 8; 247 | FIntPoint GroupSize(kDefaultGroupSize, kDefaultGroupSize); 248 | FIntVector GroupCount = FComputeShaderUtils::GetGroupCount(PassViewSize, GroupSize); 249 | 250 | TShaderMapRef ComputeShader(GlobalShaderMap); 251 | 252 | FComputeShaderUtils::AddPass( 253 | GraphBuilder, 254 | RDG_EVENT_NAME("SceneVETest CS Shader %dx%d", PassViewSize.X, PassViewSize.Y), 255 | ComputeShader, 256 | PassParameters, 257 | GroupCount); 258 | 259 | // One can use a similar method as in the VS/PS version to create a ScreenPassRenderTarget and return with the MoveTemp 260 | // We are doing here another trick to do a fast copy from the template buffer to the original scenecolor 261 | // If you are doing multiple passes, technically you should be able to even use the original scenecolor RT as the final output buffer as UAV 262 | // However, for some reason that seems to work very randomy, so usually with multiple passes, this is the last method I use with multiple Compute Shaders 263 | 264 | AddCopyTexturePass(GraphBuilder, templateRenderTargetTexture, SceneColor.Texture); 265 | 266 | return SceneColor; 267 | } 268 | 269 | 270 | } 271 | 272 | } 273 | 274 | -------------------------------------------------------------------------------- /Plugins/SceneVETestPlugin/Source/SceneVETesting/Private/SceneVEProcess.h: -------------------------------------------------------------------------------- 1 | // Test project fo SceneExtensionView / RDG Shader Basic setup 2 | // Copyright 2021 Ossi Luoto 3 | // 4 | // SceneVEProcess.cpp - Actual RDG hook and Shader Loading 5 | 6 | #pragma once 7 | #include "SceneVEComponent.h" 8 | 9 | #include "ScreenPass.h" 10 | #include "Shader.h" 11 | #include "GlobalShader.h" 12 | #include "ShaderParameterUtils.h" 13 | #include "ShaderParameterStruct.h" 14 | 15 | #include "RenderGraphUtils.h" 16 | 17 | // Parameter Declaration 18 | BEGIN_SHADER_PARAMETER_STRUCT(FSceneVETestShaderParameters, ) 19 | SHADER_PARAMETER_RDG_TEXTURE(Texture2D, InputTexture) 20 | SHADER_PARAMETER_SAMPLER(SamplerState, InputSampler) 21 | RENDER_TARGET_BINDING_SLOTS() 22 | END_SHADER_PARAMETER_STRUCT() 23 | 24 | // Declare Test Shader Class 25 | 26 | class SCENEVETESTING_API FSceneVETestShaderVS : public FGlobalShader 27 | { 28 | public: 29 | // Vertex Shader Declaration 30 | DECLARE_GLOBAL_SHADER(FSceneVETestShaderVS) 31 | 32 | // Basic shader stuff 33 | static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) 34 | { 35 | return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); 36 | } 37 | static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) 38 | { 39 | FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); 40 | } 41 | 42 | FSceneVETestShaderVS() {} 43 | 44 | FSceneVETestShaderVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) 45 | : FGlobalShader(Initializer) 46 | { 47 | } 48 | }; 49 | 50 | class SCENEVETESTING_API FSceneVETestShaderPS : public FGlobalShader 51 | { 52 | public: 53 | // RDG Pixel Shader Declaration 54 | DECLARE_GLOBAL_SHADER(FSceneVETestShaderPS) 55 | SHADER_USE_PARAMETER_STRUCT_WITH_LEGACY_BASE(FSceneVETestShaderPS, FGlobalShader) 56 | 57 | // Basic shader stuff 58 | static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) 59 | { 60 | return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); 61 | } 62 | static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) 63 | { 64 | FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); 65 | } 66 | 67 | // Use the parameters from previously delcared struct 68 | using FParameters = FSceneVETestShaderParameters; 69 | 70 | }; 71 | 72 | // Struct to include common parameters, useful when doing multiple shaders 73 | BEGIN_SHADER_PARAMETER_STRUCT(FCommonShaderParameters, ) 74 | SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) 75 | END_SHADER_PARAMETER_STRUCT() 76 | 77 | 78 | class SCENEVETESTING_API FSceneVETestShaderCS : public FGlobalShader 79 | { 80 | public: 81 | DECLARE_GLOBAL_SHADER(FSceneVETestShaderCS) 82 | SHADER_USE_PARAMETER_STRUCT(FSceneVETestShaderCS, FGlobalShader) 83 | 84 | BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) 85 | SHADER_PARAMETER_STRUCT_INCLUDE(FCommonShaderParameters, CommonParameters) 86 | SHADER_PARAMETER(FIntRect, ViewportRect) 87 | #if ENGINE_MAJOR_VERSION == 5 88 | SHADER_PARAMETER(FVector2f, SceneColorBufferInvSize) 89 | #else 90 | SHADER_PARAMETER(FVector2D, SceneColorBufferInvSize) 91 | #endif 92 | SHADER_PARAMETER_RDG_TEXTURE(Texture2D, OriginalSceneColor) 93 | SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, Output) 94 | END_SHADER_PARAMETER_STRUCT() 95 | 96 | // Basic shader stuff 97 | static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) 98 | { 99 | return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5); 100 | } 101 | 102 | }; 103 | 104 | 105 | class FSceneVEProcess 106 | { 107 | public: 108 | // Hook to the SceneViewExtension Base 109 | static FScreenPassTexture AddSceneVETestPass(FRDGBuilder& GraphBuilder, const FSceneView& View, const FPostProcessMaterialInputs& Inputs); 110 | 111 | private: 112 | 113 | }; -------------------------------------------------------------------------------- /Plugins/SceneVETestPlugin/Source/SceneVETesting/Private/SceneVETesting.cpp: -------------------------------------------------------------------------------- 1 | // Test project fo SceneViewExtension / RDG Shader Basic setup 2 | // Copyright 2021 Ossi Luoto 3 | // 4 | // Main Module to set Shader Directories 5 | 6 | #include "SceneVETesting.h" 7 | 8 | #define LOCTEXT_NAMESPACE "FSceneVETesting" 9 | 10 | void FSceneVETesting::StartupModule() 11 | { 12 | // Set up the Shader Directories 13 | FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("SceneVETestPlugin"))->GetBaseDir(), TEXT("Shaders")); 14 | AddShaderSourceDirectoryMapping(TEXT("/Plugins/SceneVETestPlugin"), PluginShaderDir); 15 | } 16 | 17 | void FSceneVETesting::ShutdownModule() 18 | { 19 | } 20 | 21 | #undef LOCTEXT_NAMESPACE 22 | 23 | IMPLEMENT_MODULE(FSceneVETesting, SceneVETesting); -------------------------------------------------------------------------------- /Plugins/SceneVETestPlugin/Source/SceneVETesting/Public/SceneVETesting.h: -------------------------------------------------------------------------------- 1 | // Test project fo SceneViewExtension / RDG Shader Basic setup 2 | // Copyright 2021 Ossi Luoto 3 | // 4 | // Main Module to set Shader Directories 5 | 6 | #pragma once 7 | 8 | #include "CoreMinimal.h" 9 | #include "Modules/ModuleManager.h" 10 | #include "Modules/ModuleInterface.h" 11 | #include "Interfaces/IPluginManager.h" 12 | 13 | class FSceneVETesting : public IModuleInterface 14 | { 15 | public: 16 | 17 | /** IModuleInterface implementation */ 18 | virtual void StartupModule() override; 19 | virtual void ShutdownModule() override; 20 | }; -------------------------------------------------------------------------------- /Plugins/SceneVETestPlugin/Source/SceneVETesting/SceneVETesting.Build.cs: -------------------------------------------------------------------------------- 1 | // Test project fo SceneViewExtension / RDG Shader Basic setup 2 | // Copyright 2021 Ossi Luoto 3 | // 4 | // Build tool Dependencies 5 | // IMPORTANT! Paths required for Build tool/Visual Studio to locate f.ex. Screenpass.h and Post Process stuff 6 | 7 | using UnrealBuildTool; 8 | using System.IO; 9 | 10 | public class SceneVETesting : ModuleRules 11 | { 12 | public SceneVETesting(ReadOnlyTargetRules Target) : base(Target) 13 | { 14 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 15 | 16 | PublicDependencyModuleNames.AddRange(new string[] { 17 | "Core", 18 | "CoreUObject", 19 | "Engine", 20 | "RenderCore", 21 | "Renderer", 22 | "RHI", 23 | "Projects", 24 | }); 25 | 26 | var EngineDir = Path.GetFullPath(Target.RelativeEnginePath); 27 | 28 | PrivateIncludePaths.AddRange( 29 | new string[] { 30 | // this is required to find PostProcessing includes f.ex. screenpass.h 31 | Path.Combine(EngineDir, "Source/Runtime/Renderer/Private") 32 | } 33 | ); 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Update for Unreal Engine 5 2 | 3 | Please find an up-to-date plugin repository here: [https://github.com/A57R4L/SceneViewExtensionTemplate](https://github.com/A57R4L/SceneViewExtensionTemplate) 4 | 5 | # SceneViewExtTest 6 | UE 4.26 (works now on 4.27 and 5.0.1) test project and plugin for SceneViewExtension and how to hook custom shaders (Vertex Shader, Pixel Shader and Compute Shader) to RDG/GraphBuilder 7 | 8 | I had really hard time finding up to date documentation on how to use custom shaders and access the post process chain in Unreal Engine without modifying the engine code. This is an attempt to document the very basic process to setup a custom hook to the SceneView and some basic shaders. 9 | 10 | This repository holds a template UE4 project with a single empty actor that has a SceneViewComponent that hooks a custom Vertex&Pixel shader to the Post Process chain. All the code lies in the plugin folder. It takes care of the Pixel and Vertex shader loading (a compute or geometry shader with this technique shouldn't be that difficult either). Then injects those to the post process chain via SceneViewExtensionBase class that gives the access to the RDG/GraphBuilder. As the Engine code is constantly evolving, things might get broken for the next version. However this is already a higher level implementation than direct RHI/DirectX access, so this should be easier to update if/when the engine code changes again. 11 | 12 | This is for the Win64 platform and can't verify how easily transfers to other platform. 13 | 14 | Most of the insights came from the Engine code - especially Pixel Inspector and IO Color Profile Extension. A hugh shout out to so many people asking and contributing with various small insights on the UE forum and Unreal Slackers Discord especially. For me just to get this actually working was a quest for several months, so this repository was made to hopefully easen the path for someone else. And also to gather feedback to further improve the implementation and best practices. 15 | 16 | NOTE ON THE ACTUAL UE4 PROJECT/FULL REPOSITORY: When hitting play the viewport blue and green components of the scenecolor are reversed as a proof of concept, otherwise it is just a vanilla empty c++ project template from the engine. Plugin folder should work as a copy/paste to any UE4.26> project. 17 | 18 | UPDATE May 2022: 19 | * Some refractoring and #if MAJOR_ENGINE_VERSION == 5 modifications to highlight changes from UE4.26->UE4.27->UE5.x 20 | * Alternative example to hook a compute shader instead of Vertex and Pixel Shader. There are some additional features in UE5 to affect the screen percentage at various points of rendering, and I'm not quite sure everything is properly in place yet at the engine side - see the comments on different ways to get the right viewport size depending on at what point of the post processing chain you plan to implement your actual effect 21 | * You can change the used shader method with console command: r.SceneVETest.Shader 22 | -------------------------------------------------------------------------------- /SceneViewExtTest.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.26", 4 | "Category": "", 5 | "Description": "", 6 | "Modules": [ 7 | { 8 | "Name": "SceneViewExtTest", 9 | "Type": "Runtime", 10 | "LoadingPhase": "Default" 11 | } 12 | ], 13 | "Plugins": [ 14 | { 15 | "Name": "SceneVETestPlugin", 16 | "Enabled": true 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /Source/SceneViewExtTest.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class SceneViewExtTestTarget : TargetRules 7 | { 8 | public SceneViewExtTestTarget( TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Game; 11 | DefaultBuildSettings = BuildSettingsVersion.V2; 12 | ExtraModuleNames.AddRange( new string[] { "SceneViewExtTest" } ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Source/SceneViewExtTest/SceneViewExtTest.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SceneViewExtTest : ModuleRules 6 | { 7 | public SceneViewExtTest(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); 12 | 13 | PrivateDependencyModuleNames.AddRange(new string[] { }); 14 | 15 | // Uncomment if you are using Slate UI 16 | // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); 17 | 18 | // Uncomment if you are using online features 19 | // PrivateDependencyModuleNames.Add("OnlineSubsystem"); 20 | 21 | // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/SceneViewExtTest/SceneViewExtTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "SceneViewExtTest.h" 4 | #include "Modules/ModuleManager.h" 5 | 6 | IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, SceneViewExtTest, "SceneViewExtTest" ); 7 | -------------------------------------------------------------------------------- /Source/SceneViewExtTest/SceneViewExtTest.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | -------------------------------------------------------------------------------- /Source/SceneViewExtTest/SceneViewExtTestGameModeBase.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | 4 | #include "SceneViewExtTestGameModeBase.h" 5 | 6 | -------------------------------------------------------------------------------- /Source/SceneViewExtTest/SceneViewExtTestGameModeBase.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/GameModeBase.h" 7 | #include "SceneViewExtTestGameModeBase.generated.h" 8 | 9 | /** 10 | * 11 | */ 12 | UCLASS() 13 | class SCENEVIEWEXTTEST_API ASceneViewExtTestGameModeBase : public AGameModeBase 14 | { 15 | GENERATED_BODY() 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /Source/SceneViewExtTestEditor.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class SceneViewExtTestEditorTarget : TargetRules 7 | { 8 | public SceneViewExtTestEditorTarget( TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Editor; 11 | DefaultBuildSettings = BuildSettingsVersion.V2; 12 | ExtraModuleNames.AddRange( new string[] { "SceneViewExtTest" } ); 13 | } 14 | } 15 | --------------------------------------------------------------------------------