目录
渲染管线的选择
1.透明物体容易出现渲染错误
2.使用多SetPass的情况会打断合批,使得Draw居高不下。
3.Blit操作不能自定义开启关闭
URP的默认渲染管线
在《黑暗之潮》的一些应用
1.平面阴影
2.沙盘地图描边
使用URP自定义组件可以实现的一些效果
URP的性能优势
SRP Batcher与传统合批
QA环节一些不容忽视的补充
渲染管线的选择
林大是这样描述道:“选择URP的理由,使用移动平台的PBR渲染管线,非PBR的东西也可以拿来渲染。非侵入修改可实现管线自定义,可实现功能定制。全部C#源码,渲染过程可见,可控。源码结构清晰,便于扩展和自定义。
比内置管线性能更好。”
这场分享看下来,基于Shader变种的SPR Batcher,控制Blit操作的使用时机,使用单Pass ColorTexture替代GrabPass,优化LoadStore操作…… 优化后的带宽和使用效率,可以甩内置管线几条街,嗯…… 说着说着就激动了。
1.透明物体容易出现渲染错误
透明物体容易出现渲染错误,下面两个图是半透明焰光的示范,可以明显的观察到上图的焰光并没有覆盖到地表上。
如下图,图左为错误结果,图右为正常结果。
在内置管线中的解决方案大都是修改对应Shader的Renderqueue来规避,这种情况下一般是需要引入新的Shade,即使是新增了Shader也同样是会面对同样的问题,随着项目的体量越来越大,Shader越来越多,这种问题的处理,应该会很棘手。或者直接就抛弃掉这种效果,所以说整体效率的内置管线在处理这种业务逻辑的时候,多少有些差强人意。
2.使用多Pass的情况会打断合批,使得Draw居高不下。
内置管线的一些效果只能通过Shader Pass实现,如角色增加描边效果,一般都会需要增加额外的Pass去处理。
如下图,虽然Object1和Object2虽然使用了相同的Pass,但两者的Pass操作不能进行合批。
绘制2个物体,每个物体相同的3个Pass,需要6次SetPass操作(Object1,Pass1也是需要一次SetPass),Draw Call的次数也水涨船高,且切换渲染状态是一个高消耗的操作,在这种多Pass物体存在比较多的情况下,势必会造成DrawCall和性能消耗居高不下的局面。
通过可编程渲染管线,可以针对解决上述问题做出一个很好的优化处理。
一次性将使用相同Pass的物体渲染绘制,可以很好的降低切换渲染状态/SetPass操作的数量,在这里我的理解是通过牺牲带宽或者牺牲内存的方式,来换取一个比较少的SetPass操作,以达到一个好的运行状态。
在上图中绘制3个物体,2个Pass,需要执行2次SetPass,相比于内置管线的渲染流程,这种渲染流程下的Draw Call、SetPass、合批操作要高效的多。
3.Blit操作不能自定义开启关闭
移动端来说,CPU与GPU带宽有限,Blit操作会带来很多的带宽开销,所以,合理管控Blit操作是一个很好的的优化方案。
内置管线为了兼容性,会在渲染中添加Blit操作,且无法关闭。全屏Blit操作对于移动平台来说开销较大,而在确定的渲染管线中,可以明确知道Blit是否必须,不少情况可以省去。
URP的默认渲染管线
先渲染主光源光影效果,再渲染副光源光影效果,把场景所有物体的深度渲染到一张RT上,绘制所有不透明物体,绘制天空盒,把当前的渲染结果会知道RT上做后效的时候使用,所有透明物体的绘制,全屏后效处理,绘制UI,把所有的绘制结果进行Blit操作,然后写到屏幕缓冲区当中。
在《黑暗之潮》的一些应用
1.平面阴影
不需要额外渲染ShadowMap,也不需要因为使用ShadowMap,也不需要额外的进行采样操作,所以对性能的提升还是比较强的。
2.沙盘地图描边
一般的情况下,描边的实现方案一般是法线往外扩(传统描边),但这种对方法在边界比较复杂的情况下,阴影会很难处理,或者说处理的差强人意。
黑暗之潮的做法是,先将地块用纯色绘制出来,然后降采样降低分辨率,接着进行模糊操作,然后再升采样升分辨率,最后地图区域设置透明度。
这样做的好处就是利用较小的带宽消耗,来达到一个比较好的阴影、透明度的过渡。
使用URP自定义组件可以实现的一些效果
1.单独针对某一物体设置FOV参数(重载摄像机的变换矩阵)
2.透贴(透明)效果可以通过延后其渲染时机,一般情况下是放在DepthPrepass之前。
3.在任意时间点执行渲染操作。
4.手动调用CommandBuffer底层接口
5.复用已经实现的各种Pass
6.省掉FinalBlitPass操作
后效不可避免的会进行全屏Blit操作,默认情况下会在渲染UI后,使用FinalBlit Pass,将结果复制到FrameBuffer,可以将上述的两个过程进行合并,只进行一次Blit操作。在《黑暗之潮》中给定的方案是利用后效的Blit操作直接讲结果复制到FrameBuffer,并直接在FrameBuffer上进行UI绘制,在省一次Blit基础上还能实现3D场景与UI使用不用的分辨率渲染。
7.可以控制切换RT时RenderBuffer的LoadStore操作。
在内置管线中,渲染的计算结果会先写到片上内存,然后GPU真正绘制的时候,再从片上内存加载到帧缓冲区。这种情况下,有的RT只是为了缓存信息,并不参与GPU的绘制,所以在这种情况下,LoadStore就变得不必要。通过控制LoadStore操作,可以有效地缓解GPU和CPU的带宽压力。
URP的性能优势
单Pass实时光照
切换RT可自定义LoadStore操作,节省带宽。
去掉不必要的Blit操作。
SRP Batcher。
单Pass ColorTexture替代GrabPass。GrabPass真·全屏抓取,不会降分辨率的抓取。还有一个问题就是,单次渲染帧中GrabPass不知道会被调用多少次,所以会不可避免的造成一定程度上的性能浪费,尤其是对于移动平台。
SRP Batcher与传统合批
Dynamic Batching要求苛刻,且CPU开销大。
Statci Batching对动态物体无效,且内存占用巨大,对LOD不友好。
GPU Instancing对Mesh和Material均一直的情况下生效。
Draw Call开销最大的就是SetPassCall。
通过ConstantBuffer保存PerMaterial/perDraw数据,实现Shader变种级别的合并。
林大有提到中配数据中的32w三角面400dc,已经是内置管线项目的高端机型所达到的效果了,听到这里我都惊呆了,这个SRP Batcher好强啊。
QA环节一些不容忽视的补充
StaticBatching 选择面比较小的,同一物体重复次数不多的情况下,最通用的就是SRPBatching
关于变体爆炸,通过变种收集自己用的变种,告诉Unity自己使用的变种,一般的项目最多有200-300个变种。
描边一般是在顶点着色器里面使用法线外扩的方式。
ASE代替ShaderGrap是可以的
SPRBatch切换状态也会带来影响,只是对DrawCall平均调用做了一个优化。
国内大部分机型都支持OpenGl ES 3.0 所以对于ConstantBuffer指令的使用,在国内基本上是没问题的。
URP效率比内置高的情况,一般是两种情况,一种是管线设置不合理,一种就是UnityBug。