目标:尽可能少改动管线,尽可能减少增加的性能消耗
那么先快速过一下UE的这几个Bloom相关的Pass做了啥
BloomSetup:
输入SceneColor,输出1/4分辨率BloomSetupRT
传入后处理盒子上设置的BloomThreshold
BloomDown:
获取BloomSetup的BloomRT,每次/2尺寸,创建4个依次缩小的降采样
降采样做了UV Circle偏移模糊
BloomUp:
获取BloomDown的4个RT,逐次*2尺寸,一共3次放大
升采样也做了UV Circle偏移模糊
传入后处理盒子上设置的BloomIntensity
升采样时返回的颜色乘上了BloomIntensity,最终输出RT尺寸为BloomSetup输出的1/2
SunMerge:
输出RT尺寸为SceneColor/4
输入BloomSetup结果和BloomUp结果,及Vignette和DirtMask影响
传入后处理盒子上设置的Bloom1Tint/BloomIntensity影响传入的BloomUp结果
CombineLUTs:
之前CLUT的文章讲过,这里不再提。不影响本文自定义Bloom强度控制
Tonemap:
输入SceneColor、SunMerge结果、CombineLUT
ES31流程没用到ColorScale1,所以mobile流程可认为Tone流程没有受Setting.BloomIntensity影响
要逐物体/逐像素控制Bloom强度,无外乎就是要给Bloom计算流程传入一个存储了想要的Bloom强度的buffer。Deferred的话可以用GBuffer来存,Forward的话就得自己添加额外的MeshPass来做,而为了性能与整体改动考量,这里将目光放在CustomStencil上。
并且在写之前,咱们就能确认这样改是一定可行的。因为连连看中的后处理材质中可获取的到CustomStencil,说明后处理阶段已经输入了我们想要的SceneTextures数据了,事实也如此,直接用即可。
OK,那么下一步就是考虑做在哪一步中了。
能做在SunMerge中么,显然不行,扩不出来。
那么考虑做在BloomUp中么,由于DownSample尺寸降的很严重,叠回来完全对不上。
那么就只能考虑做在BloomSetup中了
即BloomSetup输入CustomStencil做为mask做强度影响,后续输出的结果再去走降/升采样,也能返回平滑的bloom结果。
具体实现中有几个需要注意的点:
1、CustomDepth/Stencil Pass的绘制是有条件的,默认情况下是场景中存在使用customStencil信息的半透明/后处理材质时才会走,所以要和我们这套方案挂钩需要修改。
在UsesCustomDepthStencilLookup()中,可以看到根据半透/后处理中是否用到来做的判断
2、后处理盒子上加参,这里代码就不展开了,照着已有的加就行
3、添加材质宏/变体相关对应后处理盒子上的开关控制,按着已有的加就行。但增加数量对于已有较多宏的变体数膨胀影响会比较大,比如后续会提到的Tonemap中的修改,所以还是要注意控制
4、CustomStencil用法可参照材质节点写法
以上修改后,如上文所提,SunMerge中除开外还有部分BloomSetup结果计算影响,所以会有如下图的边缘问题。
要在不修改原有Bloom算法的前提下消掉这部分的话,解决方案是BloomSetup MRT额外输出一个原本不受CustomStencil影响的buffer结果给到SunMerge用
具体实现中需注意的点:
1、usf中加宏时注意写法,错误的写法可能不会触发shader编译报错
2、添加时注意RDG规范
剖析虚幻渲染体系(11)- RDG - 0向往0 - 博客园
到这,这套流程的效果就没问题了。BloomSetup多一个CustomStencil输入,多一个1/4尺寸BloomOrigin输出。然后SunMerge的BloomSetup输入换成BloomOrigin的输入。
主要还是耗在带宽,那么后续可继续做优化的地方是CustomStencil的buffer大小,在用于该自定义bloom的时候,尺寸降一下。可以看到UE默认走Mobile流程的时候就已经1/2降了,这里可以在其基础上降的更多。
当然除了RT尺寸修改,Viewport也要对应上。
以上即是性能友好/逐物体整体bloom强度可控的方案。当然customStencil限制了这个只能整个物体层面调整强度而非像素级,如之前所说要么Deferred的GBuffer,要么Forward自己额外增加MeshPass,都能做到像素级的控制。但是他们所提供的buffer进到bloom流程后,要做的处理和该方案是一样的。
当然这套方案最大的问题还是没有深度测试,所以对于有模型遮挡的情况不友好。于Forward而言就只有加meshPass去做,该方案可以让有遮挡的模型都写stencil,在物件不多的情况下也OK。
延伸:
如想要内外发光强度可分别控制
在以上基础上,再用CustomStencil控制物体内部的Bloom强度,即在Tonemap pass中传入customStencil做剔除。
下图在调强整体+内部强度0的情况下,即使是full size的CustomStencil,锯齿非常严重。这是由于CustomStencil本身没有任何的AA。
方案是启用内部剔除功能时,CustomStencil必须full size,然后再tonemap中加上模糊计算。
这里需要注意的是,如上一篇文章所提,UE的SceneTexture buffer大小和其中实际绘制区域大小是基于屏幕适配的,不同的显示器DPI缩放设置,相机视口裁切都会有影响。
所以我们处理stencil UV时如果按下面这样写,视口缩放时UV的偏移就不是固定屏幕比例的,所以如果要保证效果,每个偏移后的UV的要做ScaleBias处理,当然这会增加计算量。
Main中参数加宏的写法也是可以的,注意代码宏传参
结果: