ainatipen 发表于 2022-1-5 17:32

虚幻4渲染编程(Shader篇)【第十八卷:Render Graph in UE4】

MY BLOG DIRECTORY:
INTRODUCTION:
UnrealEngine4在最近一段时间的版本里(4.24)开始大规模使用一个叫RenderGraph的渲染框架重构整个渲染管线。不过类似BasePass这种涉及到MeshDrawCommand的部分感觉epic没有意向进行改动。如果不修改MeshDrawCommand部分的框架的前提下,我觉得这套框架比较适合屏幕空间的绘制管线,对于单个物体的绘制可能并不合适。
RenderGraph这套框架也是对底层的进一步封装,还完成了资源管理。图形部分各种资源的管理看似变得更加轻松。对于异步支持完善,能使代码功力不怎么强的TA也能轻松驾驭Unreal的底层管线(滑稽)。
RenderGraph的大体思路就是构建一个渲染表,最后调用Execute()来执行图表中的渲染逻辑,层次非常清晰。
<hr/>MAIN CONTENT:


RenderGraph可以先从RenderGraph.h这个文件开始,这里有一大片注释写了如何用它这套框架。RenderGraph主要包含两个重要组件,一个是FRDGBuilder,负责构建Render graph的资源及添加pass等,构建RenderGraph。另一个是FRDGResource,RenderGraph的资源类,所有资源都由它派生。
RenderGraph的渲染逻辑位于Pass内,要创建一个Pass使用GraphBuilder.AddPass来添加。


下面是一个使用案例:
GraphBuilder.AddPass(
        RDG_EVENT_NAME("SimpleTest"),
        PassParameters,
        ERDGPassFlags::Raster,
        (FRHICommandList& RHICmdList)
        {
                FPixelShaderUtils::DrawFullscreenPixelShader(RHICmdList, View.ShaderMap, *PixelShader, *PassParameters, FIntRect(0, 0, TexBufferSize.X, TexBufferSize.Y));
        });AddPass函数的第一个参数是这个pass事件的名字,第二个是UniformBuffer,第三个是Pass的类型,第四个是一个Lambda函数,这个函数会带有渲染逻辑,当RenderGraph调用Execute的时候,这里面的逻辑会被执行。
当一个Pass被创建的时候,必须带有一个PassParameters。PassParameters里至少要包含此次Pass的Render target。
下面我完成一个最简单的需求:用Render grap向SceneColorTexture上绘制最简单的颜色。


首先在DeferredShadingRender中添加一个调用这个Rendergraph的函数


然后再Render函数里添加上


然后在新建一个cpp文件,键入如下代码:


#include "CoreMinimal.h"
#include "SceneRendering.h"
#include "RHICommandList.h"
#include "Shader.h"
#include "RHIStaticStates.h"
#include "ScenePrivate.h"
#include "RenderGraph.h"
#include "PixelShaderUtils.h"
#include "SceneTextureParameters.h"
#include "DeferredShadingRenderer.h"

class FSimpleUseRenderGraphPS : public FGlobalShader
{
        DECLARE_GLOBAL_SHADER(FSimpleUseRenderGraphPS);
        SHADER_USE_PARAMETER_STRUCT(FSimpleUseRenderGraphPS, FGlobalShader)

        BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
                RENDER_TARGET_BINDING_SLOTS()
        END_SHADER_PARAMETER_STRUCT()

        static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
        {
                return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
        }

        static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
        {
               
        }
};

IMPLEMENT_GLOBAL_SHADER(FSimpleUseRenderGraphPS, "/Engine/Private/MyGS/MyRenderGraph.usf", "MainPS", SF_Pixel);

void FDeferredShadingSceneRenderer::SimpleUseRenderGraph(FRHICommandListImmediate& RHICmdList)
{
        FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);

        FRDGBuilder GraphBuilder(RHICmdList);

        FIntPoint TexBufferSize = FIntPoint(SceneContext.GetSceneDepthTexture()->GetSizeX(), SceneContext.GetSceneDepthTexture()->GetSizeY());

        FRDGTextureRef SceneColor = GraphBuilder.RegisterExternalTexture(SceneContext.GetSceneColor(), TEXT("SceneColor"));

        for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
        {
                FViewInfo& View = Views;

                FSceneTextureParameters SceneTextures;
                SetupSceneTextureParameters(GraphBuilder, &SceneTextures);

                RDG_EVENT_SCOPE(GraphBuilder, "SimpleUseRenderGraph(ViewId=%d)", ViewIndex);

                TShaderMapRef<FSimpleUseRenderGraphPS> PixelShader(View.ShaderMap);

                FSimpleUseRenderGraphPS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSimpleUseRenderGraphPS::FParameters>();
                PassParameters->RenderTargets = FRenderTargetBinding(SceneColor, ERenderTargetLoadAction::ELoad);
                ClearUnusedGraphResources(*PixelShader, PassParameters);

                GraphBuilder.AddPass(
                        RDG_EVENT_NAME("SimpleTest"),
                        PassParameters,
                        ERDGPassFlags::Raster,
                        (FRHICommandList& RHICmdList)
                        {
                                FPixelShaderUtils::DrawFullscreenPixelShader(RHICmdList, View.ShaderMap, *PixelShader, *PassParameters, FIntRect(0, 0, TexBufferSize.X, TexBufferSize.Y));
                        });

        }
        GraphBuilder.Execute();
}
可以看到这里要做一个全屏的绘制使用RenderGraph就十分简单了,短短70行代码就搞定了,之前的文章里随便都是几百行。
然后再把shader完成即可
MyRenderGraph.usf




我这里的绘制资源都是使用引擎已经创建好的,如果想使用自己创建的Texture或者UniformBuffer需要用RenderGraphBuild创建


如果是Texture,在AddPass以后记得Extraction


<hr/>SUMMARY AND OUTLOOK:
RenderGraph其实就是一个再次封装的渲染框架,能使开发更为容易。而且epic也会用这套来重构引擎,所以搞懂这个还是很有必要的。
enjoy it.
<hr/>NEXT:
<hr/>By YivanLee 2020/1/20

zt3ff3n 发表于 2022-1-5 17:41

也不知道啥时候吧RDG和MeshDraw融合一下

Ilingis 发表于 2022-1-5 17:51

frame graph

RecursiveFrog 发表于 2022-1-5 17:58

FrameGraph可能不准确,因为有些逻辑不是一帧里执行的,所以Unreal使用了RenderGraph这个术语。

APSchmidt 发表于 2022-1-5 18:00

是因为Frame包括但不止渲染。还有动画Graph等等[飙泪笑]

zt3ff3n 发表于 2022-1-5 18:02

可以多pass了?

TheLudGamer 发表于 2022-1-5 18:08

什么?

BlaXuan 发表于 2022-1-5 18:09

楼主有清楚void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)这个函数吗?希望讲解一下

stonstad 发表于 2022-1-5 18:14

前面的文章有

xiaozongpeng 发表于 2022-1-5 18:20

查找shader的路径真是诡异,居然跳过Shaders这一级。
页: [1] 2
查看完整版本: 虚幻4渲染编程(Shader篇)【第十八卷:Render Graph in UE4】