unity中bloom的低消耗实现

问题:

第一种方式:直接用bloompro放在渲染英雄的camera上来处理,但后来发现OnRenderImage方法是会对整个屏幕渲染结束后做处理,然后他会把背景也截出来做处理。但无论是有背景还是没背景,就算只是渲染空的内容也会卡顿,在低端机会更明显。估计跟ImageEffect的实现相关。特别在android上,基本只有3帧左右。

原因分析:

http://www.gamethk.com/news/detail/4751/6.html

这篇文章有相关原因的分析,

在glReadPixels的最后一个参数不为空,则表示数据从显存传输到系统内存,逆向传输,这是非常缓慢的过程,并且是阻塞模式。也许是因为Unity内部实现的原因或是硬件支持不够。

ios传输快,android慢的原因是:

苹果是arm芯片基础改造的,ARM体系通过CCI总线保证core之间的数据一致性,对于Mali GPU,则提供一个ACP(Accelerator Coherency Port)接口,实现了GPU观测CPU的Snoop path. 从G71开始,新的Bifrost架构的GPU全部实现了ACE总线,这也就意味着ARM已经实现了CPU-GPU之间双向的数据一致性。

iOS设备中只有一块物理内存硬件,主存地址和显存地址在同一块虚存地址空间中,虚存最终的确只有一份纹理内容位于IOKit区域中,而且该纹理内容的确就是被GPU所用的纹理。
在纹理上传过程中,Unity先在堆内存申请缓存,然后将纹理文件内容读进缓存里,然后调用图形API将该该纹理内容数据拷贝到IOKit虚存中,供GPU访问。拷贝完成后缓存视乎情况从堆内存释放。

对于大部分的android:

对于Integrated GPU而言,其local memory和CPU共享相同的存储实体,是为UMA结构。CPU有能力直接访问GPU全部的local memory。GPU通过GART能够访问到CPU地址空间的数据(反向获取数据会慢),并且可以通过Snoop属性,来做到单向一致性(GPU观测CPU数据)。Snoop并非毫无代价,其实现颇具复杂度。

第二种方式:

把英雄的后处理渲染在一个建好的rt上,让英雄实时渲染在rt上。然后在渲染背景的camera上把rt上屏。因为英雄的camera选择的清屏方式ClearFlags选择的是纯色清屏(Solid Color)并且选择的是黑色透明(选择黑色透明是考虑不影响后面的shader的计算),所以整个英雄的屏幕渲染上rt上是不会有背景的。 在渲染背景的camera下建立的quad上放入材质,放入我们的rt就可以实时显示英雄了。

缺点:

因为渲染的是rt,是一个texture,所以精度会有损失。美术觉得效果不理想,rt精度上如果选择1024x1024这种类型,是会在屏幕上渲染出一个正方形的英雄相当于两边的屏幕的英雄内容会被截掉。会影响显示。这样做后在英雄身上的一些粒子特效会出现问题,因为颜色和透明度的叠加,包括对灰度图的叠加,会导致一些图片显示的问题。(特别是一些粒子加了shader却没有图片的,直接就黑了)

第三种方法:

一个camera渲染英雄(并且是不做任何其他处理),另一个camera 跟前一个camera的depth保持一致,然后culingmask选择需要渲染的对象,比如背景和英雄。然后targettexture选择我们创建的rt。这样做就把第二个指向英雄的camera的内容渲染到rt上了。

并且在该camera上放入我们需要做bloom的脚本。

在做bloom的shader上让他只输出我们需要的bloom效果,不输入原型 。

然后创建另一个camera,这个camera的渲染depth要高于普通渲染英雄的camera。(因为要叠加到英雄上)

然后在下面放入quad,然后放入材质让他 选择一个shader

最终上屏的就是bloom效果。

可以看到我只输出了bloom那部分,没有输出原图。

这样就可以把原本的英雄camera和只显示bloom效果的camera叠加起来显示。最终显示出效果。

优点:

(1)不影响原本的英雄的精度的显示,美术满意

(2)bloom的模糊和rt的精度都可以任意调节,不影响原本英雄。

缺点:

(1)多个camera

(2)drawcall会多点

当然你也可以多一个图的一个通道来决定哪些部位要进行高光。因为一般金属感的地方才需要高光,而不是全身的颜色决定。

这种方式在移动端的效率会很高,推荐使用

消耗比较:

å¾ç

å¾ç

猜你喜欢

转载自blog.csdn.net/llsansun/article/details/83744848
今日推荐