基础概念
- UPrimitiveComponent: 可用于渲染或者物理交互的基类,游戏线程拥有其所有变量和状态
- FPrimitiveSceneProxy:
UPrimitiveComponent
的渲染线程版本 - FPrimitiveSceneInfo:
UPrimitiveComponent
在渲染器的内部状态,与FPrimitiveSceneProxy
一一映射FMeshDrawCommand
保存了在一个特定Pass
下绘制一个Mesh
所需要知道的所有信息
- ShaderBindings: 记录了
Shader
各个阶段绑定的参数集 - VertexStreams: 记录了
VertexBuffer
相关信息 - IndexBuffer: 记录了
IndexBuffer
相关信息 - CachedPipelineId: 用于索引GraphicsPipelineState
FMeshBatch
FPrimitiveSceneProxy
生成FMeshBatch
有两个途径
- Cache
- Dynamic
具体由哪个途径生成FMeshBatch
通过FPrimitiveSceneProxy::GetViewRelevance
决定
StaticMesh
来说就是Cache
的方式,在Proxy
添加到场景时调用DrawStaticElements
生成的FMeshBatch
被保存在FPrimitiveSceneInfo
的StaticMeshes
中
每帧进行重用直到Proxy
从场景中移除
Dynamic
的方式将会每帧重建FMeshBatch
,适用于粒子这种
通过调用GetDynamicElements
来生成
MeshProcessor
- Select Shader
- Collect Pass Bindings, vertex Factory bindings, material bindings
- Builds One Or More FMeshDrawCommands From a FMeshBatch
- AddMeshBatch: 必须重载,根据
FMeshBatch
生成FMeshDrawCommand
每个Pass都需要实现一个MeshPassProcessor
,以DepthRendering
为例:
通过调用BuildMeshDrawCommands
生成MeshDrawCommand
MeshDrawCommand如何产生?
DynamicMesh
的MeshDrawCommand
需要每帧产生
目前只有FLocalVertexFactoy
,即(UStaticComponent
)可以被Cached
因为其它的VertexFactory
需要依赖View
来设置Shader Binding
- Static Relevance
FBatchingSPDI::DrawMesh
如下:
可见对于StaticMesh
而言,MeshBatch
可以提前CacheMeshDrawCommand
需要根据SupportsCachingMeshDrawCommands
确定是否能Cache
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
的过程:
CacheMeshDrawCommands
的实现如下:
会按PassType
存储在Scene::CacheDrawLists[PassType]
中
接下来看下如何收集MeshDrawCommand
MeshDrawCommand
的收集就在ComputeViewVisibility
中完成
- Static MeshDrawCommand的收集如下:
AddCommandsForMesh
中会根据bSupportsCachingMeshDrawCommands
来决定是否已经Cache
好了
需要生成MeshDrawCommand
的StaticMeshBatch
记录在DynamicBuildRequests
中
- Dynamic MeshDrawCommand收集
DynamicMesh的MeshBatch
需要每帧收集:
然后通过SetupMeshPass
将StaticMeshBatch
和DynamicMeshBatch
转换为MeshDrawCommand
因此生成MeshDrawCommand
的大致流程如下:
1 | void ComputeViewVisibility(...) |
最终这些MeshDrawCommand
被存储于FViewInfo::ParallelMeshDrawCommandPasses[PassType]
中
MeshDrawCommand如何使用
最终通过调用以下语句触发DrawCall
1 | View.ParallelMeshDrawCommandPasses[MeshPassType].DispatchDraw(nullptr, RHICmdList); |
custom mesh pass
以复制一个简化版的DepthPass
为例
添加Pass枚举
打开文件MeshPassProcessor.h,修改EMeshPass
结构体,如下:
修改GetMeshPassName
,如下:
新建文件
新增MyTestPassRendering.h
,MyTestPassRendering.cpp
存至Engine/Source/Runtime/Renderer/Private
目录
添加Shader
新建文件MyTestVertexShader.usf
,存于Engine\Shaders\Private
目录
(照抄的PositionOnlyDepthVertexShader.usf)
修改MyTestPassRendering.h
新增类FMyTestVS
,如下:
在MyTestPassRendering.cpp
中通过IMPLEMENT_MATERIAL_SHADER_TYPE
实现材质shaderIMPLEMENT_SHADERPIPELINE_TYPE_VS
就是构建一个只有VS
的FShaderPipelineType
对象
添加MeshPassProcessor
AddMeshBatch
是必须要重载的函数
然后需要把这个类型的MeshPassProcessor
通过以下方式注册到FPassProcessorManager
中
这里是把一个函数指针通过FRegisterPassProcessorCreateFunction
的构造函数,记录到FPassProcessorManager
中
SetupMyTestPassState
负责设置渲染状态,这里实现如下:
构造函数实现如下:
构造函数中调用了三个SetUniformBuffer
- SetViewUniformBuffer: ViewUniformBuffer中包含各种变换矩阵以及计算用的贴图数据等等
- SetInstancedViewUniformBuffer: 和ViewUniformBuffer差不多,用于
Instance Stereo
- SetPassUniformBuffer: 包含SceneTexture,如GBuffer,SceneDepthTexture等,便于
MaterialGraph
和GlobalShader
使用
这里我们自己定义一个MyTestPassUniformBuffer
:
初始化MyTestPassUniformBuffer
:
接下来看AddMeshBatch
:
做了个Pass Filter
这个实现里也没啥东西…调用了Process
函数,直接看Process
这个函数就是先做了下Shader Filter
,然后生成MeshDrawCommand
调用BuildMeshDrawCommands
时,传入了一个参数PassDrawRenderState
在构造函数中对PassDrawRenderState
设置了三个UniformBuffer
因此生成的MeshDrawCommand
都是绑定了这三个UniformBuffer
的
GetMyTestPassShaders
实现Shader Filter
:
收集MeshDrawCommand
之前讲过了MeshDrawCommand
的三个来源,那么生成了MeshDrawCommand
之后,还需要确定哪些需要被这个Pass调用
打开SceneVisibility.cpp
,修改MarkRelevant()
:
这步是收集Cache过的MeshDrawCommand
修改SceneVisibility.cpp
中的ComputeDynamicMeshRelevance
:
这步是收集Dynamic的MeshDrawCommand
创建RenderTarget
打开SceneRenderTargets.h
,添加成员MyTestSceneDepthZ
:
然后创建RT,修改AllocateDeferredShadingPathRenderTargets
释放时机,修改ReleaseSceneColor
,ReleaseAllTargets
以及复制构造函数:
打开SceneRenderTargets.h
,在FSceneRenderTargets
中添加两个函数:
- BeginRenderingMyTestPass
- FinishRenderingMyTestPass
上面定义了一个临时变量FRHIRenderPassInfo RPInfo
,这个类型可以设置ColorRT
,DepthRT
然后通过RHICmdList.BeginRenderPass(RPInfo, TEXT("PassName"))
来绑定RT
渲染
在FDeferredShadingSceneRenderer
中增加函数RenderMyTestPass
,并在MyTestPassRendering.cpp
中实现
SetupMyTestPassView
如下:
然后找个地方调用一下,比如放在RenderPrePass
之后
到这一步只能靠renderdoc这种剖析工具查看渲染
Buffer Visualization
修改DeferredShadingCommon.ush
的FGbufferData
修改DeferredShadingCommon.ush
的DecodeGBufferData
这样就把自定义的值放到FGBufferData
里了
修改SceneRenderTargetParameters.h
该结构定义了一个变量SceneTexturesStruct
,可见SceneRenderTargets.cpp
修改SceneRenderTargets
的SetupSceneTetureUniformParameters
在RenderMyTestPass
中我们会调用SetupSceneTetureUniformParameters
将MyTestSceneDepthZ
绑定到SceneTexturesStruct.MyTestTexture
上
因此,在Shader
代码中可通过SceneTexturesStruct.MyTestTexture
访问
修改DeferredShadingCommon.ush
的GetGBufferDataUint
修改DeferredShadingCommon.ush
的GetGBufferData
修改SceneViewFamilyBlackboard.h
修改SceneViewFamilyBlackboard.cpp
修改SceneViewFamilyBlackboard.ush
修改BaseEngine.ini
修改`MaterialExpressionSceneTexture.h
接下来新建一个材质,名为MyTestDepth
,放在Engine/Content/BufferVisualization
目录下:
效果如下:
Comments