BlueRoseNote/03-UnrealEngine/Rendering/RenderingPipeline/RenderDependencyGraph学习笔记(三)——在插件中使用PixelShader.md
2023-06-29 11:55:02 +08:00

295 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 前言
在插件中使用RDG调用ComputeShader的方法我花了没几天就搞定了。但PixelShader相对来说就麻烦了怎么搞都没有绘制到RT上。最后还是通过改写DrawFullscreenPixelShader的代码搞定了。
另外说一下PixelShader的调用和传统的GlobalShader调用很相似。
## 设置Shader虚拟目录
这个之前忘记说了,所以这里补充一下,具体操作为在插件的模块启动函数中添加以下代码::
```
void FBRPluginsModule::StartupModule()
{
FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("BRPlugins"))->GetBaseDir(), TEXT("Shaders"));
AddShaderSourceDirectoryMapping(TEXT("/BRPlugins"), PluginShaderDir);
}
```
之后就可以使用这个虚拟目录来定义Shader
```
IMPLEMENT_GLOBAL_SHADER(FSimpleRDGComputeShader, "/BRPlugins/Private/SimpleComputeShader.usf", "MainCS", SF_Compute);
```
## 参考案例
这里我没什么管理可以推荐的感觉都不是很好。但你可以搜索ERDGPassFlags::Raster就可以找到RDG调用PixelShader的代码。
## 使用DrawFullscreenPixelShader绘制
在RDG中已经封装了一个绘制函数DrawFullscreenPixelShader()可以拿来做测试。使用的方法也比较简单直接在GraphBuilder.AddPass的Lambda中调用DrawFullscreenPixelShader即可。但
其使用的顶点格式是公共资源(CommonRenderResources.h)中的GFilterVertexDeclaration.VertexDeclarationRHI、GScreenRectangleVertexBuffer.VertexBufferRHI、GScreenRectangleIndexBuffer.IndexBufferRHI。
```
void FScreenRectangleVertexBuffer::InitRHI()
{
TResourceArray<FFilterVertex, VERTEXBUFFER_ALIGNMENT> Vertices;
Vertices.SetNumUninitialized(6);
Vertices[0].Position = FVector4(1, 1, 0, 1);
Vertices[0].UV = FVector2D(1, 1);
Vertices[1].Position = FVector4(0, 1, 0, 1);
Vertices[1].UV = FVector2D(0, 1);
Vertices[2].Position = FVector4(1, 0, 0, 1);
Vertices[2].UV = FVector2D(1, 0);
Vertices[3].Position = FVector4(0, 0, 0, 1);
Vertices[3].UV = FVector2D(0, 0);
//The final two vertices are used for the triangle optimization (a single triangle spans the entire viewport )
Vertices[4].Position = FVector4(-1, 1, 0, 1);
Vertices[4].UV = FVector2D(-1, 1);
Vertices[5].Position = FVector4(1, -1, 0, 1);
Vertices[5].UV = FVector2D(1, -1);
// Create vertex buffer. Fill buffer with initial data upon creation
FRHIResourceCreateInfo CreateInfo(&Vertices);
VertexBufferRHI = RHICreateVertexBuffer(Vertices.GetResourceDataSize(), BUF_Static, CreateInfo);
}
```
DrawFullscreenPixelShader()使用的VertexShader是FScreenVertexShaderVSusf为FullscreenVertexShader.usf。代码如下
```
#include "../Common.ush"
void MainVS(
float2 InPosition : ATTRIBUTE0,
float2 InUV : ATTRIBUTE1, // TODO: kill
out float4 Position : SV_POSITION)
{
Position = float4(InPosition.x * 2.0 - 1.0, 1.0 - 2.0 * InPosition.y, 0, 1);
}
```
这里可以看得出一个问题那就是PixelShader无法获得UV坐标。所以DrawFullscreenPixelShader()能做事情很有限。因此本人写的例子是自定义了顶点格式。
## CommonRenderResources
Ue4的已经帮我们设置好了几个基础的VertexDeclaration位于RenderCore\Public\CommonRenderResources.h。
GEmptyVertexDeclaration.VertexDeclarationRHI在Shader中的Input为
```
in uint InstanceId : SV_InstanceID,
in uint VertexId : SV_VertexID,
```
GFilterVertexDeclaration.VertexDeclarationRHI在Shader中的Input为
```
in float4 InPosition : ATTRIBUTE0,
in float2 InUV : ATTRIBUTE1,
```
对应的顶点缓存与索引缓存为TGlobalResource<FScreenRectangleVertexBuffer> GScreenRectangleVertexBuffer与TGlobalResource<FScreenRectangleIndexBuffer> GScreenRectangleIndexBuffer;
如果你想在调用PixelShader时使用FScreenRectangleVertexBuffer就需要转换UV坐标了(-1,1)=>(0,1)因为FScreenRectangleVertexBuffer的UV定义范围为(-1,1)。
## RenderTarget的传入与绑定
传入的RenderTarget皆可以用Texture2D类型声明。例如
```
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FSimpleUniformStructParameters, SimpleUniformStruct)
SHADER_PARAMETER_TEXTURE(Texture2D, TextureVal)
SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler)
SHADER_PARAMETER(FVector4, SimpleColor)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
```
绑定需要在上面的声明宏中加入RENDER_TARGET_BINDING_SLOTS(),之后设置变量时绑定:
```
FSimpleRDGPixelShader::FParameters *Parameters = GraphBuilder.AllocParameters<FSimpleRDGPixelShader::FParameters>();
Parameters->RenderTargets[0] = FRenderTargetBinding(RDGRenderTarget, ERenderTargetLoadAction::ENoAction);
```
还可以绑定DepthStencil
```
Parameters->RenderTargets.DepthStencil = FDepthStencilBinding(OutDepthTexture,ERenderTargetLoadAction::ELoad,ERenderTargetLoadAction::ELoad,FExclusiveDepthStencil::DepthNop_StencilWrite);
```
在USF中对应Out为
```
out float4 OutColor : SV_Target0,
out float OutDepth : SV_Depth
```
如果有多个RenderTarget绑定会如SV_Target0、SV_Target1、SV_Target2一般递增。
## 资源清理
本人案例中因为只有一个Pass所以就没有用这两个函数。
```
ValidateShaderParameters(PixelShader, Parameters);
ClearUnusedGraphResources(PixelShader, Parameters);
```
## RDGPixelDraw
直接上代码了。
```
void RDGDraw(FRHICommandListImmediate &RHIImmCmdList, FTexture2DRHIRef RenderTargetRHI, FSimpleShaderParameter InParameter, const FLinearColor InColor, FTexture2DRHIRef InTexture)
{
check(IsInRenderingThread());
//Create PooledRenderTarget
FPooledRenderTargetDesc RenderTargetDesc = FPooledRenderTargetDesc::Create2DDesc(RenderTargetRHI->GetSizeXY(),RenderTargetRHI->GetFormat(), FClearValueBinding::Black, TexCreate_None, TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV, false);
TRefCountPtr<IPooledRenderTarget> PooledRenderTarget;
//RDG Begin
FRDGBuilder GraphBuilder(RHIImmCmdList);
FRDGTextureRef RDGRenderTarget = GraphBuilder.CreateTexture(RenderTargetDesc, TEXT("RDGRenderTarget"));
//Setup Parameters
FSimpleUniformStructParameters StructParameters;
StructParameters.Color1 = InParameter.Color1;
StructParameters.Color2 = InParameter.Color2;
StructParameters.Color3 = InParameter.Color3;
StructParameters.Color4 = InParameter.Color4;
StructParameters.ColorIndex = InParameter.ColorIndex;
FSimpleRDGPixelShader::FParameters *Parameters = GraphBuilder.AllocParameters<FSimpleRDGPixelShader::FParameters>();
Parameters->TextureVal = InTexture;
Parameters->TextureSampler = TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
Parameters->SimpleColor = InColor;
Parameters->SimpleUniformStruct = TUniformBufferRef<FSimpleUniformStructParameters>::CreateUniformBufferImmediate(StructParameters, UniformBuffer_SingleFrame);
Parameters->RenderTargets[0] = FRenderTargetBinding(RDGRenderTarget, ERenderTargetLoadAction::ENoAction);
const ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel; //ERHIFeatureLevel::SM5
FGlobalShaderMap *GlobalShaderMap = GetGlobalShaderMap(FeatureLevel);
TShaderMapRef<FSimpleRDGVertexShader> VertexShader(GlobalShaderMap);
TShaderMapRef<FSimpleRDGPixelShader> PixelShader(GlobalShaderMap);
//ValidateShaderParameters(PixelShader, Parameters);
//ClearUnusedGraphResources(PixelShader, Parameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("RDGDraw"),
Parameters,
ERDGPassFlags::Raster,
[Parameters, VertexShader, PixelShader, GlobalShaderMap](FRHICommandList &RHICmdList) {
FRHITexture2D *RT = Parameters->RenderTargets[0].GetTexture()->GetRHI()->GetTexture2D();
RHICmdList.SetViewport(0, 0, 0.0f, RT->GetSizeX(), RT->GetSizeY(), 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GTextureVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
RHICmdList.SetStencilRef(0);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *Parameters);
RHICmdList.SetStreamSource(0, GRectangleVertexBuffer.VertexBufferRHI, 0);
RHICmdList.DrawIndexedPrimitive(
GRectangleIndexBuffer.IndexBufferRHI,
/*BaseVertexIndex=*/0,
/*MinIndex=*/0,
/*NumVertices=*/4,
/*StartIndex=*/0,
/*NumPrimitives=*/2,
/*NumInstances=*/1);
});
GraphBuilder.QueueTextureExtraction(RDGRenderTarget, &PooledRenderTarget);
GraphBuilder.Execute();
//Copy Result To RenderTarget Asset
RHIImmCmdList.CopyTexture(PooledRenderTarget->GetRenderTargetItem().ShaderResourceTexture, RenderTargetRHI->GetTexture2D(), FRHICopyTextureInfo());
}
```
调用方法和之前的ComputeShader部分相同这里就不赘述了。具体的可以参考我的插件。
### 自定义顶点格式部分
```
struct FTextureVertex
{
FVector4 Position;
FVector2D UV;
};
class FRectangleVertexBuffer : public FVertexBuffer
{
public:
/** Initialize the RHI for this rendering resource */
void InitRHI() override
{
TResourceArray<FTextureVertex, VERTEXBUFFER_ALIGNMENT> Vertices;
Vertices.SetNumUninitialized(6);
Vertices[0].Position = FVector4(1, 1, 0, 1);
Vertices[0].UV = FVector2D(1, 1);
Vertices[1].Position = FVector4(-1, 1, 0, 1);
Vertices[1].UV = FVector2D(0, 1);
Vertices[2].Position = FVector4(1, -1, 0, 1);
Vertices[2].UV = FVector2D(1, 0);
Vertices[3].Position = FVector4(-1, -1, 0, 1);
Vertices[3].UV = FVector2D(0, 0);
//The final two vertices are used for the triangle optimization (a single triangle spans the entire viewport )
Vertices[4].Position = FVector4(-1, 1, 0, 1);
Vertices[4].UV = FVector2D(-1, 1);
Vertices[5].Position = FVector4(1, -1, 0, 1);
Vertices[5].UV = FVector2D(1, -1);
// Create vertex buffer. Fill buffer with initial data upon creation
FRHIResourceCreateInfo CreateInfo(&Vertices);
VertexBufferRHI = RHICreateVertexBuffer(Vertices.GetResourceDataSize(), BUF_Static, CreateInfo);
}
};
class FRectangleIndexBuffer : public FIndexBuffer
{
public:
/** Initialize the RHI for this rendering resource */
void InitRHI() override
{
// Indices 0 - 5 are used for rendering a quad. Indices 6 - 8 are used for triangle optimization.
const uint16 Indices[] = {0, 1, 2, 2, 1, 3, 0, 4, 5};
TResourceArray<uint16, INDEXBUFFER_ALIGNMENT> IndexBuffer;
uint32 NumIndices = UE_ARRAY_COUNT(Indices);
IndexBuffer.AddUninitialized(NumIndices);
FMemory::Memcpy(IndexBuffer.GetData(), Indices, NumIndices * sizeof(uint16));
// Create index buffer. Fill buffer with initial data upon creation
FRHIResourceCreateInfo CreateInfo(&IndexBuffer);
IndexBufferRHI = RHICreateIndexBuffer(sizeof(uint16), IndexBuffer.GetResourceDataSize(), BUF_Static, CreateInfo);
}
};
class FTextureVertexDeclaration : public FRenderResource
{
public:
FVertexDeclarationRHIRef VertexDeclarationRHI;
virtual void InitRHI() override
{
FVertexDeclarationElementList Elements;
uint32 Stride = sizeof(FTextureVertex);
Elements.Add(FVertexElement(0, STRUCT_OFFSET(FTextureVertex, Position), VET_Float2, 0, Stride));
Elements.Add(FVertexElement(0, STRUCT_OFFSET(FTextureVertex, UV), VET_Float2, 1, Stride));
VertexDeclarationRHI = RHICreateVertexDeclaration(Elements);
}
virtual void ReleaseRHI() override
{
VertexDeclarationRHI.SafeRelease();
}
};
/*
* Vertex Resource Declaration
*/
extern TGlobalResource<FTextureVertexDeclaration> GTextureVertexDeclaration;
extern TGlobalResource<FRectangleVertexBuffer> GRectangleVertexBuffer;
extern TGlobalResource<FRectangleIndexBuffer> GRectangleIndexBuffer;
```
```
TGlobalResource<FTextureVertexDeclaration> GTextureVertexDeclaration;
TGlobalResource<FRectangleVertexBuffer> GRectangleVertexBuffer;
TGlobalResource<FRectangleIndexBuffer> GRectangleIndexBuffer;
```