{"id":19237994,"url":"https://github.com/pikachuxxxx/ue5hellotriangle","last_synced_at":"2025-10-14T08:32:46.559Z","repository":{"id":261659229,"uuid":"884966789","full_name":"Pikachuxxxx/UE5HelloTriangle","owner":"Pikachuxxxx","description":"How to render a Hello Triangle in UE5 using it's RHI and SceneViewExtensionBase class","archived":false,"fork":false,"pushed_at":"2024-11-10T12:22:20.000Z","size":66,"stargazers_count":4,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-05T21:32:15.012Z","etag":null,"topics":["graphics-programming","hello-triangle","ue5","ue5-plugin","unreal","unreal-engine","unreal-engine-5"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Pikachuxxxx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-07T17:53:32.000Z","updated_at":"2025-03-18T22:01:15.000Z","dependencies_parsed_at":"2024-11-07T18:51:53.585Z","dependency_job_id":null,"html_url":"https://github.com/Pikachuxxxx/UE5HelloTriangle","commit_stats":null,"previous_names":["pikachuxxxx/ue5hellotriangle"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pikachuxxxx%2FUE5HelloTriangle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pikachuxxxx%2FUE5HelloTriangle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pikachuxxxx%2FUE5HelloTriangle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pikachuxxxx%2FUE5HelloTriangle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Pikachuxxxx","download_url":"https://codeload.github.com/Pikachuxxxx/UE5HelloTriangle/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248340371,"owners_count":21087438,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["graphics-programming","hello-triangle","ue5","ue5-plugin","unreal","unreal-engine","unreal-engine-5"],"created_at":"2024-11-09T16:28:57.890Z","updated_at":"2025-10-14T08:32:41.527Z","avatar_url":"https://github.com/Pikachuxxxx.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# UE5HelloTriangle\nHow to render a Hello Triangle in UE5 using it's RHI and FSceneViewExtensionBase class and UEngineSubSystem.\n\n**Engine Version: 5.4 Source Build**\n\n\n## TUTORIAL\nIn this tutorial, we’ll cover how to render a simple triangle on the screen using Unreal Engine's RHI. We’ll implement this through a custom UEngineSubsystem and extend the rendering system with FSceneViewExtensionBase. We build this as a plugin. Unreal renderer itself is a module. Using plugins helps us keep the code organized and avoid cluttering unreal's rendering code. We can almost do anything without having to dig deeper into unreal source and get away with plugin and using the RDG (aka frame graph) for most cases. \n\n### Setup\n\nSince we will be referencing private classes of RDG and RHI, we need to use a source build for this. For how to build unreal engine 5 from source, check out this blog from epic. https://dev.epicgames.com/documentation/en-us/unreal-engine/building-unreal-engine-from-source\n\n[How to setup a plugin in Unreal Engine](https://www.quodsoler.com/blog/how-to-create-an-unreal-engine-plugin-a-step-by-step-guide-with-examples#:~:text=Creating%20Your%20First%20Unreal%20Engine%20Plugin\u0026text=Navigate%20to%20the%20'Edit'%20menu,will%20depend%20on%20your%20needs.)\n, this should be same for all engine versions. We start of with a basic plugin setup, we use the `PostConfigInit` phase and setup the plugin as a `Runtime` module. Go ahead and setup a plugin called HelloTriangle or call it what you like. `Runtime` tells us that this is an Engine plugin and `PostConfigInit` tell us the initialization stage of this plugin during the engine startup. Check the above blog for more info.\n\n## Plugin Setup\nAdd the private files to be visibile to the plugin by adding it to the Build.cs file called `HelloTriangle.Build.cs` file in your plugin Source folder.\n```C#\n PublicIncludePaths.AddRange(\n    new string[] {\n        EngineDirectory + \"/Source/Runtime/Renderer/Private\"\n    }\n);\n```\n\nNext add the following **Public** modules to link to: `RHI`, `RenderCore`, `Renderer` and `Projects` to the build file\n```C#\nPublicDependencyModuleNames.AddRange(\n    new string[]\n    {\n        \"Core\",\n        \"RHI\",\n        \"Renderer\",\n        \"RenderCore\",\n        \"Projects\"\n    }\n);\n```\nNow your build file for the plugin should be ready. If you need to link with any other private/public modules you can modify the `HelloTriangle.Build.cs` accordingly. \n\nWe also need to do some setup when the module loads:\n- Setup a Virtual File System (VFS) link to the plugin shaders directory to load and give a __unique__ name to it.\n\nRemember how we stress unique, if you have multiple plugins they can't use the same VFS mapping. And also make sure the Unique VFS starts with / before the name otherwise the engine can't see this is a directory and will cause a crash.\n\n\n```C++\nvoid FHelloTriangleModule::StartupModule()\n{\n    // Register a custom VFS link to plugin shaders\n    FString baseDir = IPluginManager::Get().FindPlugin(TEXT(\"HelloTriangle\"))-\u003eGetBaseDir();\n    FString pluginShaderBaseDir = FPaths::Combine(baseDir, TEXT(\"Shaders\"));\n    // This is a virtual folder and can be the same name as OG or anything.\n    AddShaderSourceDirectoryMapping(TEXT(\"/CustomShaders\"), pluginShaderBaseDir);\n}\n```\n\nNow you're done with the plugin setup, onto the next steps we go. SWOOSH!!\n\n## Setting up SceneViewExtensionBase\nOkay now we got the engine to launch and load the `Hello Triangle` plugin and created Virtual File System links to load shaders/resources etc. we can focus on the Rendering code now. Instead of directly modifying engine source, unreal provides us with `FSceneViewExtensionBase` class that when inherited from helps us inject rnedering code using the RDG (Render Dependency Graph) or directly via writing into the Command Buffer (`FRHICommandList`). So we create a .h/.cpp files called `FHelloTriangleViewExtension` and derive it from `FSceneViewExtensionBase`.\n\n\u003e Note: If you're getting build errors, make sure you include the right headers, check the source and engine for what header files you might need to include from time to time.\n\nWe override the following methods from the ISceneViewExtensionBase interface. We only focus on `PrePostProcess_RenderThread` function for now. Our goal currently is to draw a trianlge before post process and after the lightting pass has been done. Check the source for ex. PostProcessing.cpp and other files on what function you can use to inject, you can inject your own RDG render pass at various stages of the rendering pipeline based on the functions in `ISceneViewExtensionBase` class, just check the function names that end with _RenderThread. You shoudl be able to do it before/after lighting/depth pases, before/after post processing and in b/w post processing stages by using the `SubscribePostProcssing` function. For the sake of this tutorial we will be just working with `PrePostProcess_RenderThread` function so we override only that. Check docs/source for what each virtual function does.\n\nHelloTriangleViewExtension.h class\n\n```C++\n#include \"SceneViewExtension.h\"\n#include \"RenderResource.h\"\n\nclass HELLOTRIANGLE_API FHelloTriangleViewExtension : public FSceneViewExtensionBase\n{\npublic:\n    FHelloTriangleViewExtension(const FAutoRegister\u0026 AutoRegister);\n\n\t//~ Begin FSceneViewExtensionBase Interface\n\tvirtual void PrePostProcessPass_RenderThread(FRDGBuilder\u0026 GraphBuilder, const FSceneView\u0026 View, const FPostProcessingInputs\u0026 Inputs) override;\n\t//~ End FSceneViewExtensionBase Interface\n};\n```\nHELLOTRIANGLE_API is defines by the generated headers so it's typically looks like YOURMODULENAME_API. This define makes this class publicly visibile.\n\nwe will discuss the HelloTriangleViewExtension.cpp implementation later. Let's next focus on setting up the render resources for drawing our triangle, we need:\n- Shaders (.usf and C++ equivalent classes)\n- Vertex and Index Buffers\nTriangle doesn't need a index buffer but we used to demonstrate how to bind one.\n\n## Setting up Shaders\nCreate a new folder in the same dfirectory as Source. Add another folder called Private to it and place an empty file called HelloTriangle.usf (.usf = Unreal Shader File). This is the shader that will be compiled and loaded by the engine. This single file contains both the Vertex and Pixel Shader code. USF is very similar to HLSL, consided it a super set with some unreal specific macros and functions to make writing HLSL shaders for unreal easy.\n\n```HLSL\n#include \"/Engine/Public/Platform.ush\"\n#include \"/Engine/Private/Common.ush\"\n#include \"/Engine/Private/ScreenPass.ush\"\n#include \"/Engine/Private/PostProcessCommon.ush\"\n\n// VS main\nvoid TriangleVS( in float2 InPosition : ATTRIBUTE0, in float4 InColor : ATTRIBUTE1, out float4 OutPosition : SV_POSITION , out float4 OutColor : COLOR0)\n{\n\tOutPosition = float4(InPosition, 0, 1);\n\tOutColor = InColor;\n}\n\n// PS main\nvoid TrianglePS(in float4 InPosition : SV_POSITION, in float4 InColor : COLOR0, out float4 OutColor : SV_Target0)\n{\n\tOutColor = InColor;\n}\n```\nWhat this shader is doing is it's taking a vertex as input and writing some attributes to be read by the Pixel shader. \nThe Vertex is of the format:\n```C++\nstruct FHelloVertex\n{\n    FVector2f Position;\n    FVector4f Color;\n};\n```\n\nNow is a great time to create 2 new files called HelloTriangleShader.h/cpp these will contain the code to create the shaders and other rendering resources like Vertex/index buffers etc.\n\nAdd this struct to the the file, we will use this later to fill the vertex buffer with vertices data.\n\nNow before we craete render resources let's start with the Shader C++ code. We need to define class for each sahder stage that derives from `FGlobalShader`. This is how it looks.\n\nVertex Shader\n```C++\nBEGIN_SHADER_PARAMETER_STRUCT(FTriangleVSParams, )\nEND_SHADER_PARAMETER_STRUCT()\nclass FTriangleVS : public FGlobalShader\n{\npublic:\n    DECLARE_GLOBAL_SHADER(FTriangleVS);\n    using FParameters = FTriangleVSParams;\n    SHADER_USE_PARAMETER_STRUCT(FTriangleVS, FGlobalShader);\n\n    static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters\u0026 Parameters) {\n        return true;\n    }\n};\n```\nSince we have no uniforms and bindinable resources the BEGIN_SHADER_PARAMETER_STRUCT and END_SHADER_PARAMETER_STRUCT doesn't define any parameters. This is how C++ and HLSL talk about dindable parameters in the shader.\n\nPixel Shader\n```C++\nBEGIN_SHADER_PARAMETER_STRUCT(FTrianglePSParams, )\n    RENDER_TARGET_BINDING_SLOTS()\nEND_SHADER_PARAMETER_STRUCT()\nclass FTrianglePS : public FGlobalShader\n{\n    DECLARE_GLOBAL_SHADER(FTrianglePS);\n    using FParameters = FTrianglePSParams;\n    SHADER_USE_PARAMETER_STRUCT(FTrianglePS, FGlobalShader)\n};\n```\nWe define RENDER_TARGET_BINDING_SLOTS() in the Pixel Shader to tell that we have output render targets to write to. Next go ahead and define the shader implementation.\n```C++\nIMPLEMENT_SHADER_TYPE(, FTriangleVS, TEXT(\"/CustomShaders/Private/HelloTriangle.usf\"), TEXT(\"TriangleVS\"), SF_Vertex);\nIMPLEMENT_SHADER_TYPE(, FTrianglePS, TEXT(\"/CustomShaders/Private/HelloTriangle.usf\"), TEXT(\"TrianglePS\"), SF_Pixel);\n```\n`IMPLEMENT_SHADER_TYPE` takes in the Shader Class, The location of the shader file (this is a Virtual File Path, relative to the folder you maped during plugin initialization.), then the Main function to call to the in shader file, in our case we named it TriangleVS and TrianglePS for each shader stage and then finally what shader stage it is, pixel, vertex, compute, geometry etc.\n\n## Setting up render resources\n\nNow that we have fonned the shaders ready and setup let's now create the rendering resources, we will need a VertexBuffer to hold the Trinagle Vertex Data, another resource to tell the Vertex Element layout (this defines how the resources are layed out and passed to the GPU) and finally a Index buffer (used only for demo purposes).\n\nAdd these to the HelloTriangleShaders.h\n```C++\nclass FTriangleVertexBuffer : public FVertexBuffer\n{\npublic:\n    void InitRHI(FRHICommandListBase\u0026 RHICmdList);\n};\n\nclass FTriangleIndexBuffer : public FIndexBuffer\n{\npublic:\n    void InitRHI(FRHICommandListBase\u0026 RHICmdList);\n};\n\nclass FTrianlgeVertexBufferElementDesc : public FRenderResource\n{\npublic:\n    FVertexDeclarationRHIRef VertexDeclarationRHI;\n    virtual ~FTrianlgeVertexBufferElementDesc() {}\n\n    virtual void InitRHI(FRHICommandListBase\u0026 RHICmdList);\n    virtual void ReleaseRHI();\n};\n\nextern HELLOTRIANGLE_API TGlobalResource\u003cFTriangleVertexBuffer\u003e GTriangleVertexBuf;\nextern HELLOTRIANGLE_API TGlobalResource\u003cFTrianlgeVertexBufferElementDesc\u003e GTriangleVertexBufElementDesc;\nextern HELLOTRIANGLE_API TGlobalResource\u003cFTriangleIndexBuffer\u003e GTriangleIndexBuf;\n```\nNow the C++ file looks like this.\nHelloTriangleShaders.cpp\n\n```C++\nTGlobalResource\u003cFTriangleVertexBuffer\u003e GTriangleVertexBuf;\nTGlobalResource\u003cFTrianlgeVertexBufferElementDesc\u003e GTriangleVertexBufElementDesc;\nTGlobalResource\u003cFTriangleIndexBuffer\u003e GTriangleIndexBuf;\n\nvoid FTriangleVertexBuffer::InitRHI(FRHICommandListBase\u0026 RHICmdList)\n{\n    TResourceArray\u003cFHelloVertex, VERTEXBUFFER_ALIGNMENT\u003e Vertices;\n    Vertices.SetNumUninitialized(3);\n    Vertices[0].Position = FVector2f(0.0f, 0.75f);\n    Vertices[0].Color = FVector4f(1, 0, 0, 1);\n    Vertices[1].Position = FVector2f(0.75, -0.75);\n    Vertices[1].Color = FVector4f(0, 1, 0, 1);\n    Vertices[2].Position = FVector2f(-0.75, -0.75);\n    Vertices[2].Color = FVector4f(0, 0, 1, 1);\n    FRHIResourceCreateInfo CreateInfo(TEXT(\"FScreenRectangleVertexBuffer\"), \u0026Vertices);\n    VertexBufferRHI = RHICmdList.CreateVertexBuffer(Vertices.GetResourceDataSize(), BUF_Static, CreateInfo);\n}\n\nvoid FTriangleIndexBuffer::InitRHI(FRHICommandListBase\u0026 RHICmdList)\n{\n    const uint16 Indices[] = { 0, 1, 2 };\n    TResourceArray\u003cuint16, INDEXBUFFER_ALIGNMENT\u003e IndexBuffer;\n    uint32 NumIndices = UE_ARRAY_COUNT(Indices);\n    IndexBuffer.AddUninitialized(NumIndices);\n    FMemory::Memcpy(IndexBuffer.GetData(), Indices, NumIndices * sizeof(uint16));\n    FRHIResourceCreateInfo CreateInfo(TEXT(\"FTriangleIndexBuffer\"), \u0026IndexBuffer);\n    IndexBufferRHI = RHICmdList.CreateIndexBuffer(sizeof(uint16), IndexBuffer.GetResourceDataSize(), BUF_Static, CreateInfo);\n}\n\nvoid FTrianlgeVertexBufferElementDesc::InitRHI(FRHICommandListBase\u0026 RHICmdList)\n{\n    FVertexDeclarationElementList Elements;\n    uint16 Stride = sizeof(FHelloVertex);\n    Elements.Add(FVertexElement(0, STRUCT_OFFSET(FHelloVertex, Position), VET_Float2, 0, Stride));\n    Elements.Add(FVertexElement(0, STRUCT_OFFSET(FHelloVertex, Color), VET_Float4, 1, Stride));\n    VertexDeclarationRHI = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);\n}\n```\n\nHere we create and overide their Init methods to create the resources.\n\n## Setting up the rendering code using RDG pass\nNow we have everything we need to start with rndering let's return to the HelloTriangleSceneViewExtension.cpp class and start using the RDG to create a new pass to render out triangle.\n\nStep1: Get the input scene texture to draw onto, use inject the rendering before anykind of post processing is done, which is why re override the function `PrePostProcessPass_RenderThread` in our FHelloTriangleViewExtension class.\n\n```C++\ncheckSlow(View.bIsViewInfo);\n    const FIntRect Viewport = static_cast\u003cconst FViewInfo\u0026\u003e(View).ViewRect;\n\n    // Get the scene texture\n    Inputs.Validate();\n\n    const FSceneViewFamily\u0026 ViewFamily = *View.Family;\n\n    // We need to make sure to take Windows and Scene scale into account.\n    float ScreenPercentage = ViewFamily.SecondaryViewFraction;\n\n    if (ViewFamily.GetScreenPercentageInterface())\n    {\n        DynamicRenderScaling::TMap\u003cfloat\u003e UpperBounds = ViewFamily.GetScreenPercentageInterface()-\u003eGetResolutionFractionsUpperBound();\n        ScreenPercentage *= UpperBounds[GDynamicPrimaryResolutionFraction];\n    }\n\n    const FIntRect PrimaryViewRect = UE::FXRenderingUtils::GetRawViewRectUnsafe(View);\n\n    FScreenPassTexture SceneColor((*Inputs.SceneTextures)-\u003eSceneColorTexture, PrimaryViewRect);\n\n    if (!SceneColor.IsValid())\n    {\n        return;\n    }\n\n```\nNo we have done the setup we start with adding a new pass called \"Triangle Pass\" to the RDG.\n- We create markers for GPU debugging\n- Create the VS/PS shader instance using the GlobalShaderMap\n- Bind the RenderTarget to the PixelShaderParams, in this case render to the scene texture, we we bind the scene color texture, and we clear it at start.\n- Now that the shaders are bound add a pass to the RDG using `GraphBuilder.AddPass`, make sure you pass the necessary params to the AddPass pambda function that you want to refernce later during the execution phase.\n\nComing to the rendering code:\n - Create the PSO:\n   - bind shaders\n   - specify DepthStencil, Rasterization, BlendState, MSAA etc params\n   - Specify the Shaders to use bind both the VS and PS\n   - Specofy the primitive type, in this case we use triangle list\n- Set the viewport and scissor rect\n- Bind the PSO to Command List\n- Bind the shader params for each shader stage in this case only the PS\n- Issue draw calls, we only draw 3 vertices for the triangle\n```C++\n{\n        RDG_EVENT_SCOPE(GraphBuilder, \"TrianglePass\");\n        RDG_GPU_STAT_SCOPE(GraphBuilder, TrianglePass);\n\n        FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel);\n        // Shader Parameter Setup, nothing for VS\n        FTrianglePSParams* PixelShaderParams = GraphBuilder.AllocParameters\u003cFTrianglePSParams\u003e();\n\n        // Set the Render Target In this case is the Scene Color, clear the scene RT\n        PixelShaderParams-\u003eRenderTargets[0] = FRenderTargetBinding(SceneColor.Texture, ERenderTargetLoadAction::EClear);\n\n        // Create FTriangleVS/PS Shader refs\n        TShaderMapRef\u003cFTrianglePS\u003e PixelShader(GlobalShaderMap);\n        check(PixelShader.IsValid());\n        TShaderMapRef\u003cFTriangleVS\u003e VertexShader(GlobalShaderMap);\n        check(VertexShader.IsValid());\n\n        ClearUnusedGraphResources(PixelShader, PixelShaderParams); // ???\n\n        // Adding the RDG pass\n        GraphBuilder.AddPass(\n            RDG_EVENT_NAME(\"HelloTriangle\"),\n            PixelShaderParams,\n            ERDGPassFlags::Raster,\n            [PixelShaderParams, PrimaryViewRect, VertexShader, PixelShader](FRHICommandList\u0026 RHICmdList)\n            {\n                // Create the Graphics Pipeline\n                FGraphicsPipelineStateInitializer GraphicsPSOInit;\n                // create stuff with default template args\n                GraphicsPSOInit.BlendState = TStaticBlendState\u003c\u003e::GetRHI();\n                GraphicsPSOInit.RasterizerState = TStaticRasterizerState\u003c\u003e::GetRHI();\n                GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState\u003c\u003e::GetRHI();\n                GraphicsPSOInit.PrimitiveType = PT_TriangleList;\n                GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();\n                GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();\n                GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GTriangleVertexBufElementDesc.VertexDeclarationRHI;\n\n                RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);\n\n                // Set viewport/scissor rect\n                RHICmdList.SetViewport(\n                    PrimaryViewRect.Min.X, PrimaryViewRect.Min.Y, 0.0f,\n                    PrimaryViewRect.Max.X, PrimaryViewRect.Max.Y, 1.0f);\n\n                RHICmdList.SetScissorRect(true, PrimaryViewRect.Min.X, PrimaryViewRect.Min.Y, PrimaryViewRect.Max.X, PrimaryViewRect.Max.Y);\n\n                // this creates and caches the PSO using the init desc\n                SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); // no stencil ref\n\n                // Bind shader params for each stage\n                SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PixelShaderParams);\n\n                // Bind VB/IB and draw\n                RHICmdList.SetStreamSource(0, GTriangleVertexBuf.VertexBufferRHI, 0);\n                RHICmdList.DrawIndexedPrimitive(GTriangleIndexBuf.IndexBufferRHI, 0, 0, 3, 0, 1, 1);\n            });\n    }\n```\nAnd voila! you have a Triangle!!!\n\n## One last thing\nYou have successfull created the SceneViewExtension class but who calls them? The USubsystem we created at the beginnign has a Initializae function that is called automatically.\nAdd this to it and instantiate it.\n```C++\nHelloTriangleViewExtension = FSceneViewExtensions::NewExtension\u003cFHelloTriangleViewExtension\u003e();\n```\n### Note about UEngineSubsystem:\n- https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/Subsystems/UEngineSubsystem?application_version=5.4\n\u003e UEngineSubsystems are dynamic and will be initialized when the module is loaded if necessary. This means that after StartupModule() is called on the module containing a subsystem, the subsystem collection with instantiate and initialize the subsystem **automatically**. If the subsystem collection is created post module load then the instances will be created at collection initialization time.\n\n## Closing notes\nIf you have any queries or suggestions or fixes for the tutorial please feel free to open and issues.\n\n## Demo\n![UE5HelloTriangle](https://github.com/user-attachments/assets/c1a83bf0-0d7a-42ef-b513-e05f1821292e)\n\u003cimg src=\"https://github.com/user-attachments/assets/dc7f6a13-0598-4dbc-88cf-8b1f9936bfc4\" alt=\"drawing\" height=\"400\"/\u003e\n\n\n## References\n- https://itscai.us/blog/post/ue-view-extensions/\n- https://dev.epicgames.com/community/learning/knowledge-base/0ql6/unreal-engine-using-sceneviewextension-to-extend-the-rendering-system\n- https://dev.epicgames.com/documentation/en-us/unreal-engine/render-dependency-graph-in-unreal-engine\n- https://dev.epicgames.com/documentation/en-us/unreal-engine/programming-subsystems-in-unreal-engine\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpikachuxxxx%2Fue5hellotriangle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpikachuxxxx%2Fue5hellotriangle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpikachuxxxx%2Fue5hellotriangle/lists"}