URP下的OffScreen Particle Render

URP下的OffScreen Particle Render

【博物纳新】专栏是UWA旨在为开发者推荐新颖、易用、有趣的开源项目,帮助大家在项目研发之余发现世界上的热门项目、前沿技术或者令人惊叹的视觉效果,并探索将其应用到自己项目的可行性。很多时候,我们并不知道自己想要什么,直到某一天我们遇到了它。


随着越来越多的项目使用URP管线,以及更多的项目的特效压力越来越大,因此本篇文章基于GitHub上的一个项目将其从Builtin管线下移植到URP修改版,给大家提供了URP管线下降低特效GPU压力的做法,以供大家参考。

GitHub项目:OffScreenParticleRendering
https://github.com/slipster216/OffScreenParticleRendering

这个库做的工作是将粒子系统或者半透明的渲染对象单独渲染到RenderTexture中,然后再将RenderTexture与不透明的渲染进行混合。单独渲染到RenderTexture的好处是可以通过降低RenderTexture的分辨率从而减少GPU的渲染压力。


Builtin管线下最终融合效果


Builtin管线下RenderTexture的渲染效果

为了能将此功能移植到URP下,我们先说明一下原始项目中Builtin管线下功能实现的关键点,具体细节在原GitHub项目的说明文档中有说明,主要步骤如下图1,2,3,4。

1、主相机中渲染不透明对象,且因为渲染半透明时需要做深度比较,因此主相机的depthTextureMode需要设置为DepthTextureMode.Depth。

2、在OnRenderImage中将_CameraDepthTexture进行DownSample。

3、在OnRenderImage中从主相机复制一个相机,此相机渲染到RenderTexture,且RenderTexture可以调整分辨率,在此相机中单独渲染粒子系统和其他半透明对象。调用拷贝的离线渲染相机的Camera.Render,这样可以保证离线相机的渲染顺序是在主相机之后渲染的,否则设置了RenderTarget的相机会优先比没有设置RenderTarget的相机先渲染。半透明的对象的Shader需要做特殊处理,需要将半透明对象和渲染场景生成的深度进行比较,比不透明对象离相机更近的像素才会被“绘制”到RenderTexture上。

PS:在原始项目中渲染Particle的Shader中少了“COMPUTE_EYEDEPTH(o.projPos.z);”,所以效果看上去不太对,在vert函数中添加上这句就OK了。

4、在OnRenderImage的最后通过OffScreenParticle_Upsample Shader将Particle等半透明的RenderTexture融合到不透明场景中。此Shader中将RenderTexture的颜色通过Blend SrcAlpha OneMinusSrcAlpha的方式进行原色混合,从而和主场景进行混合。


以上就是原始Builtin管线下的实现思路,转到URP下,有几个问题需要思考:

  1. OnRenderImage在URP下是不支持的。
  2. 是否还需要额外的相机来实现半透明对象的渲染。
  3. 如何处理最后的融合操作。

在URP下,对于第一个问题,有专门的RenderFeature可以对应OnRenderImage的操作;第二个问题,并不需要额外的相机来渲染半透明对象,只需要模仿URP自带的CopyColor来实现渲染另外RenderTexture的操作即可,只是BlitCopy使用的材质球不一样;第三个问题,同样用另外一个RenderFeature来实现即可。

URP下的实现效果:

URP下RenderTexture中渲染OffScreen Particle的效果如下:

在此工程中,主要的点如下:
1、移除额外的Camera,取消了OffScreenParticleCamera脚本的作用。主相机需要开启Copy Depth。额外加了一个Particle的Layer,粒子特效的层改成Particle了。

2、新增了两个RenderFeature,OffScreenParticleFeature和OffScreenMergeFeature。前面的是用来绘制Particle的,后面的是用来做主场景和Particle融合的。在OffScreenParticleFeature中添加了2个Pass,一个绘制Particle到_ParticleRT,一个对Depth进行降采样,绘制到_CameraDepthLowRes,OffScreenMergeFeature中有一个Pass,使用MergeShader进行融合。

3、OffScreenMergeFeature用到的Shader是MergeShader,根据原工程中OffScreenParticle_Upsample Shader进行修改的。

4、为了让Particle层只在OffScreenParticleFeature中进行渲染,需要将两个Shader的LightMode更改为一个自定义的字符串,并且在OffScreenParticleFeature中将这个“OffScreenParticle”加入到ShaderTagList中,ShaderTagList的值是由Pass中的Tags里面的LightMode的值决定的。

最终在FrameDebugger中确定好渲染队列:

  1. 渲染场景中不透明物体;
  2. Copy Depth到_CameraDepthTexture中;
  3. 使用OffScreenParticlePass进行Particle层对象的渲染,可以通过DownSample来调整RT的大小,原始大小或者1/2或者1/4;
  4. 对Depth进行降采样,渲染最终Merge中用到的_CameraDepthLowRes;
  5. 使用MergePass进行最终的Merge,使用的是MergeShader。

Unity Demo下载链接:
https://pan.baidu.com/s/1Zi1avSMAwWkOjCeFj5k4iA
密码:bksb