一、效果:
在很多游戏中为了玩家能看到墙面背后是否有敌人,会给被遮挡的敌人增加描边显示,效果如下:
参考:
https://zhuanlan.zhihu.com/p/81310476
https://zhuanlan.zhihu.com/p/358140547
二、所需知识
知识点梳理
1、PixelDepth 像素深度
当前被渲染的这个像素点与摄像机的距离。该节点没有输入接口,说明这个节点不能够获取特定位置的深度,只能获取像素着色器目前处理的这个像素点。
最基础的深度概念,我们说起深度时默认指的就是PixelDepth。
2、SceneDepth 场景深度
场景深度,当前场景的深度信息映射到屏幕上的一张图,从输入值有UV这一点来看就知道它是基于屏幕UV且可以获取非当前像素深度的。比较经常用在屏幕后处理里面。
3、CustomDepth 自定义深度
通过SceneTexture节点获取,用在屏幕后处理中,只需要在指定的物体上开启“渲染自定义深度通道”即“Render Custom Depth”,该物体的PixelDepth就会被渲染到一张单独的Buffer中。
这张Buffer是一张与SceneDepth很相似的Buffer,不同的地方在于它将没有开启CustomDepth的区域扣掉了,用一个极大的值去填充(10^8),得到了一张只有CustomDepth区域有效的Buffer。
假设场景中只有一个物体开启了自定义深度,整个场景的自定义深度应该是这样的:
4、CustomStencil/CustomDepthStencil 自定义模具/自定义深度模具
不要被它称呼中带有的Depth迷惑了(血泪教训),它根本不是深度值,而是一个模板值,这个模板值就是在细节面板设置的一个1-255的整数值,从个人的使用体验来讲它就像是一个图层的标识,可以将物体分类处理。
各蓝图节点之间的区别:
PixelDepth VS SceneDepth
SceneDepth很好理解,就是一张写入了屏幕上每一个像素距离摄像机的距离的图片,可以找到很多示例,注意深度值一般要除以3000左右的数值来使深度值在0-1范围内才能直接输出到材质的BaseColor节点上测试。
而PixelDepth不是一张图,只是一个点而已,它对其他像素的深度一无所知。
SceneDepth VS CustomDepth、
就是把不需要的地方涂掉,看图就比较好理解了。
CustomDepth VS CustomStencil
假设图中三个物体都被开启了在CustomDepth通道中渲染,并且设置了不同的模板值,那么在材质里就可以获取它们的模板值并且分别处理。
说明
在视口左上角的视图模式可以在缓冲显示的子菜单里找到CustomDepth和CustomStencil的可视化,可以通过这个菜单检查自定义深度和模具开启情况和具体数值。注意在ES3.1情况下是看不到缓冲可视化的,记得关闭ES3.1再进行检查。
启用CustomDepth需要在项目设置-Rendering-Postprocessing中启用自定义深度-模具通道
这里深度和模具是分开开启的,“已启用”选项意味着只启用深度而禁用模具,需要注意有一下,如果设置了模板、能够看到自定义深度,却看不到模板可视化图像,可以检查一下项目设置。(以模具开启)。
三、分析
要找到物体的边缘边界,假设这是场景的自定义深度图,每一格是一个像素,里面存放着各个像素点的自定义深度值
我们看到的物体内部是蓝色数值,红色的就是我们需要的边,正无穷就是没有开启自定义深度的物体外部
判定物体的边有很多种方法,我们要先搞清楚以下几点:
a.物体的内部:可以获取到自定义深度的数值,上下左右4个像素点都可以获取自定义深度
b.物体的边缘:可以获取到自定义深度的数值,上下左右4个像素点不全都能获取到自定义深度
c.物体的外部:自定义深度的数值为正无穷,上下左右4个像素点不全都能获取到自定义深度
通过上述规律,我们可以用这种方法确定物体的边界:当一个像素点自身的自定义深度值不为正无穷,但上下左右四个像素点的和却为正无穷时,这个像素点就是物体的边(UE4中没有正无穷,所以用一个很大的数来近似判定是否为正无穷)
而在UE4中SceneTextureCustomDepth(自定义深度)就是用来输出这张图的 。效果验证图如下:
但是直接连接的话由于数值都大于1,所以都显示为白色,需要除以一个很大的数来把数值控制在0-1之间才能正确显示我们需要的结果
四、步骤:
了解了原理后,我们开始制作描边效果。
1、创建后期材质
创建一个新的材质球,在参数中修改为后期处理与色调映射前
2、添加屏幕位置ScreenPosition节点
屏幕uv是这样的,先创建一个ScreenPosition(屏幕位置)节点,ScreenPosition的原点在屏幕左上角。
ScreenPosition蓝图节点的ViewportUV引脚输出的是每个像素点在屏幕上的UV值(2维数组),取值范围是0~1,等于是每个像素点的UV都获取一次,然后进行处理,连接到自发光可以看到,横坐标为R值纵坐标为G值,右上角为(1,0)显示红色,左下角为(0,1)显示绿色,右下角为(1,1)显示黄色(红+绿为黄色)
ScreenPosition蓝图节点的PixelPosition引脚输出的是每个像素点在屏幕上的位置坐标,取值范围是0~屏幕尺寸,直接连接到自发光可以看到画面偏亮,但不是纯白色
而当我们把这个值除以1000后,画面的颜色与ViewportUV接近
3、理解SceneTexelSize场景纹素大小并使用
我们再创建一个SceneTexelSize(场景纹素大小),这也是一个2维数组的值,对应着UV(u,v),uv均为正数,这里u就代表着要在横向偏移一个像素单位需要增加或者减少多少,v也同理
比如你的屏幕是1920*1080的,那么你屏幕的横向上就有1920个像素,U的取值区间是0~1,那么横向上每个像素就占1/1920≈0.000521,同理纵向上每个像素就占1/1080≈0.000926,那么SceneTexelSize的值就为(0.000521,0.000926)。
举个例子,比如现在有个像素点的uv为(0.2,0.6),那么这个像素上方的像素点则为
(0.2,0.6-0.000926),下方则为(0.2,0.6+0.000926),左右同理(因为UE4中uv坐标系原点在左上角,所以这里向上为减,向下为加)
上述的结论可以由PixelPosition与SceneTexelSize相乘得到的结果验证
SceneTexture:PostProcessInput0(后期处理输入0)节点:这个节点其实就是SceneColor(场景本来的颜色),而SceneColor只能用于材质域为Surface(表面)的材质,所以这里用的是SceneTexture:后期处理输入0
观察场景没有变化。
理解了这些节点之后,我们就可以通过这些节点来计算出各个自定义深度像素点的上下左右4个像素点之和了。如下图所示:
4、判断上下左右四个深度之和是否是无穷大
然后我们再把算出来的和跟一个近似无穷大的数作比较,如果大于近似无穷大的数则为自定义模具物体的边与外部,给它一个颜色值(这里我用了黄色),如果小于近似无穷大的数则为自定义模具物体内部,给它场景原本的颜色 。蓝图节点如下图:
5、使用后期材质观察效果
(一定要注意蓝图节点输出数据的维数):
6、使用自定义模具作为选区使用
从上面结果上看,我们现在已把开启自定义深度的物体外部和物体边缘显示了黄色,物体内部显示了本来颜色,现在我们需要把物体外部排除掉,让它也显示原本的颜色(自定义模具正好能够得到这么个选区)。
实现的蓝图节点如下图:
注意:项目设置中不要忘了以模具开启:
得到的效果如下:
7、添加遮挡判断
现在的结果是比较接近目的了,但是我们需要的是被挡住的才能显示,所以还得加上判断:
通过上面的区别我们知道了customDepth与scenedepth的区别,通过下图,我们能更加直观的区分开:
SceneDepth(场景深度):当前画面上的像素点和摄像机之间的距离,这是从你看得到的地方开始算的,比如摄像机镜头正对着一面墙,墙后有个自定义深度的立方体,那么获取到的场景深度就是黑色虚线的长度(墙到摄像机之间的距离),跟立方体无关。
CustomDepth(自定义深度):想获得被遮挡的物体离相机的距离,要开启物体上的自定义深度通道,如果在UV对应的像素点方向上有物体开启了自定义深度,即使被遮挡也可以获取到它的场景深度。如果该位置没有物体开启自定义深度,那么得到的值则是无穷大。
所以我们就可以根据自定义深度(绿色虚线)和场景深度(黑色虚线)的差值来判断是否有东西被遮挡住(如果有两个开启了自定义深度通道的物体在一条线上的话,那么获取到的CustomDepth是离摄像机更近的那个)。
CustomDepth-SceneDepth的差
(1)如果开启CustomDepth被挡住,那么CustomDepth>SceneDepth,CustomDepth-SceneDepth=有限值
(2)如果开启CustomDepth没被挡住,那么CustomDepth=SceneDepth=0
(3)如果没有开启CustomDepth,那么CustomDepth-SceneDepth=无穷大
结论:
当<=0时,也就是物体没有被遮挡,那么用原本的颜色即SceneTexture后期处理输入0的Color值;
当0<差值<9999999时,说明物体开启了自定义深度并且被挡住了,输出之前得到的描边;
当>9999999时,也就是为正无穷,说明没有物体开启自定义深度通道,也是用原本的颜色。
但是由于上一步中我们已经用SceneTexture自定义模具判断是否开启自定义深度通道了,所以我们下一步只需要判断是否遮挡就可以了:
8、效果如图:
五、拓展
1、亮度分层
现在完全达到了我们需要的效果,只有遮挡的部分会显示描边,但是在上一章中我们知道了,当我们调整自定义深度模具值后,亮度会变化,因为自定义模具只是返回一个数值就是模具值,开具自定义深度并有模具值的才显示这个颜色灰阶,其它显示黑色,这会使效果更佳有趣,可是如果我们不需要亮度增加的话,我们设置自定义深度模具值为1就可以。
2、描边宽度
可以添加一个Width参数,这个参数与SceneTexelSize相乘,就可以调节描边的粗细了,就是通过改变偏移的像素数量来控制描边粗细。蓝图节点如下: