如何在Unity3D里制作卡通渲染效果

发表于2017-03-16
评论0 1.21w浏览

作者:  Marek Marchlewicz     润稿 - 阿祥


Twin Souls: Path of the Shadows
是一款游戏性相当完善,且卡通渲染效果相当优异的手机游戏,本文由 Unity 大中华区技术经理Marek Marchlewicz(马瑞),为大家分享一篇如何透过修改 Unity 的渲染管道来实现与Twin Souls: Path of theShadows一样的卡通渲染效果。

卡通渲染原理

卡通渲染是一种非真实感的渲染方法,一般也称作 Cel-Shading Toon Shading,透过将平滑阴影变化转换成拥有明显边界,让画面呈现出手绘般的效果。

说明: https://2.bp.blogspot.com/-cNHMo8RLWgo/WMirgV4wxQI/AAAAAAAAD0A/U4Vx6ozyBz4zV4L1jXUUjl3DnKKju3NnQCLcB/s1600/tumblr_inline_nqtgw1fo3n1ri0s9d_540.jpg

 

上图中,由左至右分别是 DiffuseCel Shading 以及拥有三个临界值的 Cel Shading。在通常情况下一个像素所接收到的光源强度是光源方向与法线方向之间的点积(NdotL)。将这个光源强度经由四舍五入调整,或是透过不同临界值的取样后,就会呈现出不同的卡通渲染效果。在最简单的卡通渲染效果中,如果点积大于零,则像素会呈现高光,反之如果点积小于零,则像素会被设定成阴影。

 

实现方法

卡通渲染效果有许多不同的实现方式,而这次我们会针对渲染路径(Renderring Path)来完成不同的实现方法。渲染路径可以在摄影机的 Inspector 面板中设定。下面我们会介绍如何透过正向渲染与延迟渲染路径来实现卡通渲染效果。

 

正向渲染(Forward Rendering)

