Unity下的日式卡通渲染实现-着色篇(一)

这篇文章讲述的是项目中二次元日式卡通着色渲染用到的一些跟着色相关的技术点。

一、卡通着色

何谓卡通着色?大概是让角色看起来卡通的角色吧。这里说的卡通着色,实际上指的是色阶着色。即根据光照和法线计算出当前像素处于哪个色阶,色阶之间有平缓过渡。

1.1 三色阶

如下图所示,

这是一个三阶色的着色结果。可以看到着色结果是明显的三阶,从正对光照到背对光照是过渡分明的白色、灰色、黑色的三色阶。卡通渲染着色最基础的部分就是这种明显过渡的色阶。根据需要,项目中可能采用的是二色阶或者三色阶。我们观察过战双的角色效果,猜测采用的是三色阶。
实现原理:
选定三个颜色,作为亮色、灰色、暗色;选定2个阈值,0-灰色阈值表示亮部,灰色阈值-暗色阈值表示灰部,大于暗色阈值表示暗部;计算1-halfLambert表示着色的暗度,将暗度映射到上一个范围,选取颜色;为了色阶过渡平滑,增加过渡区域大小,使用smoothstep平滑过渡边界。

1.2 暗部控制

增加一张控制贴图,用一个通道比如r通道控制对应像素的暗的程度,我们叫其为darkness。
限制一:如果当前像素的darkness大于暗部的阈值,那么才可能在暗部区域,否则只能是灰部或者亮部区域。
限制二:当前像素的dark程度是darkness和1-halfLambert的最大值,也就是像素的dark程度只能大于等于控制贴图的darkness。
这样的限制后,方便美术控制哪些区域是暗部,不管光照如何变化这些区域始终是暗色,同时其着色结果只能比darkness更暗。
效果如下图所示:

可以看到原本灰部的一些区域被限制一限制为固定暗部了,同时亮部的一些区域被限制二调整为灰部了。对应限制二,其实也可以只对暗部处理,这主要是看美术的需求。

1.3 色阶贴图

进一步扩展,默认情况下三色阶的颜色是在材质中指定好的固定颜色。美术进一步提出,希望去从贴图中去获得变化更多的颜色。因此,可以对色阶某一个颜色增加一张颜色贴图。色阶着色时候,从贴图中通过uv取当前色色阶色,而不是使用固定的色阶色。
下图是一个灰部使用色阶贴图的效果:

1.4 Ramp贴图

跟二色阶和三色阶对应的是,直接使用halfLambert或者1-halfLambert从ramp贴图中获得着色结果。由于Ramp贴图本身就是一个良好过渡的梯度贴图,因此也可以实现和上述三色阶类似的效果。
下图是一个二阶色Ramp的结果,中间的红色是Ramp贴图上的过渡色:

Ramp贴图和三阶色实现的效果类似,不过Ramp贴图需要一张额外的Ramp贴图和以及一次贴图读取操作,但是三阶色计算量大。

1.5 和颜色贴图结合

三阶色的结果乘以角色的基础颜色后就可以得到基本的卡通着色效果,如下图所示:

二、Pbr着色

可以参考文章【04】从零开始的卡通渲染-PBR篇。这篇文章讲得非常详细,包括角色的Pbr着色和卡通着色如何组合以及角色的卡通着色如何与Pbr场景角色组合等的一些思路。
我们的基本思路是实现一个近似的Pbr着色,包括直接光照和间接光照(近似环境光照pbr)。增加Pbr着色好处是可以实现一些Pbr材质才能达到的效果,比如Pbr的金属感等。同时通过在pbr的控制贴图中使用一个通道来作为蒙版,控制pbr着色的比例。Pbr的实现代码很多,因此具体实现思路,不再赘述。

三、皮肤Ramp

皮肤Ramp是一个比较简单的功能。通过计算halfLambert作为坐标去ramp皮肤贴图中获得皮肤颜色,再进行一定的缩放以及和皮肤基础颜色相乘得到一个最终的颜色。同时类似于于Pbr着色,提供一个蒙版或者混合比例,和卡通角色结果进行插值。
这个着色功能在某些情况下可以方便美术控制整体的皮肤梯度色调。

四、高光

卡通渲染的高光的可以采用Blinn-Phong高光或者使用近似的GGX直接光照高光。这两种高光的实现思路,请参考相关资料。

4.1 各向异性高光

所谓各项异性高光,指的是在不同的方向上高光表现是不一致的。
比如,Blinn-Phong高光不管从什么方向看过去都是一个高亮的光斑,如下图所示:

但是各向异性高光可能是不规则形状的,比如环形,即我们常说的天使环,如下图所示:

各向异性高光主要是应用在头发上。

4.2 法线+高光控制贴图实现天使环

其实,即使是使用普通的Blinn-Phong高光,使用高光强度控制贴图和各项异性的法线也是可以调出类似的天使环的效果的。首先,控制贴图的强度分布映射到模型上本来就得是环形的,这个主要是靠美术去控制。另外,法线的分布要有一定的方向性。
另外一种方式就是下面要说的Kajiya-Kay各向异性高光。
下图是一个这种思路实现的效果:

4.3 Kajiya-Kay 各向异性高光

网上关于Kajiya-Kay的实现原理文章非常多,比如这篇卡通渲染之头发高光(anisotropy)的实现(URP)
这种各项异性高光的实现思路是把头发当成圆柱体,将法线用副切线替换后再计算高光,如下图所示:

如果想增加一些散乱的效果,可以使用噪声贴图让副切线沿着顶点法线方向做一个随机偏移。

五、自发光

自发光比较简单,直接增加一个颜色值即可。

六、边缘光

边缘光有两种实现思路,一种是根据法线和视线的夹角计算边缘光的强度;一种是根据深度贴图计算当前像素处于边缘的可能性,再转化为边缘光强度。

6.1 NdotV边缘光

NdotV表示的是世界空间法线和视线的点积,反应的是从当前视线看去,像素点处于边缘的可能性。不过,通常还需要对边缘光强度做一些校正,比如根据离视点的距离等。

6.2 深度贴图边缘光

如果使用深度贴图边缘光,那么需要一个额外的深度Pass,将角色的深度写入到深度贴图里。在着色渲染Pass中,采样深度贴图获得当前深度,然后沿着一个特定的方向偏移屏幕空间的深度贴图uv坐标,再采样一个偏移后的深度。对比这2个深度,如果超出一定的阈值,将差值转化为深度边缘光强度。下一篇文章讲到的深度贴图阴影也是这个原理。
效果如下图所示:

七、着色总结

综上所述,一个卡通渲染的着色最终的计算结果是:
卡通着色结果= lerp(三阶色,Pbr着色,Ramp皮肤)+ 高光 + 自发光 + 边缘光。
下一篇文章,我们将讨论卡通着色的阴影实现思路。

八、参考资料

【03】从零开始的卡通渲染-着色篇2
【04】从零开始的卡通渲染-PBR篇
卡通渲染之头发高光(anisotropy)的实现(URP)