## 前言 在插件中使用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 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是FScreenVertexShaderVS,usf为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 GScreenRectangleVertexBuffer与TGlobalResource 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(); 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 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(); Parameters->TextureVal = InTexture; Parameters->TextureSampler = TStaticSamplerState::GetRHI(); Parameters->SimpleColor = InColor; Parameters->SimpleUniformStruct = TUniformBufferRef::CreateUniformBufferImmediate(StructParameters, UniformBuffer_SingleFrame); Parameters->RenderTargets[0] = FRenderTargetBinding(RDGRenderTarget, ERenderTargetLoadAction::ENoAction); const ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel; //ERHIFeatureLevel::SM5 FGlobalShaderMap *GlobalShaderMap = GetGlobalShaderMap(FeatureLevel); TShaderMapRef VertexShader(GlobalShaderMap); TShaderMapRef 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::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 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 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 GTextureVertexDeclaration; extern TGlobalResource GRectangleVertexBuffer; extern TGlobalResource GRectangleIndexBuffer; ``` ``` TGlobalResource GTextureVertexDeclaration; TGlobalResource GRectangleVertexBuffer; TGlobalResource GRectangleIndexBuffer; ```