虚幻4渲染编程(Shader篇)【第十二卷:MeshDrawPipline】

本文详细介绍了虚幻4引擎在4.22版本中引入的MeshDrawPipeline,替代了原有的DrawList和DrawPolicy。主要内容包括FPrimitiveSceneProxy、InitViews、FMeshPassProcessor、FMeshDrawCommand和ShaderBindings的使用,以及GPUScene、CustomDepthPass和ParallelMeshDrawCommandPasses的工作流程。通过对渲染过程中的主要类和步骤的解析,展示了新渲染管线的灵活性和紧凑性。

我的专栏目录:

小IVan:专题概述及目录

简介:

Unreal engine的渲染模块是越来越复杂,最开始只有延迟管线,随后加入了mobile,现在又加了很多实时光线追踪的东西,各种DX12,VK新的RHI也纷纷加入。

虚幻4的4.22版本对渲染模块进行了较大重构,废弃了之前的DrawList&&Draw Policy,取而代之的是MeshDrawPipline。与之前的DrawPolicy相比,MeshDraw更为简洁,灵活。新旧Pipline对比图如下:

4.22

v2-4741d01b86c2a0838f69f62994217103_b.jpg

<=4.22

v2-6842757d6cdbbe664bf881c9cc7e3eb0_b.jpg

我之前有写老的drawingpolicy的文章:小IVan:虚幻4渲染编程(Shader篇)【第十卷:绘制策略】。如有错误还请巨佬们斧正。

因为涉及到的细枝末节的东西很多,所以这篇文章我会在一个月内持续更新修改。


首先先过一下渲染过程中主要的类和步骤。

【1】FPrimitiveSceneProxy

这个类的功能没有什么改变,依然在通过GetDynamicMeshElements等函数从每个primitivecomponent中收集渲染资源

v2-080a2aeb3ed579f31529be4e4e34c893_b.jpg

【2】InitViews

FPrimitiveComponent的GetDynamicElements这个收集操作通过渲染器的InitViews在可见性剔除之后发起

v2-d6dabdcae9c1cf6622c7c99ba7db08ce_b.jpg
v2-189412e0445d583e8feead60ea730d3f_b.png

FMeshElementCollector会把场景里的渲染数据收集起来

v2-cd4cee4741e940730114974215da5e0a_b.jpg

最后会调用SetupMeshPass来创建MeshPassProcessor

v2-dc6ab7b11df504f6b43baba7f8826b62_b.png

【3】FMeshPassProcessor

v2-3b49a186b5f6ec56e0c6b1bea7547ded_b.jpg

FMeshPassProcessor有两个作用,第一个是选择绘制时所使用的shader,第二个是搜集这个pass绑定的顶点工厂,材质等。可以从一个FMeshBatch创建多个FMeshDrawCommands

v2-abdcff6aa0929355b1cba70dfa5fcec1_b.jpg

搜索引擎就会发现,每个pass都会有一个对应的FMeshPassProcessor。FMeshPassProcessor是所有pass的FXXXMeshPassProcessor基类。

在一个pass在初始化的时候,会往MeshPassProcessor里填充BatchMesh渲染单元。

以depthpass为例,DepthPass的PassProcessor创建代码如下:

v2-a3d4bd08a60bdbc4d62faf3731134541_b.jpg

在渲染管线重会调用Processor的AddBatchMesh把渲染资源加进去

v2-8ffbe4a6821ddd27a88f6f0065d392ed_b.png
v2-19232eef47238492f433fe500f17c2f4_b.png

在AddMeshBatch函数中会进行Passfilter和SelectShader,分别对应下图的1,2部分。

v2-4bbf753c277566e76c536c728a84b441_b.jpg

shader等设置好以后会通过BuildMeshDrawCommands创建command然后通过FinalizeCommand完成command的添加

v2-adc9709fa091950ba56f148fe3d5b432_b.jpg
v2-3bc2e944f7313912849a60db6803ef64_b.jpg

【4】FMeshDrawCommand

v2-0c7e20b858b3de3301a485f256a49f32_b.jpg

代码注释已经解释得很清楚了。一个MeshDrawCommand描述了一次绘制所需要的所有资源。FMeshDrawCommand类中主要负责管理了三类资源:(1)ShaderBindings,记录了这次Command各个阶段绑定的参数集。(2)VertexStreams 记录了VertexBuffer的信息。IndexBuffer记录了IndexBuffer的相关纤细。CachedPipeline用于索引GraphicPipelineState。

【5】ShaderBindings

v2-b24e4393235cf5c91297d3a051b729e4_b.jpg

可以从新老方式中看到,新的方式是通过收集参数而不是直接设置RHICmdList来设置Paramerters了。ShaderBinding是作为MeshDrawCommand的组成部分之一,在这里面需要绑定管线所需要的所有参数集。

