custom mesh render pass

基础概念

  • UPrimitiveComponent: 可用于渲染或者物理交互的基类,游戏线程拥有其所有变量和状态
  • FPrimitiveSceneProxy: UPrimitiveComponent的渲染线程版本
  • FPrimitiveSceneInfo: UPrimitiveComponent在渲染器的内部状态,与FPrimitiveSceneProxy一一映射

    FMeshDrawCommand

保存了在一个特定Pass下绘制一个Mesh所需要知道的所有信息

MeshDrawCommand

  • ShaderBindings: 记录了Shader各个阶段绑定的参数集
  • VertexStreams: 记录了VertexBuffer相关信息
  • IndexBuffer: 记录了IndexBuffer相关信息
  • CachedPipelineId: 用于索引GraphicsPipelineState

FMeshBatch

FMeshBatch
FPrimitiveSceneProxy生成FMeshBatch有两个途径

  • Cache
  • Dynamic

具体由哪个途径生成FMeshBatch通过FPrimitiveSceneProxy::GetViewRelevance决定

橙色表示每帧都执行,蓝色表示只执行一次然后被Cache

StaticMesh来说就是Cache的方式,在Proxy添加到场景时调用DrawStaticElements
生成的FMeshBatch被保存在FPrimitiveSceneInfoStaticMeshes
每帧进行重用直到Proxy从场景中移除
Cache Path

Dynamic的方式将会每帧重建FMeshBatch,适用于粒子这种
通过调用GetDynamicElements来生成
Dynamic Path

MeshProcessor

  • Select Shader
  • Collect Pass Bindings, vertex Factory bindings, material bindings
  • Builds One Or More FMeshDrawCommands From a FMeshBatch

FMeshPassProcessor

  • AddMeshBatch: 必须重载,根据FMeshBatch生成FMeshDrawCommand

每个Pass都需要实现一个MeshPassProcessor,以DepthRendering为例:
DepthPass AddMeshBatch
通过调用BuildMeshDrawCommands生成MeshDrawCommand
DepthPass BuildMeshDrawCommand

MeshDrawCommand如何产生?

Cached Mesh Draw Command

DynamicMeshMeshDrawCommand需要每帧产生
目前只有FLocalVertexFactoy,即(UStaticComponent)可以被Cached
因为其它的VertexFactory需要依赖View来设置Shader Binding

  • Static Relevance

Static MeshBatch Cache Flow

FBatchingSPDI::DrawMesh如下:
生成MeshBatch,并确定是否支持Cache为MeshDrawCommand

可见对于StaticMesh而言,MeshBatch可以提前Cache
MeshDrawCommand需要根据SupportsCachingMeshDrawCommands确定是否能Cache

SupportsCachingMeshDrawCommands如下:
SupportsCachingMeshDrawCommands

目前只有FLocalVertexFactory支持Cache MeshDrawCommand
如果VertexFactory依赖于View,由于View会变,则Shader Bindings需要每帧更新,因此无法Cache MeshDrawCommand

Shader Bindings:

  • Pass-Constant uniform buffer, 如ViewUniformBuffer,DepthPassUniformBuffer
  • Vertex Factory Bindings
  • Materail Bindings
  • Primitive Bindings
  • Pass specific bindings which change between draws

目前已知DynamicMesh需要每帧生成MeshDrawCommand
StaticMesh根据StaticMeshBatchRelevance决定是否需要重新生成

先看下Cache MeshDrawCommand的过程:
Cache Generate MeshDrawCommand

CacheMeshDrawCommands的实现如下:
CacheMeshDrawCommands

会按PassType存储在Scene::CacheDrawLists[PassType]

接下来看下如何收集MeshDrawCommand
Gather MeshDrawCommand

MeshDrawCommand的收集就在ComputeViewVisibility中完成

  • Static MeshDrawCommand的收集如下:
    Gather Static MeshDrawCommand

AddCommandsForMesh中会根据bSupportsCachingMeshDrawCommands来决定是否已经Cache好了
AddCommandsForMesh

需要生成MeshDrawCommandStaticMeshBatch记录在DynamicBuildRequests

  • Dynamic MeshDrawCommand收集

DynamicMesh的MeshBatch需要每帧收集:
Dynamic Path

然后通过SetupMeshPassStaticMeshBatchDynamicMeshBatch转换为MeshDrawCommand

因此生成MeshDrawCommand的大致流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
void ComputeViewVisibility(...)
{
...

GatherStaticMeshDrawCommandAndUnCachedMeshBatch(...);

GatherDynamicMeshBatch(...);

for(int viewIndex = 0; viewIndex < Views.Num(); ViewIndex++)
{
SetupMeshPass(Views[viewIndex], ...)
}
}

最终这些MeshDrawCommand被存储于FViewInfo::ParallelMeshDrawCommandPasses[PassType]

MeshDrawCommand如何使用

最终通过调用以下语句触发DrawCall

1
View.ParallelMeshDrawCommandPasses[MeshPassType].DispatchDraw(nullptr, RHICmdList);

custom mesh pass

以复制一个简化版的DepthPass为例

添加Pass枚举

打开文件MeshPassProcessor.h,修改EMeshPass结构体,如下:
Modify MeshPass Enum

修改GetMeshPassName,如下:
Modify GetMeshPassName

新建文件

新增MyTestPassRendering.h,MyTestPassRendering.cpp
存至Engine/Source/Runtime/Renderer/Private目录

添加Shader

