创建一个Toon风格的着色器

**Toon着色器(toon shading)**是游戏中最常使用的效果之一,也被称作(AKA)cel shading(cel是celluloid的缩写[中文也叫 赛璐珞])。它是一种非真实渲染技术,可以让3D模型呈现一种平面效果。许多游戏中用这种着色器把3D的物体渲染成一种手绘物体的效果。下图中你能看到这两者的区别,右边的是标准着色器,左边的是Toon着色器:


  • 始前准备

    开始学习这个知识点之前,我们先创建一个着色器和对应的材质球,而且需要导入一个特殊的纹理,步骤如下:

    1. 创建一个新的着色器;在这例子中,我们会用上一个知识点的着色器进行扩展

    2. 为着色器创建一个新的材质,并且把它应用到3D模型中。 拥有曲面的模型对于toon着色器来说最好。

    3. 这个知识点需要一张额外的纹理,叫做ramp贴图[如果要全译,我把它叫梯度贴图]。有一点很重要,在导入的时候把Wrap Mode改为Clamp,如果你想让颜色的边缘变得灵敏,就把Filter Mode设置为Point


  • 操作步骤

    通过下面的步骤我们可以获得toon风格的特殊审美呈现:

    1. 添加一个新的叫**_RampTex**纹理属性:
    _RampTex ("Ramp", 2D) = "white" {}
    
    1. 同时在CGPROGRAM块中添加对应的变量:
    sampler2D _RampTex;
    
    1. 修改**#pragma指示 ,从而让着色器使用一个叫LightingToon()**的函数:
    #pragma surface surf Toon
    
    1. 使用下面这个光照模型:
    fixed4 LightingToon(SurfaceOutput s ,fixed3 lightDir,fixed atten)
    {
        half NdotL = dot(s.Normal,lightDir);
        NdotL = tex2D(_RampTex,fixed2(NdotL,0.5));
        fixed4 c;
        c.rgb = s.Albedo * _LightColor0.rgb*NdotL*atten;
        c.a = s.Alpha;
        return c;
    }
    

  • 原理介绍

    toon风格着色器的主要特征是它的光的渲染方式;表面并非均匀的着色。为了能达这种效果,我们需要一张ramp贴图。他的作用是对Lambertian的光线强度NdotL重新映射,然后把值赋值给另一个值。我们使用一张ramp贴图而不是一个梯度值,是为了强制光线按照步骤渲染。下图展示了ramp贴图是如何纠正光的强度的:


  • 额外内容

    我们有很多不同方式来获得toon着色器效果。 我们可以使用不同的ramp贴图来让我们的模型看起来更有吸引力,这就需要你们自己去试试了,然后找到一张你认为最好的。

    还有另一种可选方法对纹理进行梯度采样,就是通过对光强度NdotL进行截断取值,这样就只能在0到1的范围内给它赋值特定的值:

    fixed4 LightingToon(SurfaceOutput s ,fixed3 lightDir,fixed atten)
    {
        half NdotL = dot(s.Normal,lightDir);
        half cel = floor(NdotL * _CelShadingLevels)/(_CelShadingLevels - 0.5);
        half4 c;
        c.rgb = s.Albedo * _LightColor0.rgb*cel*atten;
        c.a = s.Alpha;
        return c;
    }
    

    截断取值部分的代码中,NdotL乘以了倍数**_CelShadingLevels并且把它进行了取整,而得到了一个整数,接着又把结果除了回去。通过上面的这些步骤后, 变量cel被赋值,这个值的范围是0到1之间的值,而且这个值跟_CelShadingLevels相等。有了这个,我们就不再需要ramp纹理了而且所有的颜色梯度也在这个范围。如果你正在你的着色中实现这个功能,不要忘记在你的着色器中添加_CelShadingLevels**属性。