UE4虚幻引擎插件添加GlobalShader

1

主题

5

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2022-12-24 13:41:37 | 显示全部楼层
话说前头

       最近希望能在插件中添加GlobalShader来进行绘制,找了一圈发现了一篇比较适合的文章。
      但是可能作者写的时间比较早,导致里面有些接口已经没有了。对于Copy程序员的我来说,这给我造成了一定的困扰,但同时我相信这个问题也会困扰后面的Copy程序员。所以就有了这篇文章,主要是简单介绍一下怎么在插件中添加GlobalShader并且能成功绘制。在此还得感谢前人的努力,不然还得困扰我好一阵子。
     最后声明一点,本篇文章主要基于4.27.2的UE版本,编译并且效果正确。不保证其余版本的UE也能够编译通过。
创建插件

     首先先创建一个空的插件,取名为“GraphicTools”名字倒不是必须一致,全凭个人喜好。创建完插件以后需要更改一下几处。

  •     首先在插件的uplugin文件中将插件的的LoadingPhase改成“PostConfigInit”,毕竟我们这个插件要包含GlobalShader,所以需要更改这个模块的加载时机。



  • 在插件同名的Cpp文件中,添加下图的代码。这一步是设置shader的虚拟地址,能让引擎找到我们的shader文件


// Copyright Epic Games, Inc. All Rights Reserved.

#include "GraphicTools.h"
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
#include "Interfaces/IPluginManager.h"
#include "Misc/Paths.h"
#include "ShaderCore.h"

#define LOCTEXT_NAMESPACE "FGraphicToolsModule"

void FGraphicToolsModule::StartupModule()
{
        // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
        FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("GraphicTools"))->GetBaseDir(), TEXT("Shaders"));
        AddShaderSourceDirectoryMapping(TEXT("/Plugin/GraphicTools"), PluginShaderDir);
}

void FGraphicToolsModule::ShutdownModule()
{
        // This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
        // we call this function before unloading the module.
}

#undef LOCTEXT_NAMESPACE
       
IMPLEMENT_MODULE(FGraphicToolsModule, GraphicTools)

  • 在插件的build.cs文件中添加如下依赖


至此插件的创建的工作就做完了,现在开始添加对应文件以及代码。
创建蓝图接口

     要想实现绘制操作,首先我们需要在蓝图中能够执行我们的绘制指令,所以需要写一个类继承自UBlueprintFunctionLibrary,然后再添加对应的绘制函数。我们先创建一个GraphicToolsBlueprintFunctionLib.h以及对应的cpp文件
#pragma once
#include "UObject/ObjectMacros.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "GraphicToolsBlueprintFunctionLib.generated.h"
UCLASS(MinimalAPI, meta=(ScriptName = "GraphicTools"))
class UGraphicToolsBlueprintFunctionLib :  public  UBlueprintFunctionLibrary
{
        GENERATED_BODY()
public:
        UGraphicToolsBlueprintFunctionLib(){}
        UFUNCTION(BlueprintCallable, Category = "SLSGraphicTools", meta = (WorldContext = "WorldContextObject"))
        static void DrawColorBoard(
                const UObject* WorldContextObject,
                class UTextureRenderTarget2D* OutputRenderTarget,
                FLinearColor Color);
};添加了一个DrawColorBoard函数用于实现我们的绘制命令,由于我们希望能够画出颜色,所以函数的入参运行传入颜色。
void UGraphicToolsBlueprintFunctionLib::DrawColorBoard(const UObject* WorldContextObject,
        UTextureRenderTarget2D* OutputRenderTarget,
        FLinearColor Color)
{
        check(IsInGameThread());

        if (!OutputRenderTarget)
        {
                FMessageLog("Blueprint").Warning(
                        LOCTEXT("UGraphicToolsBlueprintLibrary::DrawCheckerBoard",
                                "DrawUVDisplacementToRenderTarget: Output render target is required."));
                return;
        }

        FTextureRenderTargetResource* TextureRenderTargetResource = OutputRenderTarget->GameThread_GetRenderTargetResource();
        ERHIFeatureLevel::Type FeatureLevel = WorldContextObject->GetWorld()->Scene->GetFeatureLevel();
        ENQUEUE_RENDER_COMMAND(CaptureCommand)
        (
                [TextureRenderTargetResource, Color, FeatureLevel](FRHICommandListImmediate& RHICmdList)
                {
                        DrawColorBoard_RenderThread
                        (
                                RHICmdList,
                                TextureRenderTargetResource,
                                Color,
                                FeatureLevel
                        );
                }
        );
}这段就是函数的实现,可以看到通过ENQUEUE_RENDER_COMMAND这个宏,会往渲染线程添加一个绘制指令,然后就是一个Lambda函数,先捕获我们要的参数,然后最终会调到我们真正的渲染函数DrawColorBoard_RenderThread,这个后面再说,我们先假装实现了。
定义了这个函数以后,打开引擎添加一个新的蓝图类,就可以在蓝图类中调用我们写的函数了。