新建文件MyTestVertexShader.usf,存于Engine\Shaders\Private目录
MyTestVertexShader
(照抄的PositionOnlyDepthVertexShader.usf)

修改MyTestPassRendering.h

新增类FMyTestVS,如下:
MyTestVS

MyTestPassRendering.cpp中通过IMPLEMENT_MATERIAL_SHADER_TYPE实现材质shader
Implement Material Shader
IMPLEMENT_SHADERPIPELINE_TYPE_VS就是构建一个只有VSFShaderPipelineType对象

添加MeshPassProcessor

FMyTestPassMeshProcessor

AddMeshBatch是必须要重载的函数

然后需要把这个类型的MeshPassProcessor通过以下方式注册到FPassProcessorManager
Register PassProcessor Create Function

这里是把一个函数指针通过FRegisterPassProcessorCreateFunction的构造函数,记录到FPassProcessorManager

SetupMyTestPassState负责设置渲染状态,这里实现如下:
SetupMeshPassState

构造函数实现如下:
FMyTestPassMeshProcessor Constructor

构造函数中调用了三个SetUniformBuffer

  • SetViewUniformBuffer: ViewUniformBuffer中包含各种变换矩阵以及计算用的贴图数据等等
  • SetInstancedViewUniformBuffer: 和ViewUniformBuffer差不多,用于Instance Stereo
  • SetPassUniformBuffer: 包含SceneTexture,如GBuffer,SceneDepthTexture等,便于MaterialGraphGlobalShader使用

这里我们自己定义一个MyTestPassUniformBuffer:
Define MyTestPassUniformBuffer

初始化MyTestPassUniformBuffer:
InitMyTestPassUniformBuffer

接下来看AddMeshBatch:
AddMeshBatch
做了个Pass Filter
这个实现里也没啥东西…调用了Process函数,直接看Process
Process

这个函数就是先做了下Shader Filter,然后生成MeshDrawCommand

调用BuildMeshDrawCommands时,传入了一个参数PassDrawRenderState
在构造函数中对PassDrawRenderState设置了三个UniformBuffer
因此生成的MeshDrawCommand都是绑定了这三个UniformBuffer

GetMyTestPassShaders实现Shader Filter:
GetMyTestPassShaders

收集MeshDrawCommand

之前讲过了MeshDrawCommand的三个来源,那么生成了MeshDrawCommand之后,还需要确定哪些需要被这个Pass调用

打开SceneVisibility.cpp,修改MarkRelevant():
MarkRelevant
这步是收集Cache过的MeshDrawCommand

修改SceneVisibility.cpp中的ComputeDynamicMeshRelevance:
ComputeDynamicMeshRelevance
这步是收集Dynamic的MeshDrawCommand

创建RenderTarget

打开SceneRenderTargets.h,添加成员MyTestSceneDepthZ:
MyTestSceneDepthZ

然后创建RT,修改AllocateDeferredShadingPathRenderTargets
Allocate MyTestSceneDepthZ

释放时机,修改ReleaseSceneColor,ReleaseAllTargets
Release MyTestSceneDepthZ

以及复制构造函数:
MyTestSceneDepthZ Copy Constructor

打开SceneRenderTargets.h,在FSceneRenderTargets中添加两个函数:

  • BeginRenderingMyTestPass
  • FinishRenderingMyTestPass
    Function Declaration
    MyTestPass Bind RT

上面定义了一个临时变量FRHIRenderPassInfo RPInfo,这个类型可以设置ColorRT,DepthRT
然后通过RHICmdList.BeginRenderPass(RPInfo, TEXT("PassName"))来绑定RT

渲染

FDeferredShadingSceneRenderer中增加函数RenderMyTestPass,并在MyTestPassRendering.cpp中实现
RenderMyTestPass

SetupMyTestPassView如下:
SetupMyTestPassView

然后找个地方调用一下,比如放在RenderPrePass之后
Invoke RenderMyTestPass

到这一步只能靠renderdoc这种剖析工具查看渲染

Buffer Visualization

修改DeferredShadingCommon.ushFGbufferData
FGBufferData

修改DeferredShadingCommon.ushDecodeGBufferData
DecodeGBufferData

这样就把自定义的值放到FGBufferData里了

修改SceneRenderTargetParameters.h
SceneTextureStruct
该结构定义了一个变量SceneTexturesStruct,可见SceneRenderTargets.cpp

修改SceneRenderTargetsSetupSceneTetureUniformParameters
SetupSceneTextureUniformParameters

RenderMyTestPass中我们会调用SetupSceneTetureUniformParameters
MyTestSceneDepthZ绑定到SceneTexturesStruct.MyTestTexture
因此,在Shader代码中可通过SceneTexturesStruct.MyTestTexture访问

修改DeferredShadingCommon.ushGetGBufferDataUint
GetGBufferDataUint

修改DeferredShadingCommon.ushGetGBufferData
GetGBufferData

修改SceneViewFamilyBlackboard.h
Modify FSceneViewFamilyBlackboard

修改SceneViewFamilyBlackboard.cpp
Register MyTestSceneDepthZ

修改SceneViewFamilyBlackboard.ush
Modify SceneViewFamilyBlackboard.ush

修改BaseEngine.ini
Add Config

修改`MaterialExpressionSceneTexture.h

Add MaterialEditor InputSlot

接下来新建一个材质,名为MyTestDepth,放在Engine/Content/BufferVisualization目录下:
MyTestDepth

效果如下:
BufferVisualization

Reference

perlin-noise unreal setup.bat speed

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×