Unity学习笔记 Vol.81 如何制作卡通着色器
摘要
本次我们将了解Unity开放项目中卡通风格背后的理念和流程。开放项目是小型开源游戏,社区创作者可自由合作,为整个游戏开发做出积极贡献。这个项目是动作冒险游戏,你可以在描述中找到GitHub及相关链接。
我们使用了Unity 2019.4LTS开发游戏,确保开发过程中有最佳的稳定性。我们更新了卡通着色器,使其兼容Unity2020.2,你可以在原Git代码库的一个分支中找到2020.2版本的卡通着色器,名为devlog-1-toon-shading。项目下载请参考官方视频教程。

教程
确定卡通形象
在确定了艺术风格后,我们很清楚哪个Unity可编程渲染管线最符合我们的需求,图像风格并不写实,不需要高度逼真的视觉效果,我们需要的是干净、简单且风格化的图像风格,因此决定采用通用渲染管线。

在这个渲染管线下,我们可以实现卡通风格,同时保证Unity项目性能,在各种硬件上运行,着色器使用Shader Graph制作有两个主要原因,我们希望这些内容对于大家来说都是可以使用、理解的,第二个是使用Shader Graph制作出画面后,我们可以轻松为着色器添加额外的效果,比如使用顶点偏移的风吹效果,调整透明度实现的溶解效果等等


卡通着色也称为Cel Shading,是一种以非真实光照模型为特征的渲染风格,此类风格通过用统一的颜色表示阴影和高光区域,并不使用普通的渐变着色,除了自定义的光照,我们还用一个额外的通道来制作黑色的轮廓线,为模型添加更多细节,使其看起来更像会动的漫画或手绘动画。

我们仅在人物身上添加了轮廓线,使其从环境中脱颖而出,可以看到,最终3D模型与原画非常相似,为了取得最终结果,我们将加入各种类型的光照,并可以从Shader Graph中控制光线。

在此阶段,我们没有专注于卡通着色效果,相反,我们希望先确保着色器支持以下内容:
· 带有光照、阴影和镜面反射的自定义光照模型
· Shadow cascades(阴影级联)
· 静态对象的烘焙式全局光照
· 环境光
· 其它光照与阴影
· 光照探针

第一步是添加主光源支持,默认下,URP中的主光源是场景中最强的方向光,借助Shader Graph中的自定义功能节点,我们可以收集这个光源中的所有光照信息

节点中的代码会调用URP中已有的一个函数,称为GetMainLight,用以访问相关数据,如方向、颜色和阴影贴图,在这里作为阴影衰减(ShadowAttenuation)显示出来

接着取几何表面法线向量和光方向的点积来建立自定义光照模型,我们将结果钳制在0和1之间,再与阴影衰减相乘

结合数值结果和光照颜色,就形成了基本的漫反射光

除了从主光源获取数据输入之外,我们希望着色器能接收,场景中存在的所有其他光照

因此也和之前一样使用了自定义函数,借助GetAdditionalLight内置函数,可以遍历所有其他存在的光照,再使用GetAdditionalLight来收集每种光照数据,这时事情开始有意思起来了

如果仔细观察着色器,会注意到MainShadowAttenuation,也用在了静态几何形上,以实现对大型几何阴影的实时控制,我们建立的方法基本等于Unity中的混合光照模式

该实时阴影将乘上Baked GI节点的输出,取得最终的阴影数据

接着角色便能在卡通风格的景色上投射黑色阴影,符合原画。Baked GI节点也为着色器添加了光照探针支持,适用于动态几何形

为了测试着色器的功能,我们创建了一个包含所有光照情况的场景,加入了方向光、实时点光源,混合点光源,以及静态与非静态球体,两者会投射出实时与烘焙阴影,在该着色器基础上,我们就能制作卡通着色器效果了,着色器上的所有光照功能都根据运行逻辑进行了划分,我们可在节点之间添加额外的节点上步骤来控制数值实现想要的画面,卡通着色器一个觉特征是,用高对比度的色带取代平滑的颜色渐变,使用称为Ramp Shading的技术

为此,我们添加了一些渐变色采样节点,将所有的光照数据转换为由自定义色带定义的新颜色

我们的设想是将表面光照的梯度值限定为较小范围内的颜色,这样一来就能在受光与阴影部分生成清晰的分界线,通过修改色带颜色可以实现不同的画风。
渲染轮廓线

要制作轮廓线效果,我们需要从摄像机缓存处,获取法线与深度信息,需要使用结合了两者的纹理,这种纹理直到最近才引入URP,主要用于屏幕空间环境光遮蔽,但它并不支持兼容2019.4的URP,幸好URP是可编程的渲染管线,可以根据需要修改,实际上我们采用了网上一个质量较高的方案,作者是技术美术Alexander Ameye,

为了生成法线与深度纹理,我们创建了一个自定义可编程渲染功能,将其添加到了渲染器资源中

生成新纹理后。再使用自定义功能节点,从纹理中采样、收集法线与深夜数据,使用这两种数值绘制轮廓线。我们可以为每个材质修改轮廓线的宽度,深度和法线敏感度,定义不同几何形上轮廓绘制的方式、位置,更好地匹配形状。

最后,我们将轮廓绘制逻辑加入了卡通着色器,将轮廓值乘上卡通着色器模型的颜色,得到最终数据实现最终结果,之前提到,该着色器为Open Project的早期内容之一,随着项目的推进,我们会根据游戏需求的变动不断增补,修改着色器,在了解了着色器背后的制作流程后,我们再来谈谈项目的下一步,以及如何解决当前面对的挑战。其中有一个制作中的改进,是关于对象移动时阴影出现的抖动现象,该问题不影响自身所带的阴影,仅影响其他物体投射的阴影,由阴影贴图突兀的颜色过渡直接造成,因为贴图的灰度值都被近似取为黑色或白色

增加阴影贴图的分辨率,可减少抖动现象,但这不是理想的解决方案,一个减轻问题影响的快捷方案是用Toon Ramp卡通色带,而不是强制转变的颜色渲染阴影,在色带中加入一块缓冲区,光照值可以流畅地从黑色变为白色,不会在两个数值之间跳动。

另一个可能加入的功能是处理着色器颜色属性的上色系统,根据环境在不同的色板间切换。比如,在夜间用蓝色色带为着色器的对象上色,或使用橙色到紫色的色带来模拟夕阳,修改色带的深色,便可将阴影的颜色改为任意颜色,而在轮廓线上,我们需要解决低分辨率下出现的线条锯齿现象。