如果你的场景并不是多光源场景,正向渲染(Forward Rendering)是一个不错的选择。因为在这种模式下,渲染引擎会遍历所有光源的顶点及像素,在多光源场景里可能会带来较大的负荷。但是,若是你的场景中只有少数或甚至一个光源,这种遍历方式就不会造成负担。具体实现时,可以创建一个表面着色器(Surface Shader),并透过自定义的光照模型,来实现如何在正向渲染中完成卡通渲染效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
Shader "Custom/CelShadingForward"
{
Properties
{
_Color("Color", Color) = (1, 1, 1, 1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
}
 
 
SubShader
{
Tags
{
"RenderType" = "Opaque"
}
LOD 200
 
 
CGPROGRAM
#pragma surface surf CelShadingForward
#pragma target 3.0
 
 
half4 LightingCelShadingForward(SurfaceOutput s, half3 lightDir, half atten)
{
half NdotL = dot(s.Normal, lightDir);
 
 
if (NdotL <= 0.0)
NdotL = 0;
else
NdotL = 1;
 
 
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2);
c.a = s.Alpha;
 
 
return c;
}
 
 
sampler2D _MainTex;
fixed4 _Color;
 
 
struct Input
{
float2 uv_MainTex;
};
 
 
void surf(Input IN, inout SurfaceOutput o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
 
}



经由 “LightingCelShadingForward” 的运算,可以计算出 NdotL,并且透过 NdotL 来完成卡通渲染效果。此外,还可以使用简单的方法来避免 “if else” 的使用。可行的方法为:
NdotL = 1 + clamp(floor(NdotL), -1, 0); 


若是需要模糊边缘则可以使用:

NdotL = smoothstep(0, 0.025f, NdotL);

 

延迟渲染(Deferred Rendering)

与正向渲染相比较下,延迟渲染(Deferred Rendering)的优点是在多光源的场景中可以得到较优异的性能表现。当使用延迟渲染时,引擎会先遍历场景中的每个光源,并且将光源与场景的几何信息储存在缓冲区中。

此外为了重复利用 Unity 的新功能以及强大的 StandardShader,我们这次使用的方法运用了新的 Unity 延迟渲染运作流程。而这种做法从最初的解决方案发布以来,Unity 对此又进行了许多更新,所以需要完成下列步骤:

Unity 官网下载 Built-In Shader

将下载文件进行解压缩,在 “DefaultResourcesExtra” 文件夹中找到 “Internal-DeferredShading.shader”,并复制到专案中的 “Resources” 文件夹

在解压缩文件中的 “CGIncludes” 文件夹里找到 “UnityDeferredLibrary.cginc” 以及 “UnityStandardBRDF.cginc”,并将这些档案复制到转案中的 “Recourses” 文件夹

“UnityDeferredLibrary.cginc” 重新命名为 “UnityDeprecatedEx.cginc”,并新增下列方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
inline half CustomDotClamped ( half3 a, half3 b )
{
#if (SHADER_TARGET < 30)
 
    return saturate(dot(a,b));
 
#else
 
    return max(0.0h, dot(a,b));
 
#endif
 
}
 
inline half CustomLambertTerm ( half3 normal, half3 lightDir )
{
     return smoothstep(0.0,0.05f, CustomDotClamped (normal, lightDir));
}

 

“UnityStandardBRDF.cginc” 重新命名为 “UnityStandardBRDFCustom.cginc”,复制 BRDF1_Unity_PBS 方法,重命名为 BRDF_CUSTOM_Unity_PBS 方法,并将下列代码取代:

halfnl = saturate(dot(normal, light.dir));
取代为:
half nl = CustomLambertTerm(normal, light.dir);

“Internal-DeferredShading.shader” 中,修改汇入的 CG 库名称 :

#include"UnityDeferredLibrary.cginc"

#include "UnityStandardBRDF.cginc"

修改为:

#include"UnityDeprecatedEx.cginc"

#include "UnityStandardBRDFCustom.cginc"

 

“Internal-DeferredShading.shader” 中,删除 UnityPBSLighting.cginc

“Internal-DeferredShading.shader” UNITY_BRDF_PBS 方法修改成 UNITY_CUSTOM_BRDF_PBS 方法

half4res = BRDF_Unity_PBS (......);

修改成:

half4res = BRDF_CUSTOM_Unity_PBS (......);


在完成上述的所有步骤后,我们的 “Internal-DefferredShading.shader” 就大功告成了!接着我们就可以在 Edit > ProjectSettings > Graphhics > Defferred 中选择我们修改后的 “Internal-DeferredShading.shader” 着色器。

说明: https://1.bp.blogspot.com/-FoL36qzBbT4/WMirf1Q0RiI/AAAAAAAADzw/nrMJIMFVsY0rU6ny0RWLX0stBYkByLmKwCLcB/s1600/Photo%2B14-03-2017%252C%2B8%2B05%2B29%2BAM.jpg

最后,确保摄影机的渲染路径切换成正确的延迟渲染后,可以注意到我们在标准着色器(Standard Shader)的基础上完成了卡通渲染效果,如下图所示。
说明: https://3.bp.blogspot.com/-NSU7IIqaqUQ/WMirf75-1HI/AAAAAAAADz4/AOEuLBJK8_McWQ1PrFpfCrfkwhz6YrUIgCLcB/s1600/Photo%2B14-03-2017%252C%2B8%2B06%2B11%2BAM.jpg


外轮廓(Toon outline)

许多卡通风格的游戏中,都会对物体进行外轮廓的绘制,这种方式会让画面呈现出漫画一般的视觉效果,而这种外轮廓效果当然也有许多不同的实现方式。可以透过在着色器中检查法线方向与视角方向的点积,或是使用 Two Pass 来做两次渲染,当然也可以在后制效果中实现。最简单的应用方案是在摄影机中添加 Edge Detection 效果:

点击 Assets > Import Package > Effects,导入 Standard Assets Effect

选择场景中的摄影机,并且在检视面板(Inspector)中点击 Add Component > ImageEffect > Edge Detection > Edge Detection


最后完成的效果如下。

说明: https://3.bp.blogspot.com/-Yc25gxXlP8I/WMirf-JFVUI/AAAAAAAADz0/S5c0SXAKyDEiSqKiQxKxT3M2Yu58L5fMgCLcB/s640/Photo%2B14-03-2017%252C%2B8%2B06%2B02%2BAM.jpg


最终效果

在本文中,我们参考了《Twin Souls: The Path of Shadows》中的卡通渲染效果,最后在几乎没有使用其他添加资源的前提下,透过修改 Unity 的渲染管道来加以实现卡通风格渲染,完成了类似的画面呈现,如下图所示。

说明: https://4.bp.blogspot.com/-x8FQ54ILju0/WMirgSh4plI/AAAAAAAADz8/1dgVBwpxn8oRARqwcLlnpTREb66YFIruQCLcB/s1600/Photo%2B14-03-2017%252C%2B8%2B06%2B34%2BAM.gif


总结

这种透过使用延迟渲染所完成的卡通渲染效果技术相当有用,因为它并不会替换整个光照模型,而只是针对现有模型做特殊的阴影处理,可以在几乎没有额外性能开销的情况下保留 Unity PBRPhysically-BasedRendering)技术并完成卡通渲染效果。


如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引