描边shader再探讨

之前的一篇描边shader只是讲了实现的大致思路,但是实际去实现一个普通的角色描边shader的话,会发现很多很多的问题……

普通的描边感觉比较好也比较常用的方式是用两个pass,底下那层在屏幕空间沿法线外扩,且剔除正面,上面那层用普通的。
但是真的使用起来会用很多很麻烦的事。

一、关于是否只要外轮廓

图一为只要外轮廓,图二为中间也要,注意看手,手这个角度在外轮廓中,但也有描边。

如果只要外轮廓,轮廓内部的描边不要的话,可以在第一个pass设置ZWrite off,后一个pass就可以严严实实的盖住它,只露出旁边一圈。
关闭深度写入很省心但是会有很多问题,比如Geometry之后会有天空盒的渲染,就会把深度未写入的描边给盖住,就需要调整渲染队列,然后又会带来新的问题……这个时候这个做法就已经不经济了。另一个方法是用Offset指令控制深度测试,让后面的描边颜色稍稍靠后一点。但是往后多少呢,少了的话,一部分面片穿插不能解决,多了的话,角色后面有个靠的很近的物体可能就会出问题……

而如果要内部描边的话,让一些背面的渲染结果“露出来”,就会因为模型的缘故出现各种奇奇怪怪的问题,严重的比如……

即便模型建的比较规范,没有严重的面片穿插问题,也会出现一些描边位置奇怪的问题,比如

你会感觉你希望的描边并不是这样的……不过想做到这个要求就比较高了,计算机视觉这方面要做的好都需要复杂得多的算法,比如用双阈值判断是否连接……而且关于哪些地方应该描边有很多主观的因素,想象你自己在一张原画上画,很多地方该不该描也要犹豫。

二、关于如何解决面片割裂开

就是沿法线外扩的时候,面片会分开,像这样:

如果外扩不是很多的话,可以将要外扩的方向和这个点相对于模型中心的方向lerp一下,可以改善一点点……
另外有个方法是在导入设置中设置法线为calculate

但是这么做一方面比较费,另一方面效果还是要看情况,比如修改前修改后分别是这个效果

要效果好一点,就需要对美术资源下手,比如崩坏3就是这样做的:

接下来让我们来谈谈高品质勾线的方法,对于角色和动态物体我们使用backface勾线方法,并使用顶点色对勾线的宽度进行控制,勾线本身需要连续的顶点法线才能在锐角边不会出现断层,因此我们将平滑过的法线存储在另一套顶点色里,此外,我们也使用顶点色来控制勾线宽度,比如,发尖处勾线会逐渐变细,我们通过在顶点颜色填充渐变为0的值以使线条宽度逐渐过渡到零,另外,根据相机与物体之间的距离,还应有基于距离修正的勾线宽度。每种材质上也应该有对应的不同勾线颜色, 所有这些功能都是高品质的勾线所必需的。

实际上在一般的描边粗细条件下,大部分模型的断裂现象不明显,但是头发由于发尾比较尖细就会很明显,参考崩坏顶点色的做法,可以直接给发尾的顶点色改成黑色,第一个pass延发现外扩的距离*顶点色,这样发尾描边就不会断开,而且发尾原本描边就是细一点好看。

三、关于描边是否渐变

有些时候需要描边往内渐隐,会有一种发光感

第一反应:简单啊,就是另一种描边的方式,根据法线和视线夹角的值加描边,角色的边缘和视线的值差不多是九十度。但是还有另外一个要求,只要外轮廓,中间不要加……如果按之前想的方法,中间所有凸起的地方旁边都会有一圈发光描边,就不符合要求了。

第一个想法:第一个pass外扩,第二个pass往内缩一些,然后把第一个pass的渐变限制在露出来的那一截。实际操作的时候,由于露出来的那一截需要比较宽的一段,顶点沿法线移动太多导致很多面片分离以及穿插的问题……

第二个想法:用模型空间的坐标做判断,设计一个函数使得,顶点离原点越近,描边影响就越小,同时x轴上的影响更剧烈一些(因为这个shader用在角色上,x轴比较短,y轴比较长)。这样的话,角色一旦是张开手臂的话,手臂上就没有描边了……而且因为头和身子头发都是分开的mesh,也会有问题,随着角色形象的变化还有很多很多问题………总的来说这个方法太过“定制化”了,毫无实用性。

第三个想法:一般中间不需要描边的部分离摄影机更近,所以可以计算顶点与摄像机的距离,同时计算模型上的(0, y, 0)点与摄像机的距离,两个距离差值越大,描边越弱。这个效果相对来说还可以。

但是也会有很多的问题
1、中心即便突出一些,也突出的很少,需要比较高的精度
2、不同角色差异会很大
3、当角色斜侧着身子就会不符合想要的效果。比如像上图这个角度,最左边的左手和最右边的头发应该是同等的描边效果,然而它们分别是离摄像机最远的一点和最近的一点。

四、关于使用后处理

其实用后处理很多时候效果会好很多,毕竟发生的很多问题都是3D方面的问题……然而因为后处理太费,尤其是对于移动端,这是一种尽量不要去考虑的方式……
后处理的方式就是添加一个额外相机,复制原相机属性,只将需要描边的物体渲染在RT,可以通过layer指定渲染物体。然后将RT扩展出外边,类似高斯模糊,如果不想要渐变的描边效果可以做判断,只要大于0就算描边区域(记得用step不要用if)

五、总结

目前没有非常完美的方案,要结合实际使用情况选择一种相对来说效果好一些的方案。但相对来说,2pass的方法+描边调细+特殊情况配合顶点色 是最不错也最常见的方案了。