屏幕空间厚度
总有一些时候需要获取特定模型的屏幕空间厚度,如次表面散射,透明渲染,或者最近项目中要求实现的X光效果。
Unity在2023.1的HDRP中提供了这一功能,经询问,URP暂无此计划。
翻阅HDRP的实现源码后才发现这个效果并没有那么复杂,性能损耗也没有想象中那么大,实现的方式很巧妙,遂决定在URP中复刻一下。
HDRP中的实现
最关键的代码是一个Shader:ComputeThickness
,贴一下关键代码:
这里巧妙的利用了BlendOp Add,不剔除背面,每个三角面的点都参与计算,如果是背面,就记为正,写入其深度。
如果是正面,就记为负数,减去其深度,这样无论模型有多复杂,只要是封闭的模型(不封闭的模型何来厚度一说),都可以像素级别的计算其准确的厚度。
第二个通道则是都记为1,无论正负都加1,目的是来计算屏幕空间的面片数量,对与非封闭模型,如树叶植被有很大帮助。
URP中的实现
知道其原理就开始着手在URP中实现吧。
思路
分为两个RenderFeature,一个用来绘制物体的厚度到厚度图,再一个全屏pass,用来绘制到屏幕上。
实现
首先还是新建两个类,一个Feature一个Pass
为了之后的通用性,设计成RenderObjectsToRTHandleFeature
,即使用设定的Material,把指定层级或渲染层的物体绘制到一张临时RT上。
这里主要设定了临时RT的名称,材质,RednerPass的插入时间,是否使用深度,RT的格式,以及要绘制的物体的层和渲染层。
对应到要绘制深度图的话,那就是如下的设置
然后具体看看Pass的实现
总之就是沿用Feature的参数,申请各种RT,然后配置各种渲染参数,最终绘制到指定的RT上。
shader
最后就是看看Shader吧。
和hdrp的实现基本一致,增加了一些URP要求的配置而已。
全屏绘制
这里就用ShaderGraph来绘制吧
可以同时启用多个厚度图Feature,每个都绘制到不同的RT上,然后在SG中根据权重来混合。
比如这里就根据骨骼和身体的不同厚度,来赋予不同的权重,就可以得到X射线的效果。
如果想更细致一点,还可以用身体厚度减去骨骼。
RenderingLayerMask
Unity并未对RenderingLayerMask提供对应的在Inspector中方便可用的下拉列表实现,这次就顺便实现一个。
首先定义下结构体
然后是对应的PropertyDrawer
大体思路是通过GraphicsSettings.defaultRenderPipeline.renderingLayerMaskNames;
来获取全部maskname,然后利用EditorGUI.MaskField来选择。