可以看到函数支持输入一个颜色还有一张Rt。
创建Shader

      接下来,就是要创建shader了。首先我们在我们plugin目录下创建一个Shaders/Private这两个文件夹,然后再添加一个DrawColorShader.usf文件,填上下方的内容。就是一个极其简单的shader,就是绘制一个颜色。
#include "/Engine/Public/Platform.ush"

float4 SimpleColor;
void MainVS(
in float4 InPosition : ATTRIBUTE0,
out float4 OutPosition : SV_POSITION
)
{
        // screenspace position from vb
        OutPosition = InPosition;
}

void MainPS(
        out float4 OutColor : SV_Target0
        )
{
        OutColor = SimpleColor;
}然后回到GraphicToolsBlueprintFunctionLib.cpp中定义Shader类。
class FMyShaderVS : public FGlobalShader
{
        DECLARE_SHADER_TYPE(FMyShaderVS, Global);
public:
        FMyShaderVS() {}
        FMyShaderVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
                : FGlobalShader(Initializer)
        {
               
        }

};

class FMyShaderPS : public FGlobalShader
{
        DECLARE_SHADER_TYPE(FMyShaderPS, Global);
public:
        FMyShaderPS() {}
        FMyShaderPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
                : FGlobalShader(Initializer)
        {
                SimpleColor.Bind(Initializer.ParameterMap, TEXT("SimpleColor"));
        }

        LAYOUT_FIELD(FShaderParameter, SimpleColor);

};

IMPLEMENT_SHADER_TYPE(, FMyShaderVS, TEXT("/Plugin/GraphicTools/Private/DrawColorShader.usf"), TEXT("MainVS"), SF_Vertex);  
IMPLEMENT_SHADER_TYPE(, FMyShaderPS, TEXT("/Plugin/GraphicTools/Private/DrawColorShader.usf"), TEXT("MainPS"), SF_Pixel);关键的绘制命令

