温馨提示:

本系列文章面向那些 Shader 刚刚入门,想寻求进一步提升的群体,如果对 Shader 一无所知的话,建议自行搜索其他 Shader入门教程观看学习,再食用本系列文章。

前言:

说起卡通渲染,就不得不提 《塞尔达:荒野之息》。

《塞尔达:荒野之息》可谓 2017 年的神作了,击败了众多 3A 大作,成为了当年的年度游戏。其采用的卡通渲染的美术风格也算是一大亮点(也可能是 Wii U 和 Switch 机能限制所致)。

当年想模仿一下它的风格,可惜技术捉急…… 如今 Shader 神功已有小成,就想着尝试一下。

因为主要在移动端开发,因此本系列文章都会采用 Vertex & Fragment Shader,非常纯净。

今后可能还会做一些其他风格的卡通渲染,不过目前就先以《塞尔达:荒野之息》的风格作为起点吧!

话不多说,先打开游戏,截个图作参考:

由图可见,塞尔达荒野之息的卡通渲染十分简单明快,主要有三个要点:亮部、暗部、边缘光

当然仔细看的话,头发是有高光且有特殊处理的,不过这篇如题 “简易版”,就不考虑那么多了,先把上边这三点做完。

一、准备工作

首先,在目录下新建一个 Unlit Shader。

获取三个常用素材,法线 N,光照方向 L,视角方向 V。

熟悉 Shader 的一定知道,这里就不多说了,直接贴代码:

struct appdata
{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL;
};struct v2f
{float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;UNITY_FOG_COORDS(3)float4 vertex : SV_POSITION;
};v2f vert (appdata v)
{...o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;...
}fixed4 frag (v2f i) : SV_Target
{...fixed3 worldNormal = normalize(i.worldNormal); //法线 Nfixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); //光照方向 Lfixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); //视角方向 V...
}

二、亮部、暗部

已知法线 N,光照方向 L。可用 N · L 来区分亮部暗部

若值小于0,则背光,判断为暗部,若值大于0,则迎光,判断为亮部。

当然也可以自己定义阈值,不一定要为0。

fixed diffValue = dot(worldNormal, worldLightDir);
fixed diffStep = step(_ShadowThreshold, diffValue);

三、边缘光

已知法线 N,视角方向 V。可用 N · V 的值来区分是否是物体边缘,

值越接近零,N 与 V 越接近垂直,则是边缘。这里多加了一个 Pow 计算,防止出现整片的边缘光,个人感觉效果较好。

(这边没使用参考文章里的做法,感觉效果不好)

另外记得背光面不用边缘光;乘以 0.5 是希望不要过曝,可以带点原本贴图的颜色。

fixed rimValue = pow(1 - dot(worldNormal, worldViewDir), _RimPower);
fixed rimStep = step(_RimThreshold, rimValue);
fixed4 rim = rimStep * 0.5 * diffStep * _RimColor;

四、其他

一些简单的光照参数还是要的,这样直接调 Direction Light 颜色就能影响所有模型,做场景气氛会很有用。

光照这边模仿一下半兰伯特(会明亮一点,光照强度 0 时不会全黑)。

不过这边不打算加环境光,因为很容易过曝,不好调色,想加的同学自己加就好。

fixed4 light = _LightColor0 * 0.5 + 0.5;
fixed4 diffuse = light * col * (diffStep + (1 - diffStep) * _ShadowBrightness) * _Color;
...
fixed4 rim = light * rimStep * 0.5 * diffStep * _RimColor;

五、成果

在 Asset Store 里下了个免费的小姐姐模型(搜索 Anime Girl Idle Animations Free),换上 Shader 看下效果:

虽然很粗糙,但还是有那么点感觉的。其他的视觉效果优化以后再写吧,毕竟本篇是“简易版”

完整 Shader 代码如下,谢谢观赏!~(欢迎各位观众给出宝贵意见)

