专栏目录

2173:【01】从零开始的卡通渲染-描边篇​zhuanlan.zhihu.com

2173:【02】从零开始的卡通渲染-着色篇1​zhuanlan.zhihu.com

2173:【03】从零开始的卡通渲染-着色篇2​zhuanlan.zhihu.com

2173:【04】从零开始的卡通渲染-PBR篇​zhuanlan.zhihu.com

序言:

接上一篇的描边篇,整理成一个专栏了。在本节中,我们开始讨论卡通渲染的一些光照计算方法。

如何让角色看起来卡通

思考一下,究竟是哪些因素,让我们觉得角色是卡通的呢。我觉得可以先从下面3点入手

1.减少色阶数量

2.冷暖色调分离

3.对明暗区域的手绘控制

减少色阶数量

减少色阶的数量,给画面卡通感

冷暖色调分离

明面和暗面分配不同冷暖的颜色,给画面卡通感

在美术上根据颜色区分为暖色调(红色,黄色)和冷色调(蓝色、紫色)。在偏真实的光照计算中,往往只计算一个明暗关系,然后由光和物体的颜色决定最终效果。而卡通渲染则会根据明暗关系,为明面和暗面分配不同色调的颜色。比如一个暖色调的明面,配合一个冷色调的暗面。将色调拉开以后,更进一步给人卡通感。相关链接 tone-based-shading

《GUILTY GEAR Xrd》中通过单独的贴图定义暗面色调,和明面的色调做区分。左图是未调整暗面色调,右图是调整了暗面色调的

在《GUILTY GEAR Xrd》游戏中,绘制了一张称为SSS Texture的贴图,来对暗面的色调进行调整。

对明暗区域的手绘控制

在手绘动画中为了好的画面效果,往往其明暗的分布并不是完全正确的。最明显的,角色的脖子部分通常都出现明显的阴影。经典光照计算的结果是非常“正确”的,因而缺少卡通的手绘感。需要用其他方式对光照的计算结果进行调整。

按照正确的光照计算,角色的脖子是不会有那么明显的阴影的

下面介绍一下《GUILTY GEAR Xrd》中是如何对明暗区域进行手绘控制的

在《GUILTY GEAR Xrd》中,通过灯光方向,Threshold贴图,法线方向对光照计算进行手绘风格的控制

灯光方向控制:

卡通渲染的角色在部分灯光方向下,可以有最佳的画面表现。有时候这个灯光方向和场景灯光或者其他角色的灯光方向不一致。为了让每个角色都有最佳表现,最好每个角色有一盏自己的灯光方向。甚至当这个角色转向时,这个灯光也跟着角色做一定程度的转向,来让角色有一个更好的光影表现。

Threshold贴图控制:

《GUILTY GEAR Xrd》中将这张贴图称作ilmTexture。为了减少歧义,我们这里也这么称呼好了。

ilmTexture关闭和开启对角色阴影区域的影响

这张贴图有些类似于AO贴图,不过它是对光照计算的结果进行一些倾向性的修正。让一部分区域,比如角色脖子的部分更容易产生阴影。来达到手绘风格的阴影效果。

法线方向控制:

法线控制有两种方法,一种是直接编辑法线,达到想要的光照结果。一种是创建一个平滑的简单模型,然后将其法线传递到复杂物体上,达到优化阴影的效果。Maya自带法线传递的功能,3ds Max可以通过插件Noors Normal Thief实现法线传递的功能。

通过直接编辑法线,达到想要的光照效果
创建简单的头套模型,传递其法线到头发上,优化头发的阴影
在《火影忍者 究极风暴》中,将人物的面部向外膨胀,再用膨胀后的面部法线来优化面部阴影

赛璐璐风格插画

《百变小樱》动画的赛璐璐片

赛璐璐片是一种塑料卡片,在早期日本动画制作流程中的,画师会在赛璐璐材质的塑料卡片上对原画进行上色。其特点为通常只有明暗2个色阶,明暗变化的交界非常明显。现在这种风格的卡通渲染比较流行。在本篇中,也将实现偏向这种风格的卡通渲染。

厚涂风格插画

《明日方舟》塔露拉立绘

厚涂风格相较赛璐璐风格,色阶更多,明暗交界变化会柔和很多。这个风格也有它的好处,因为3D场景比较难做成赛璐璐的。如何让赛璐璐风格的角色和非赛璐璐的场景融合是也许需要考虑的。厚涂风格的角色会更容易和场景进行融合。

双色阶的渲染实现