有了这些以后,接下来就是关键的绘制命令的函数了,也就是我们上方提到的DrawColorBoard_RenderThread函数
struct FTextureVertex
{
public:
        FVector4 Position;
        //FVector2D UV;
};
static void DrawColorBoard_RenderThread(
        FRHICommandListImmediate& RHICmdList,
        FTextureRenderTargetResource* TextureRenderTargetResource,
        FLinearColor Color,
        ERHIFeatureLevel::Type FeatureLevel
        )
{
        FRHITexture2D* RenderTargetTexture = TextureRenderTargetResource->GetRenderTargetTexture();
        RHICmdList.TransitionResource(EResourceTransitionAccess::EWritable, RenderTargetTexture);
        FRHIRenderPassInfo RPInfo(RenderTargetTexture, ERenderTargetActions::DontLoad_Store, TextureRenderTargetResource->TextureRHI);
        RHICmdList.BeginRenderPass(RPInfo, TEXT("shaderTest"));
        {

                FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(FeatureLevel);  
                TShaderMapRef<FMyShaderVS> VertexShader(GlobalShaderMap);  
                TShaderMapRef<FMyShaderPS> PixelShader(GlobalShaderMap);
                FGraphicsPipelineStateInitializer GraphicsPSOInit;
                RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
                GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
                GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
                GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
                GraphicsPSOInit.PrimitiveType = PT_TriangleStrip;
                GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4();
                GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
                GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
                SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);

                SetShaderValue(RHICmdList, PixelShader.GetPixelShader(), PixelShader->SimpleColor,Color);
               
                FRHIResourceCreateInfo CreateInfo;
                FVertexBufferRHIRef VertexBufferRHI = RHICreateVertexBuffer(sizeof(FTextureVertex) * 4, BUF_Volatile, CreateInfo);
                void* VoidPtr = RHILockVertexBuffer(VertexBufferRHI, 0, sizeof(FTextureVertex) * 4, RLM_WriteOnly);

                FTextureVertex* Vertices = (FTextureVertex*)VoidPtr;
                Vertices[0].Position = FVector4(-1.0f, 1.0f, 0, 1.0f);
                Vertices[1].Position = FVector4(1.0f, 1.0f, 0, 1.0f);
                Vertices[2].Position = FVector4(-1.0f, -1.0f, 0, 1.0f);
                Vertices[3].Position = FVector4(1.0f, -1.0f, 0, 1.0f);
                RHIUnlockVertexBuffer(VertexBufferRHI);

                RHICmdList.SetStreamSource(0, VertexBufferRHI, 0);
                RHICmdList.DrawPrimitive(0, 2, 1);
        }
        RHICmdList.EndRenderPass();
}里面的一些含义,如果有空可以再来翻译一下,不过也很简单。相信大家肯定看的懂。有了这些以后,我们就可以看看实际的效果了。
最终效果

我么将绘制的RT随便放到一个Plane模型上,通过调整参数可以发现我们的绘制生效了。


全部代码

#include "GraphicToolsBlueprintFunctionLib.h"

#include "Runtime/Engine/Classes/Engine/TextureRenderTarget2D.h"
#include "Runtime/Engine/Classes/Engine/World.h"
#include "GlobalShader.h"
#include "PipelineStateCache.h"
#include "RHIStaticStates.h"
#include "SceneUtils.h"
#include "SceneInterface.h"
#include "ShaderParameterUtils.h"
#include "Logging/MessageLog.h"
#include "Internationalization/Internationalization.h"
#include "Runtime/RenderCore/Public/RenderTargetPool.h"


#define LOCTEXT_NAMESPACE "GraphicToolsPlugin"


class FMyShaderVS : public FGlobalShader
{
        DECLARE_SHADER_TYPE(FMyShaderVS, Global);
public:
        FMyShaderVS() {}
        FMyShaderVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
                : FGlobalShader(Initializer)
        {
               
        }

};

class FMyShaderPS : public FGlobalShader
{
        DECLARE_SHADER_TYPE(FMyShaderPS, Global);
public:
        FMyShaderPS() {}
        FMyShaderPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
                : FGlobalShader(Initializer)
        {
                SimpleColor.Bind(Initializer.ParameterMap, TEXT("SimpleColor"));
        }

        LAYOUT_FIELD(FShaderParameter, SimpleColor);

};