v2-4f60c34422624d137f243d4052b720ed_b.jpg

【GPUScene】

v2-1b3a12c20d2ad2417a785cc4f998cd23_b.jpg

GPUScene有一个PrimitiveBuffer,它会跟踪场景Primitive的添加,移除操作,会在GPU端有一个镜像场景。虚幻使用一个Computeshader来更新primitivebuffer给下一帧使用。Primitivedatabuffer要求一个primitive的所有shader只能有一个primitiveID

v2-cb0e8f3d3e65632c29c0d458180fbe3a_b.jpg

这个primitiveID只有LocalVertexFactory才有

v2-e71eeb565b7bcc312fb12336361528c3_b.jpg
v2-33cc1b62c9f081fa1773c1bcf77000e0_b.png

这里用CustomDepthPass作为一个例子看一下静态物体的绘制流程。

【CustomDepthPass】

首先准备好个空场景然后拖一个盒子进去

v2-681e8a20f22bd4567b5bec51b6dcb6c5_b.jpg

这时会调用AddPrimitive的逻辑

v2-04307e21e9f52dbfb84ac922fea5acf9_b.jpg

会创建场景代理,各种渲染数据,矩阵包围盒等。并且会更新GPUScene

v2-0d406cd0e19f6113cd53ca87135e3986_b.jpg

并且会CacheMeshDrawCommands

v2-56818a180c431e306b1d6f2714bdf78c_b.png

然后创建PassProcessor然后AddMeshBatch,这个Processor会创建MeshDrawCommands

v2-e1a576499b75b51dfd34afa1bf7cb4ad_b.jpg

完成commands的创建后销毁这个Processor。这便完成了对Commands的添加。

v2-511e1eb2ab9196e90c2c235a4786e526_b.jpg

要绘制时直接调用这些commands即可。这些commands会去调用RHI,RHI层会去调用各大图形接口。

v2-07d4d4c8f33911066bbf56016ca21b5a_b.png

最终就完成了绘制。

这里会发现,我们添加的时候是往TSet<FMeshDrawCommandStateBucket, MeshDrawCommandKeyFuncs> CachedMeshDrawCommandStateBuckets;里添加,真正使用的时候不会直接使用这个数据,而是经过剔除和各种处理后把一帧画面里所需要的数据放到ParallelMeshDrawCommandPasses里。


DrawCall的发起者ParallelMeshDrawCommandPasses

【ParallelMeshDrawCommandPasses】

v2-fe2b7bd95c4cd3ba2dad4660c29b653b_b.jpg

DispatchPassSetup是一个关键函数,它负责为每个pass管理MeshDrawCommands,为绘制每个pass做准备。

可以看到我们有很多种pass类型。一个pass里有很多MeshCommand

v2-857a64092c73e4230104611eff74af34_b.jpg

在InitView的时候,我们把我们的command按照不同的pass类型分别塞倒ParallelMeshDrawCommandPasses里,这样就可以方便地统一调用command来完成各个pass的绘制了。

v2-3787a8f6fdf436b5da6cf2b486cdf66f_b.jpg

PrePass:

v2-43778ba03642bf6dd9e8824439f69660_b.png

DepthPass:

v2-00896070b0764ab7dc4f73915597432c_b.png

CustomDepthPass:

v2-d11c0c37b01246b35e6987cfb48c9e84_b.png

BasePass:

v2-d1d67f7a603d45151991197ad19c987b_b.png

还有很多我这里就不例举了。

我在视口中加入一个球一个盒子,他们的vertex buffer不一样材质一样,所以无法被加入到一个batchmesh中,所以可以理论上customdepthpass会有两次绘制

v2-cce48ca03b263974a80630a131d2e288_b.jpg
v2-4d98f33504f1e447ccca20aa432995f2_b.jpg

我在视口中再拖了一个盒子,现在视口中有两个盒子一个球体。BasePass只画了两次,因为两个盒子被DrawInstance了,然而CustomDepthPass却画了三次:

v2-43af65d28c7ccd21bcef0244947a57f3_b.jpg
v2-047dea6113aca7e691d55118406d1ef9_b.jpg

对于美术制作层面来说,如果是做端游,我们可以把模型切成碎块然后搭建场景不需要batch,因为虚幻的basepass已经dynamicbatch了的(前提是这些碎片的vertexbuffer和材质是一样的)比如搭建一个木头结构的房子其实也就用几个木头模型罢了。这样切模型和非在DCC中batch模型的区别在于,其它有些pass不会以dynamicbatch规则去合并这些dc。


综上虚幻的渲染模块经过这次重构后,灵活性更强,逻辑更加紧凑。

v2-520e526b6d25884e480f0d1e6695bb66_b.jpg

Enjoy it.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值