首先我们实现一个明暗边界分明的光照效果,并支持分别设置明暗区域的颜色,设置暗面颜色为冷色调,和明面的色调做出区分。

Shader "Unlit/CelRender"
{Properties{_MainTex ("MainTex", 2D) = "white" {}_MainColor("Main Color", Color) = (1,1,1)_ShadowColor ("Shadow Color", Color) = (0.7, 0.7, 0.8)_ShadowRange ("Shadow Range", Range(0, 1)) = 0.5_ShadowSmooth("Shadow Smooth", Range(0, 1)) = 0.2[Space(10)]_OutlineWidth ("Outline Width", Range(0.01, 2)) = 0.24_OutLineColor ("OutLine Color", Color) = (0.5,0.5,0.5,1)}SubShader{Tags { "RenderType"="Opaque" }pass{Tags {"LightMode"="ForwardBase"}Cull BackCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"sampler2D _MainTex; float4 _MainTex_ST;half3 _MainColor;half3 _ShadowColor;half _ShadowRange;struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;float2 uv : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2; };v2f vert(a2v v){v2f o;UNITY_INITIALIZE_OUTPUT(v2f, o);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;o.pos = UnityObjectToClipPos(v.vertex);return o;}half4 frag(v2f i) : SV_TARGET {half4 col = 1;half4 mainTex = tex2D(_MainTex, i.uv);half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);half3 worldNormal = normalize(i.worldNormal);half3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);half halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;half3 diffuse = halfLambert > _ShadowRange ? _MainColor : _ShadowColor;diffuse *= mainTex;col.rgb = _LightColor0 * diffuse;return col;}ENDCG}Pass{//描边,参考上一篇}}
}

实现明暗边界分明的光照,并且单独设置明面和暗面的颜色来区分色调

smoothstep柔化明暗边界

现在我们希望能够对明暗边界的变化做一些柔化,让风格往厚涂的风格靠一些,这样可以跟更容易地跟一些非赛璐璐风格的场景做融合。这里我们使用smoothstep函数实现这个效果。这个函数可以在根据输入数据,计算一个范围在0到1区间的平滑过渡曲线。通过这个函数的结果对明面和暗面的颜色进行插值,来实现明暗边界的软硬控制。wiki百科链接

smoothstep(0,0.7,x)

对代码进行如下修改

          half halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;half ramp = smoothstep(0, _ShadowSmooth, halfLambert - _ShadowRange);half3 diffuse = lerp(_ShadowColor, _MainColor, ramp);

使用smoothstep函数对明暗分界的软硬进行控制

Ramp贴图

还有一个做法是通过采样Ramp贴图来实现对色阶和明暗边界的控制。可以看成是用标准光照的结果为UV,采样一张用作颜色映射表的贴图,通过这张贴图控制光照计算的结果。制作如下图的ramp贴图,然后对代码进行修改。

  half halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;half ramp =  tex2D(_rampTex, float2(saturate(halfLambert - _ShadowRange), 0.5)).r;half3 diffuse = lerp(_ShadowColor,_MainColor, ramp);

Ramp贴图,从左向右对应光照从0到1的范围。注意最左边光照应该最弱的部分反而设置的比较亮
使用Ramp贴图对色阶进行控制,在暗面形成了一个有点亮的反光

Ramp贴图能够更容易的定义多个色阶,不过贴图需要自己制作。贴图制作起来并不复杂,也可以通过编写编辑器工具来生成。这里的贴图是使用Toony Colors Pro插件生成的。

在制作Ramp贴图的时候,最左边光照应该最弱的部分反而设置的比较亮。这个是为了制作出暗面的反光效果。在素描上面有个明暗五调子的知识,在物体边缘的部分会有一圈反光,所以物体的边缘不会是最暗的部分。链接

素描关系上的三大面五大调子

在图形学上,也有对应的概念,称为菲涅耳(fresnel)现象。我觉得这体现了一个非常有趣的观点,无论是图形学使用光照模型对现实世界的物理现象进行模拟,还是画家们通过观察现实世界总结出的美术理论,最终都是殊途同归的。一个使用公式进行绘图,一个使用画笔进行绘图罢了。

在《偶像大师》系列,也使用了Ramp贴图来实现色阶和色调的控制

左上的贴图是《偶像大师1》的ramp贴图,左下是《偶像大师2》的ramp贴图
《偶像大师1》(左)和《偶像大师2》(右)的画面变化

因为本篇的篇幅有点太长了,有关边缘反光部分的讨论,放在下一篇再详细讨论。

ilmTexture贴图的实现

《GUILTY GEAR Xrd》中使用称为ilmTexture的贴图对角色明暗区域实现手绘风格的控制。其中绿通道控制漫反射的阴影阈值,红通道控制高光强度,蓝通道控制高光范围。这里跟据这个原理,完成一个最简单的实现。卡通渲染不像Lambert等光照模型有统一的公式,如果要更进一步的表现还需要根据画面需求做各种trick。比如专栏标题的展示图片还添加了阴影残留和阴影色调分离的效果,这方面就由大家自己发挥吧。

Shader "Unlit/CelRenderFull"
{Properties{_MainTex ("MainTex", 2D) = "white" {}_IlmTex ("IlmTex", 2D) = "white" {}[Space(20)]_MainColor("Main Color", Color) = (1,1,1)_ShadowColor ("Shadow Color", Color) = (0.7, 0.7, 0.7)_ShadowSmooth("Shadow Smooth", Range(0, 0.03)) = 0.002_ShadowRange ("Shadow Range", Range(0, 1)) = 0.6[Space(20)]_SpecularColor("Specular Color", Color) = (1,1,1)_SpecularRange ("Specular Range",  Range(0, 1)) = 0.9_SpecularMulti ("Specular Multi", Range(0, 1)) = 0.4_SpecularGloss("Sprecular Gloss", Range(0.001, 8)) = 4[Space(20)]_OutlineWidth ("Outline Width", Range(0, 1)) = 0.24_OutLineColor ("OutLine Color", Color) = (0.5,0.5,0.5,1)}SubShader{Pass{Tags { "LightMode"="ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbase#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"sampler2D _MainTex; float4 _MainTex_ST;sampler2D _IlmTex; float4 _IlmTex_ST;half3 _MainColor;half3 _ShadowColor;half _ShadowSmooth;half _ShadowRange;half3 _SpecularColor;half _SpecularRange;half _SpecularMulti;half _SpecularGloss;struct a2v{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL;};struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;   float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2; };v2f vert (a2v v){v2f o = (v2f)0;o.pos = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;return o;}half4 frag (v2f i) : SV_Target{half4 col = 0;half4 mainTex = tex2D (_MainTex, i.uv);half4 ilmTex = tex2D (_IlmTex, i.uv);half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);half3 worldNormal = normalize(i.worldNormal);half3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);half3 diffuse = 0;half halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;half threshold = (halfLambert + ilmTex.g) * 0.5;half ramp = saturate(_ShadowRange  - threshold); ramp =  smoothstep(0, _ShadowSmooth, ramp);diffuse = lerp(_MainColor, _ShadowColor, ramp);diffuse *= mainTex.rgb;half3 specular = 0;half3 halfDir = normalize(worldLightDir + viewDir);half NdotH = max(0, dot(worldNormal, halfDir));half SpecularSize = pow(NdotH, _SpecularGloss);half specularMask = ilmTex.b;if (SpecularSize >= 1 - specularMask * _SpecularRange){specular = _SpecularMulti * (ilmTex.r) * _SpecularColor;}col.rgb = (diffuse + specular) * _LightColor0.rgb;return col;}ENDCG}Pass{//描边,参考上一篇}}FallBack Off
}

上色后的效果

总结

在本节中,介绍了卡通渲染和写实风格渲染的主要区别和一些实现方法。在下一个章节中,将会讨论卡通渲染的边缘光、后处理、以及和PBR进行融合的方向等。

分享

在写这篇文章的ramp贴图部分的时候,想起了以前在OpenGPU论坛看到的Trace大佬翻译的《偶像大师》相关文章。回去找的时候发现论坛已经没了,以前上面的很多文章也丢失了。觉得还是有点伤感的,我最早是看着OpenGPU上面的Trace翻译的科普文章,开始对渲染产生兴趣的。从这一点上来说,我非常感谢在网络上科普图形学知识的前辈们,给了我们非常好的学习途径。后来发现因为我一直有备份ppt的习惯,让我保留了这几篇文章的备份。虽然都很老了,但是觉得不应该消失在网络上面。所以我把这两篇《偶像大师》的文章上传到百度网盘里了,感兴趣的小伙伴可以去下载。 那么我们下一篇见。

百度网盘 提取码:2whi。

附:参考链接

【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(1)

【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(2)

【翻译】西川善司的「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,后篇

寄存器分配图着色_【02】从零开始的卡通渲染-着色篇1相关推荐

  1. UE4从零开始的卡通渲染——阴影篇(二)

    前言 上一篇中我们已经实现了卡通渲染最基本的色阶分离的效果,并且与原生UE渲染效果进行了比较.虽然整体效果是有了但是细节上还是缺少打磨,所以接下来我们看看该如何修改我们的效果 问题 当我们拉近观察人物 ...

  2. unity 3d物体描边效果_从零开始的卡通渲染描边篇

    序言: 一直对卡通渲染非常感兴趣,前后翻找了不少的文档,做了一些工作.前段时间<从零开始>的手游上线了,试着渲染了一下的其中模型,觉得效果很不错.打算写一个专栏记录其中的渲染技术.在后面的 ...

  3. 寄存器分配图着色_寄存器分配

    寄存器分配, 是通过将程序变量尽可能地分配到寄存器,从而提高程序 执行速度的一种方法.寄存器是编译器优化中最为重要的问题之一 (好 的寄存器分配能够提高程序执行速度超过 250% ):也是编译器理论中 ...

  4. 二次元卡通渲染-着色

    前言 本文为"优梦创客"原创文章,您可以自由转载,但必须加入完整的版权声明 更多学习资源请加QQ:1517069595获取(企业级性能优化/热更新/Shader特效/服务器/商业项 ...

  5. 寄存器分配图着色_富士苹果促进着色技术八大要点!是时候看看了!

    大家好,我是果业通.请关注我的公众号,每日为您推送关于果树种植类的技术.资讯内容. 注意影响苹果果形的因素!你知道吗? 找农资?找化肥?买农药?信息都在这里面! 招募联络员!果农都在抢着报名! 富士苹 ...

  6. 深度学习将灰度图着色_通过深度学习为视频着色

    深度学习将灰度图着色 零本地设置/ DeOldify / Colab笔记本 (Zero Local Setup / DeOldify / Colab Notebook) "Haal Kais ...

  7. 苹果分屏软件_必备的优秀软件集合---Mac篇(二)

    系统类集合 PS: 打开链接时,注意墙里墙外 2019年4月29日更新: 即将下撤所有非正版链接,为每个软件增加官网链接,国外软件增加国内正版购买链接 请大家支持正版 2019年1月9号更新: 应大家 ...

  8. ue4cmd怎么调用_[UE4,automation]UE4批渲染cmd篇

    之前做项目的过程中,有一部分工作是在UE4里制作输出小短片.由于要完成的量比较大,所以研究了一些批渲染的方法. 逻辑上跟以前在maya里用batch render差不多,不过UE4这边的设置相对繁琐一 ...

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

    这篇文章讲述的是项目中二次元日式卡通着色渲染用到的一些跟着色相关的技术点. 一.卡通着色 何谓卡通着色?大概是让角色看起来卡通的角色吧.这里说的卡通着色,实际上指的是色阶着色.即根据光照和法线计算出当 ...

最新文章

  1. GPT-4前奏?OpenAI发布120亿参数图像版GPT-3
  2. Java怎么xml拒绝,Sun Java运行时环境XML解析拒绝服务漏洞
  3. Bootstrap3免费单页面模板-Shuffle
  4. typora居中_Windows系统下Typora的安装和语法
  5. vue 实现任意组件之间的通信
  6. 智能安全实验室-全能优化(Guardio)错误解决(1):“出现异常:EXCEPTION_ACCESS_VIOLATION=Access Violation”...
  7. 大淘宝的终极商业阶段
  8. Python基础练习三超市存包柜模拟(优化)
  9. Python高性能计算库—Numba
  10. AS3开发必须掌握的内容
  11. 数字电路实验 04 - | 组合逻辑电路的设计与测试
  12. 电池充电语音警报——隐私政策
  13. 动态规划——贴纸拼词
  14. 项目体系架构设计——基于Spark平台的协同过滤实时电影推荐系统项目系列博客(四)
  15. [C语言]累加器----PTA
  16. 【转】计算机词汇简繁体对照表
  17. 程序猿 or 攻城狮
  18. Densely Connected Convolutional Networks(论文解读三)
  19. 处理2倍图片和3倍图片
  20. Map和对象相互转换

热门文章

  1. dry的原理_速干面料的原理
  2. git rebase --skip_可冒充git大神的git tips
  3. 如何绘制业务架构图 — 3.分解图
  4. 作者:陈婷婷(1986-),女,中国科学院北京基因组研究所生命与健康大数据中心工程师...
  5. 【项目管理】技术债务
  6. 【JavaScript】在JavaScript中使用JSON进行序列化/反序列化操作
  7. cmake 常用变量和常用环境变量
  8. Ubuntu 中将Python3 置为默认版本
  9. Java-Socket实现文件的断点续传
  10. emoji表情过滤处理