|
发表于 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)

至此插件的创建的工作就做完了,现在开始添加对应文件以及代码。
创建蓝图接口
要想实现绘制操作,首先我们需要在蓝图中能够执行我们的绘制指令,所以需要写一个类继承自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 &#34;GraphicToolsBlueprintFunctionLib.h&#34;
#include &#34;Runtime/Engine/Classes/Engine/TextureRenderTarget2D.h&#34;
#include &#34;Runtime/Engine/Classes/Engine/World.h&#34;
#include &#34;GlobalShader.h&#34;
#include &#34;PipelineStateCache.h&#34;
#include &#34;RHIStaticStates.h&#34;
#include &#34;SceneUtils.h&#34;
#include &#34;SceneInterface.h&#34;
#include &#34;ShaderParameterUtils.h&#34;
#include &#34;Logging/MessageLog.h&#34;
#include &#34;Internationalization/Internationalization.h&#34;
#include &#34;Runtime/RenderCore/Public/RenderTargetPool.h&#34;
#define LOCTEXT_NAMESPACE &#34;GraphicToolsPlugin&#34;
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(&#34;SimpleColor&#34;));
}
LAYOUT_FIELD(FShaderParameter, SimpleColor);
};
IMPLEMENT_SHADER_TYPE(, FMyShaderVS, TEXT(&#34;/Plugin/GraphicTools/Private/DrawColorShader.usf&#34;), TEXT(&#34;MainVS&#34;), SF_Vertex);
IMPLEMENT_SHADER_TYPE(, FMyShaderPS, TEXT(&#34;/Plugin/GraphicTools/Private/DrawColorShader.usf&#34;), TEXT(&#34;MainPS&#34;), 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(&#34;shaderTest&#34;));
{
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(&#34;Blueprint&#34;).Warning(
LOCTEXT(&#34;UGraphicToolsBlueprintLibrary::DrawCheckerBoard&#34;,
&#34;DrawUVDisplacementToRenderTarget: Output render target is required.&#34;));
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 |
|