Shader "Custom/ToonShadingSimple"
{Properties{[Header(Main)]_MainTex ("Texture", 2D) = "white" {}_Color ("Color", Color) = (1.0, 1.0, 1.0, 1.0)_RimColor ("RimColor", Color) = (1.0, 1.0, 1.0, 1.0)_ShadowThreshold ("ShadowThreshold", Range(-1.0, 1.0)) = 0.2_ShadowBrightness ("ShadowBrightness", Range(0.0, 1.0)) = 0.6_RimThreshold ("RimThreshold", Range(0.0, 1.0)) = 0.35_RimPower ("RimPower", Range(0.0, 16)) = 4.0}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass{Cull BackTags { "LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"#include "Lighting.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL;};struct v2f{float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;UNITY_FOG_COORDS(3)float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;fixed4 _RimColor;fixed _ShadowThreshold;fixed _ShadowBrightness;fixed _RimThreshold;half _RimPower;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;UNITY_TRANSFER_FOG(o,o.vertex);return o;}fixed4 frag (v2f i) : SV_Target{fixed3 worldNormal = normalize(i.worldNormal); //法线 Nfixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); //光照方向 Lfixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); //视角方向 V// sample the texturefixed4 col = tex2D(_MainTex, i.uv); fixed diffValue = dot(worldNormal, worldLightDir);fixed diffStep = step(_ShadowThreshold, diffValue);fixed4 light = _LightColor0 * 0.5 + 0.5;fixed4 diffuse = light * col * (diffStep + (1 - diffStep) * _ShadowBrightness) * _Color;// 模仿参考文章的方法,感觉效果不是太好// fixed rimValue = 1 - dot(worldNormal, worldViewDir);// fixed rimStep = step(_RimThreshold, rimValue * pow(dot(worldNormal,worldLightDir), _RimPower));fixed rimValue = pow(1 - dot(worldNormal, worldViewDir), _RimPower);fixed rimStep = step(_RimThreshold, rimValue);fixed4 rim = light * rimStep * 0.5 * diffStep * _RimColor;fixed4 final = diffuse + rim;// apply fogUNITY_APPLY_FOG(i.fogCoord, final);return  final;}ENDCG}}
}

下一篇传送门:

https://blog.csdn.net/qq_27534999/article/details/100925621

参考资料:

1、https://roystan.net/articles/toon-shader.html

2、《Unity Shader 入门精要》

Unity Shader 卡通渲染 (一):仿塞尔达荒野之息 Shader(简易版)相关推荐

  1. Unity Shader 卡通渲染 (三):仿塞尔达荒野之息 Shader(顶点色控制细节)

    上一篇传送门: https://blog.csdn.net/qq_27534999/article/details/100925621 顶点色在卡通渲染中有挺多应用,本篇会在上一篇的基础上,运用模型顶 ...

  2. RenderDoc塞尔达荒野之息抓帧分析

    RenderDoc是一种抓帧工具,主要用来分析游戏开发中渲染流程,官网: https://renderdoc.org/ 我是用的版本是RenderDoc_1.14_64.zip 塞尔达荒野之息使用Ce ...

  3. 塞尔达荒野之息vs艾尔登法环

    玩了荒野之息(switch版本)再去玩艾尔登法环,感觉艾尔登法环完全没有外界宣扬的那么好.我是七彩虹3060显卡,特效全开了,一开始场景还比较精致的,后面也很多场景比较粗糙. 然后就是感觉这个人物很笨 ...

  4. 分享几张塞尔达荒野之息精彩壁纸

    分享几张塞尔达荒野之息精彩壁纸 001-荒野之息经典封面.jpg 002-林克醒来看向海拉陆大陆经典场面.jpg 003-林克射箭特写.jpg 004-海拉鲁荒野之息.jpg 005-林克攀爬峭壁.j ...

  5. 塞尔达amiibo_塞尔达荒野之息pC版(附带全Amiibo)安装教程,最无敌的游戏

    点击上方「蓝字」关注我们 给你最好的 <塞尔达传说:荒野之息(The Legend of Zelda: Breath of the Wild)>是任天堂旗下经典角色扮演游戏系列<塞尔 ...

  6. Unity 程序化动画:还原塞尔达旷野之息 守护者 (六足)

    又整了个小Demo,感觉程序化动画还挺好玩.先上效果图,使用到的所有模型均来源于网络. 程序化动画生成守护者移动 实现思路:守护者共6条腿,初始化先激活两条腿14可移动,每移动完一条腿顺序激活下一条腿 ...

  7. Unity UI xlua 热更:还原塞尔达旷野之息 (持续更新:已补充箭头动效)

    整了个小Demo仿照<塞尔达传说:旷野之息>,实现 鼠标悬停在Button上时,能够改变Button-Text颜色,并且在Button前显示一个小箭头 标题鼠标指针悬停和移走,改变标题颜色 ...

  8. 微信小程序前端页面Demo系列之仿塞尔达攻略助手首页

    前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出-   自我介绍 ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计 ...

  9. Unity Shader卡通渲染 · 高清渲染管线·HDRP

    Unity Shader卡通渲染 · 高清渲染管线·HDRP 前言 最近在研究HDRP管线中的卡通渲染,就想着能不能把官方的UCTS移植到HDRP管线里面去,说干就干,到昨天晚上上传了github,今 ...

最新文章

  1. java项目经理也就那么回事_网易PM | 我们之前在需求评审环节踩过的坑...
  2. Java多线程编写简易飞机大战(一)
  3. Winform中实现执行cmd命令的工具类
  4. 设计模式理解:工厂模式,抽象工厂,原型方法
  5. 连接mysql次数_MySQL通过CONNECTION_CONTROL限制连接次数
  6. ABP Framework:移除 EF Core Migrations 项目,统一数据上下文
  7. chromedriver : Saving to 安装卡住 解决
  8. python模拟qq空间登录_python selenium模拟登录163邮箱和QQ空间
  9. 【Python系列】之python2.7.6离线安装Matplotlib
  10. 10,求一个double型数据base的整数次方《剑指offer》
  11. 雅可比矩阵与海森矩阵
  12. 华为鸿蒙os logo,华为鸿蒙 OS Logo :Powered by HarmonyOS
  13. 文档大小超出上传限制怎么办_有道翻译和翻译狗,哪个更适合翻译文档?
  14. 国庆车流激增,南京启用无人机报路况
  15. 谷歌浏览器打印不弹出预览直接打印机打印的方法
  16. 监控平台设计 之 Graphite、Prometheus 竞对
  17. 硕士学位数据分析师工资_值得拥有数据科学方面的硕士学位
  18. 生活品质-装修(01)马桶怎么选?
  19. “易+”开源网易易盾 GameSentry 正式开源,做游戏安全保障的尖兵利刃
  20. shell之未找到命令

热门文章

  1. 【LeetCode 剑指 Offer 65】不用加减乘除做加法
  2. python,基于http协议,最常用的是GET和POST两种方法
  3. OpenFlow 协议详解(干货)
  4. Android6.0危险权限列表
  5. 字节码操控框架ASM - 初识
  6. [MySQL] like “%XX“ 和 like “XX%“ 的特殊情况
  7. Nodejs excel(.xlsx) 文件的读写
  8. 人民日报推荐:极简主义生活方式
  9. Go singleflight
  10. Android 天气预报(2)