创建一个Toon风格的着色器
Toon着色器(toon shading) 是游戏中最常使用的效果之一,也被称作(AKA) cel shading (cel是celluloid的缩写[中文也叫 赛璐珞])。它是一种非真实渲染技术,可以让3D模型呈现一种平面效果。许多游戏中用这种着色器把3D的物体渲染成一种手绘物体的效果。下图中你能看到这两者的区别,右边的是标准着色器,左边的是Toon着色器: 如果只使用 表面函数(surface functions) 虽然也能获得这样的效果,但是花费性能和时间的代价太大了。表面函数仅仅对材质的属性起作用,而对材质的具体光照环境无能为力。因为toon着色器需要改变光的反射方式,所以我们接下来要创建我们自己的光照模型。
始前准备
开始学习这个知识点之前,我们先创建一个着色器和对应的材质球,而且需要导入一个特殊的纹理,步骤如下:
创建一个新的着色器;在这例子中,我们会用上一个知识点的着色器进行扩展
为着色器创建一个新的材质,并且把它应用到3D模型中。 拥有曲面的模型对于toon着色器来说最好。
这个知识点需要一张额外的纹理,叫做 ramp 贴图[如果要全译,我把它叫梯度贴图]。有一点很重要,在导入的时候把 Wrap Mode 改为 Clamp ,如果你想让颜色的边缘变得灵敏,就把 Filter Mode 设置为 Point :
操作步骤
通过下面的步骤我们可以获得toon风格的特殊审美呈现:
- 添加一个新的叫 _RampTex 纹理属性:
_RampTex ("Ramp", 2D) = "white" {}
- 同时在 CGPROGRAM 块中添加对应的变量:
sampler2D _RampTex;
- 修改 #pragma 指示 ,从而让着色器使用一个叫 LightingToon() 的函数:
#pragma surface surf Toon
- 使用下面这个光照模型:
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 属性。
希望它能帮到你或者帮你找到思路(^_^)选个表情,或者留个评论吧!