IMPLEMENT_SHADER_TYPE(, FMyShaderVS, TEXT("/Plugin/GraphicTools/Private/DrawColorShader.usf"), TEXT("MainVS"), SF_Vertex);  
IMPLEMENT_SHADER_TYPE(, FMyShaderPS, TEXT("/Plugin/GraphicTools/Private/DrawColorShader.usf"), TEXT("MainPS"), SF_Pixel);
struct FTextureVertex
{
public:
        FVector4 Position;
        //FVector2D UV;
};
static void DrawColorBoard_RenderThread(
        FRHICommandListImmediate& RHICmdList,
        FTextureRenderTargetResource* TextureRenderTargetResource,
        FLinearColor Color,
        ERHIFeatureLevel::Type FeatureLevel
        )
{
        FRHITexture2D* RenderTargetTexture = TextureRenderTargetResource->GetRenderTargetTexture();
        RHICmdList.TransitionResource(EResourceTransitionAccess::EWritable, RenderTargetTexture);
        FRHIRenderPassInfo RPInfo(RenderTargetTexture, ERenderTargetActions::DontLoad_Store, TextureRenderTargetResource->TextureRHI);
        RHICmdList.BeginRenderPass(RPInfo, TEXT("shaderTest"));
        {

                FGlobalShaderMap* GlobalShaderMap = GetGlobalShaderMap(FeatureLevel);  
                TShaderMapRef<FMyShaderVS> VertexShader(GlobalShaderMap);  
                TShaderMapRef<FMyShaderPS> PixelShader(GlobalShaderMap);
                FGraphicsPipelineStateInitializer GraphicsPSOInit;
                RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
                GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
                GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
                GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
                GraphicsPSOInit.PrimitiveType = PT_TriangleStrip;
                GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GetVertexDeclarationFVector4();
                GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
                GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
                SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);

                SetShaderValue(RHICmdList, PixelShader.GetPixelShader(), PixelShader->SimpleColor,Color);
               
                FRHIResourceCreateInfo CreateInfo;
                FVertexBufferRHIRef VertexBufferRHI = RHICreateVertexBuffer(sizeof(FTextureVertex) * 4, BUF_Volatile, CreateInfo);
                void* VoidPtr = RHILockVertexBuffer(VertexBufferRHI, 0, sizeof(FTextureVertex) * 4, RLM_WriteOnly);

                FTextureVertex* Vertices = (FTextureVertex*)VoidPtr;
                Vertices[0].Position = FVector4(-1.0f, 1.0f, 0, 1.0f);
                Vertices[1].Position = FVector4(1.0f, 1.0f, 0, 1.0f);
                Vertices[2].Position = FVector4(-1.0f, -1.0f, 0, 1.0f);
                Vertices[3].Position = FVector4(1.0f, -1.0f, 0, 1.0f);
                RHIUnlockVertexBuffer(VertexBufferRHI);

                RHICmdList.SetStreamSource(0, VertexBufferRHI, 0);
                RHICmdList.DrawPrimitive(0, 2, 1);
        }
        RHICmdList.EndRenderPass();
}

void UGraphicToolsBlueprintFunctionLib::DrawColorBoard(const UObject* WorldContextObject,
        UTextureRenderTarget2D* OutputRenderTarget,
        FLinearColor Color)
{
        check(IsInGameThread());

        if (!OutputRenderTarget)
        {
                FMessageLog("Blueprint").Warning(
                        LOCTEXT("UGraphicToolsBlueprintLibrary::DrawCheckerBoard",
                                "DrawUVDisplacementToRenderTarget: Output render target is required."));
                return;
        }

        FTextureRenderTargetResource* TextureRenderTargetResource = OutputRenderTarget->GameThread_GetRenderTargetResource();
        ERHIFeatureLevel::Type FeatureLevel = WorldContextObject->GetWorld()->Scene->GetFeatureLevel();
        ENQUEUE_RENDER_COMMAND(CaptureCommand)
        (
                [TextureRenderTargetResource, Color, FeatureLevel](FRHICommandListImmediate& RHICmdList)
                {
                        DrawColorBoard_RenderThread
                        (
                                RHICmdList,
                                TextureRenderTargetResource,
                                Color,
                                FeatureLevel
                        );
                }
        );
}


#undef LOCTEXT_NAMESPACE
回复

举报 使用道具

您需要登录后才可以回帖 登录 | 立即注册
快速回复 返回顶部 返回列表