Unity Shader 卡通渲染 (五):仿日式赛璐珞风格 Shader(顶点外扩描边)
上一篇传送门:
https://blog.csdn.net/qq_27534999/article/details/101080649
一、赛璐璐风格简介
有些人可能会问,什么是赛璐珞风格?
赛璐珞是一种合成树脂。最早在制作动画的时候,美国开始使用以赛璐珞制作的透明胶片,把人物画在胶片上面,背景画在纸上面,就可以做到很多不同的效果。
采用赛璐珞来制作的动画也叫“做赛璐珞动画”,在很长一个时期里面几乎是“手绘动画”的代名词。因为现在已经不用赛璐珞制作动画了,所以现在通常指有线稿和颜色的风格。以分层的方式作画(线稿层、明暗层、上色层、特效层)。
当然,具体风格依照动画不同,还是有所差异,这里仅挑选一个最经典的赛璐珞风格进行实现。
为了更直观的说明,这里以经典动画《星际牛仔》为例:
实际去看动画的话,可以发现人物有几个特征:
①黑色描边,较细且粗细均匀
②颜色平涂,并分为暗部亮部,暗部颜色根据环境不同会有所变化
③头发、金属、皮革等特殊部分会有高光、边缘光
④内部结构线、衬线
其中,第④点在本篇中暂时无法解决,一般的做法是直接画贴图上,不过会比较模糊,更好的办法以后再说吧嘻嘻 ~
首先,本篇会在前几篇的基础上改进 Shader 以实现效果,这里先上最终的效果图:
这边模型和背景比较简陋,可能效果不是很好,如果换更好的模型和背景的话效果会挺惊艳,个人感觉这种风格需要优秀美术资源配合才行,比如下图这样,也是用的相同 Shader:
有一点需要注意的是,这类赛璐珞风格渲染纹理不需要不要过多的细节,有时候多用单色色块反而效果会更好。
话不多说,那么接下来开始对之前的 Shader进行改造吧!
二、添加描边
为了方便观察分析,我们还是用之前那个简陋的模型(初学者专用),到上一篇为止的效果如下图所示:
在上一篇 Shader 的基础上,我们来添加描边,这里用一种比较经典的描边方法——顶点外扩(又称法线外扩、Shell 法)。
顶点外扩的原理就是新增一个 Pass,这个 Pass 将模型的顶点沿着法线向外移动一定距离,然后用 Cull Front 剔除正面,残留下来的面正好作为描边。另外,为了让描边在远近都能保持粗细均匀,我们还需要添加一些针对相机距离的计算。
性能优化注意:描边 Pass 务必放在 原 Pass 之后,这样就可以享受 Early-Z 带来的优化,防止造成 OverDraw!
该 Pass 代码具体如下:
Pass
{ Cull FrontCGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL;};struct v2f{float2 uv : TEXCOORD0;UNITY_FOG_COORDS(1)float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;float _Outline;fixed4 _OutlineColor;v2f vert (appdata v){v2f o;//计算与相机的距离,用来保持描边粗细程度float3 posView = mul(UNITY_MATRIX_MV,v.vertex).xyz;float dis = length(posView);float3 normal = v.normal;//顶点沿法线挤出v.vertex = v.vertex + float4(normalize(normal), 0) * _Outline * dis * 0.01;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);UNITY_TRANSFER_FOG(o,o.vertex);return o;}fixed4 frag (v2f i) : SV_Target{// apply fogUNITY_APPLY_FOG(i.fogCoord, col);return _OutlineColor;}ENDCG
}
效果如下:
三、高光
参考《星际牛仔》的赛璐珞风格,我们不想在皮肤、衣物上有高光,这里可以调整顶点色(详见第三篇)或参数来控制细节,去除皮肤、衣物的高光,或者给金属材质加上高光,,这里就不再赘述。
第三篇顶点色细节控制传送门:https://blog.csdn.net/qq_27534999/article/details/100985558
去掉了大部分的边缘光,只保留鞋子部分的,调整后效果如下:
貌似没有原来好看,但为了还原赛璐珞的平涂风格,先这样吧。
四、阴影色
目前为止阴影色只是单纯的降低明度,然而有时候我们需要让阴影色根据环境来改变,这里有几个办法:
1、直接定义一个阴影色属性:
比较简单,就不多说了,将原来的 Shadow Brightness 阴影亮度换成阴影色即可。
2、根据 Lightning Settings 环境光参数进行调整:
#include "Lighting.cginc"
...
//使用Gradient
fixed3 abientSky = unity_AmbientSky.rgb;
fixed3 abientEquator = unity_AmbientEquator.rgb;
fixed3 abientGround = unity_AmbientGround.rgb;
//使用Color
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
然后将 Shadow Brightness 阴影亮度换成相应变量即可。
调整后效果如下:
五、成果
另外,开头的另一张效果图,资源可在 Asset Store 免费下(TaichiCharacterPack)。
使用的是相同的Shader(没用顶点色控制),有兴趣的同学可以自己试试:
按惯例,放出完整 Shader 代码如下:
(直接用的话整个模型可能变得全白,这可能是因为模型没有刷顶点色,将 Shader 中 i.color 相关计算删除即可)
Shader "Custom/ToonShadingSimple_v4_SimpleCelluloid"
{Properties{[Header(Main)]_MainTex ("Texture", 2D) = "white" {}_Color ("Color", Color) = (1.0, 1.0, 1.0, 1.0)_ShadowColor ("ShadowColor", 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//赛璐珞风格通常情况不使用边缘光,RimThreshold 可默认为1_RimThreshold ("RimThreshold", Range(0.0, 1.0)) = 1_RimPower ("RimPower", Range(0.0, 16)) = 4.0_Specular ("Specular", Color) = (1, 1, 1, 1)_SpecularScale("Specular Scale", Range(0, 0.1)) = 0.02_EdgeSmoothness("Edge Smoothness", Range(0,2)) = 2_Outline("Outline",Range(0,1))=0.1_OutlineColor("OutlineColor",Color)=(0,0,0,1)}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;fixed4 color : COLOR;};struct v2f{float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;UNITY_FOG_COORDS(3)float4 vertex : SV_POSITION;fixed4 color : COLOR;};sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;fixed4 _ShadowColor;fixed4 _RimColor;fixed _ShadowThreshold;fixed _RimThreshold;half _RimPower;half _EdgeSmoothness;fixed4 _Specular;fixed _SpecularScale;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;o.color = v.color;UNITY_TRANSFER_FOG(o,o.vertex);return o;}fixed4 frag (v2f i) : SV_Target{//return i.color;fixed3 worldNormal = normalize(i.worldNormal); //法线 Nfixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); //光照方向 Lfixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); //视角方向 Vfixed3 worldHalfDir = normalize(worldLightDir + worldViewDir); //高光计算用// sample the texturefixed4 col = tex2D(_MainTex, i.uv); fixed spec = dot(worldNormal, worldHalfDir)+(i.color.g-0.5)*2;// w值也可用一个较小的值代替,效果差别不大fixed w = fwidth(spec)*_EdgeSmoothness;fixed4 specular = _Specular * lerp(0,1,smoothstep(-w, w, spec+_SpecularScale-1)) * step(0.001, _SpecularScale);fixed diffValue = dot(worldNormal, worldLightDir)+(i.color.r-0.5)*4;fixed diffStep = smoothstep(-w+_ShadowThreshold, w+_ShadowThreshold, diffValue);fixed4 light = _LightColor0 * 0.5 + 0.5;fixed4 diffuse = light * col * (diffStep + (1 - diffStep) * _ShadowColor) * _Color;// 模仿参考文章的方法,感觉效果不是太好// fixed rimValue = 1 - dot(worldNormal, worldViewDir);// fixed rimStep = step(_RimThreshold, rimValue * pow(dot(worldNormal,worldLightDir), _RimPower));fixed rimValue = pow(1 - dot(worldNormal, worldViewDir)+(i.color.b-0.5)*2, _RimPower);fixed rimStep = smoothstep(-w+_RimThreshold, w+_RimThreshold, rimValue);fixed4 rim = light * rimStep * 0.5 * diffStep * _RimColor;fixed4 final = diffuse + rim + specular;// apply fogUNITY_APPLY_FOG(i.fogCoord, final);return final;}ENDCG}//注意,描边 Pass 放后边,可享受 Early-Z 优化Pass{ Cull FrontCGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL;};struct v2f{float2 uv : TEXCOORD0;UNITY_FOG_COORDS(1)float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;float _Outline;fixed4 _OutlineColor;v2f vert (appdata v){v2f o;//计算与相机的距离,用来保持描边粗细程度float3 posView = mul(UNITY_MATRIX_MV,v.vertex).xyz;float dis = length(posView);float3 normal = v.normal;//顶点沿法线挤出v.vertex = v.vertex + float4(normalize(normal), 0) * _Outline * dis * 0.01;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);UNITY_TRANSFER_FOG(o,o.vertex);return o;}fixed4 frag (v2f i) : SV_Target{// apply fogUNITY_APPLY_FOG(i.fogCoord, col);return _OutlineColor;}ENDCG}}
}
下一篇传送门:
下一篇还没想好要写啥,先这么放着吧
参考文章:
1、萌娘百科 - 赛璐珞
https://zh.moegirl.org/%E8%B5%9B%E7%92%90%E7%8F%9E
2、【NPR】漫谈轮廓线的渲染
https://blog.csdn.net/candycat1992/article/details/45577749
Unity Shader 卡通渲染 (五):仿日式赛璐珞风格 Shader(顶点外扩描边)相关推荐
- Unity Shader 卡通渲染 实时模型动画描边的研究
前言 卡通渲染也叫非真实感渲染(英文简写:NPR),"描边"在图形学和数字图像里都叫边缘检测.因此你可以在很多文献网站上面找到很多这类文献,但最后我发现基于图形学使用的方式基本都是 ...
- Unity Shader 卡通渲染 模型描边之退化四边形
目录 前言 一.基于空间的边缘检测算法 二.退化四边形 三.Unity中的CommandBuffer和ComputeBuffer 四.构成描边的简单实例 五.模型描边的实现 前言 之前写了一篇< ...
- Unity Shader卡通渲染 · 高清渲染管线·HDRP
Unity Shader卡通渲染 · 高清渲染管线·HDRP 前言 最近在研究HDRP管线中的卡通渲染,就想着能不能把官方的UCTS移植到HDRP管线里面去,说干就干,到昨天晚上上传了github,今 ...
- 新房装修的简约日式装修风格技巧和装修攻略
日式装修风格是现在极为流行的一种装修风格,因为它注重自然与简约,大量使用浅色和原木色,非常符合现在90后的一种审美标准,即减法就等于加法.日式装修风格也分为很多种,今天佛山装修公司的小编分享的是简约日 ...
- 3.顶点外扩方法实现的描边shader
描边shader的实现有很多种,顶点外扩是其中之一.顶点外扩的原理是用2个Pass 渲染物体2次 第一遍:描边,顶点沿法线方向外拓后用黑色渲染.外扩这一步的实现是在投影空间,也就是2D的,根绝法线的x ...
- Unity Shader 卡通渲染 (三):仿塞尔达荒野之息 Shader(顶点色控制细节)
上一篇传送门: https://blog.csdn.net/qq_27534999/article/details/100925621 顶点色在卡通渲染中有挺多应用,本篇会在上一篇的基础上,运用模型顶 ...
- Unity Shader 卡通渲染 (一):仿塞尔达荒野之息 Shader(简易版)
温馨提示: 本系列文章面向那些 Shader 刚刚入门,想寻求进一步提升的群体,如果对 Shader 一无所知的话,建议自行搜索其他 Shader入门教程观看学习,再食用本系列文章. 前言: 说起卡通 ...
- unity shader卡通渲染(描边)+阴影+多光源处理
说道卡通渲染,应该都会想到描边: 我所学的描边有三种: 一种是计算边缘深度检测描边 一种是色差检测描边 一种是利用顶点法线向外扩展返回单色pass,使用正面裁剪 我用的第三种: pass {//剔除前 ...
- [Unity Shader]卡通渲染
最终效果,包含描边,明暗双色阶的渲染,边缘光. 参考来源:https://www.zhihu.com/column/c_1215952152252121088 卡通渲染 Back facing描边 光 ...
最新文章
- 数据结构-单链表(C语言代码)
- 全球及中国皮革和纺织品用甲酸行业竞争调查分析及投资规划报告2021年版
- 计算机网络是如何通信的【二】
- 一个命令kill所有符合条件的进程
- [BUUCTF-pwn]——[ZJCTF 2019]Login
- 弟子规python编程游戏_《Python游戏趣味编程》 第11章 消灭星星
- Linux关机运行的脚本,Linux 关机 脚本
- Vuex原来可以这样上手
- UI设计灵感|有声读物APP界面设计
- java中properties类_Java中的Properties类详解
- 惠普暗影精灵3清灰_惠普暗影精灵15评测:速度超快,价格适中|但问题却不少...
- 把握这两点,抢占下一个电商风口|2016最新中国电商App排名研究报告
- 基于知识图谱的智能问答机器人技术架构
- python tkinter label标签_Python Tkinter详解 (二)Label标签的使用
- 李嘉诚:成功创业要具备的五个要素
- 最小生成树之Prim(普里姆)算法
- 《敏捷整洁之道 回归本源》阅读表述
- 深度细节 | Go 的 panic 的秘密都在这
- 批量上传图片_微信公众号关键词回复图片突破200条规则,怎么设置?
- winxp系统连接服务器丢包解决方法
热门文章
- 2022茶艺师(中级)试题及答案
- CentOS 固定 IP 地址
- 用于土地使用和土地覆盖分类的landsat8的OLI波段组合的选择
- spooling 技术工作原理
- 使用python爬取电影下载地址并使用transmissionrpc下载
- 【Python】爬取金庸射雕英雄传连载版以及金庸作品里所有江湖门派
- 世界是如何由计算机代码运行的(原文见http://www.bbc.co.uk/timelines/zxsrcdm)
- 部署并安装Discuz论坛(首先搭建LAMP环境Apache+MySQL+PHP)
- Matlab论文插图绘制模板第31期—堆叠折线图(stackedplot)
- 百度音乐2013 8.2.8 去广告VIP绿色版|zd423作品