卡通渲染这方面,Arc System Works 无疑是我目前见过的做的最好的

“NPR就是各种Trick”

怼 Real-Time Rendering 的第7天,写一篇NPR缓一缓,这几天就是看书,然后忙里偷闲玩几把Gwent,听说移动端快出了,舒服

上一篇关于卡通渲染的文章实现了一些卡通渲染基础操作 梯度漫反射、顶点扩张描边、边缘光、各向异性高光;
本篇继续对卡通渲染基本操作进行补充。

实验素材来自Toony Colors Pro

由程序控制的多梯度

1. 对光影比例的控制

diffuse的经验模型一般都包含,

float3 ndl = max(0,dot(worldNormal, worldLightDir));

我们可以通过一个量来控制光影的比例

float diff = smoothstep(_RampThreshold - ndl, _RampThreshold + ndl, ndl);
2. 对色阶层数控制

“划分”最常用的方法就是 先乘上一个数,再取整

float level = round(diff * _ToonSteps) / _ToonSteps;

这里 / _ToonSteps 目的是 在 0 - 1 范围内梯度

3. 控制色阶之间的平滑程度
float interval = 1 / _ToonSteps;
ramp = interval * smoothstep(level - _RampSmooth * interval * 0.5, level + _RampSmooth * interval * 0.5, diff) + level - interval;
ramp = max(0, ramp);

因为smoothstep本身是平滑差值,当RampSmooth = 1,应该是完全平滑,就是说线性,所以这里可以用step或者if处理一下,建议step

阴影部分的颜色控制

1. 增加颜色系数,控制阴影部分颜色

Toony Colors Pro 2 中的做法是用 “_ShadowColor” 和 “_HightLIghtColor” 差值,把“_ShadowColor”的a通道作为差值系数,控制叠加在片段上的高光和阴影颜色

_SColor = lerp(_HColor, _SColor, _SColor.a);
float3 rampColor = lerp(_SColor.rgb, _HColor.rgb, ramp);
......
float3 diffuse = albedo * lightColor * rampColor;
2.额外的阴影纹理

虽然需要额外的采样,但是可以增加阴影的层次感,对于不同部分添加不同的阴影颜色,美术更容易控制,不用调半天材质

float4 shadowTex = tex2D(_ShadowTex,i.uv);
albedo = lerp(shadowTex.rgb,albedo.rgb,ramp);

对于描边的控制

日式卡渲趋向于基于几何体生成的方法去描边,角色不同部位的描边粗细和颜色往往更容易控制


比如上图中 头发的描边和其他部位描边颜色, 衣服描边宽度的粗细变化,甚至有的地方出现了“断线”。
可以通过额外的纹理控制,充分利用各个通道,NPR没有统一标准,这里举一个例子:
A通道,控制描边的粗细,比如 0.0,就能做出断线的效果
RGB,控制描边颜色
还可以更复杂,比如:
A通道,描边的可见性
R通道,控制 顶点 到 摄像机 的扩张系数,说白了就是根据距离远近控制(乘)描边粗细
G通道,描边的粗细
B通道,可以预留,也可用来做阴影的遮罩,我们一般需要面部或其它一些部位颜色亮一些

局部曝光

曝光是提升视觉效果的常用手段
比如说,我们可以设置边缘光设置更高的强度,根据需求控制人物不同位置的曝光程度。

对边缘光的控制

基本和梯度漫反射一样,可以提供对边缘光面积和平滑程度的控制。原理也基本同上

对于高光的控制

增加额外的控制高光的灰度图或通道,或者各向异性高光,在上一篇文章已经提到

支持多光源的卡通渲染

以前向渲染为前提
除了不同光源的衰减计算之外,在ForwardBase中计算过的东西,是完全搬运到ForwardAdd中,还是有选择性的去掉一些东西。 这取决于Shader的情况和需求。 NPR和BPR在这一方面是完全不同的,效果好看就完事儿了。

举个例子


实验素材来自MiHoYo的崩坏,它的人物渲染和罪恶装备Xrd极为相似。
前几天还原了MiHoYo的一个人物,好像叫白练?,除了头发对于面部的阴影,其他地方大体还原了(头发对面部的阴影按理说也该有了,就是没效果,我以为是暗面计算有问题,可是换成两个片面又没问题了,目前没找出来错误在哪)

顺便提一下,很牛批的软件,就是目前导出图片的时候没Alpha,采样a全是1.0,导致我还得用PS自己加通道,我感觉有必要反馈一下。
MainTex: rgb 颜色, a 是否受光照影响

LightMap:r控制高光颜色, g 控制固定阴影 b 控制高光范围。这是一张RGB图,a没用

高光和阴影控制什么的其实上面也都有提到,就是利用通道控制,只不过用的Trick不一样罢了,很多博主都讲了,我就不再赘述了。

最后奉上一个很普通的卡通渲染Shader

有3个Pass,ForwardBase,ForwardAdd,ShadowCaster。
卡通渲染不一定要OutLine,这个Shader只是用来看着玩,NPR务必根据需求自己 (借鉴+增删改)。

Shader "GaoShuai/MultiLightItem" {Properties {//颜色_Color ("Color", Color) = (1, 1, 1, 1)_HColor ("Highlight Color", Color) = (1.0, 1.0, 1.0, 1.0)_SColor ("Shadow Color", Color) = (0.8, 0.8, 0.8, 1.0)//主纹理_MainTex ("Main Texture", 2D) = "white" {}//梯度_ToonSteps ("Steps of Toon", range(1, 9)) = 2_RampThreshold ("Ramp Threshold", Range(0.1, 1)) = 0.5_RampSmooth ("Ramp Smooth", Range(0, 1)) = 0.1//高光_SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)_SpecSmooth ("Specular Smooth", Range(0, 1)) = 0.1_Shininess ("Shininess", Range(0.001, 10)) = 0.2_Gloss("Gloss",Range(0.0,1.0))=1.0// 边缘光_RimColor ("Rim Color", Color) = (0.8, 0.8, 0.8, 0.6)_RimThreshold ("Rim Threshold", Range(0, 1)) = 0.5_RimSmooth ("Rim Smooth", Range(0, 1)) = 0.1}SubShader {Tags { "RenderType"="Opaque" }Pass {Tags { "LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fog#pragma multi_compile_fwdbase#pragma multi_compile_instancing#pragma enable_d3d11_debug_symbols#include "Lighting.cginc"#include "AutoLight.cginc"float4 _Color;float4 _HColor;float4 _SColor;sampler2D _MainTex;float4 _MainTex_ST;float _RampThreshold;float _RampSmooth;float _ToonSteps;float _SpecSmooth;float _Shininess;float _Gloss;float4 _RimColor;float _RimThreshold;float _RimSmooth;struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;float4 tangent : TANGENT;UNITY_VERTEX_INPUT_INSTANCE_ID}; struct v2f {float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;SHADOW_COORDS(3)UNITY_FOG_COORDS(4)};UNITY_INSTANCING_BUFFER_START(Props)UNITY_INSTANCING_BUFFER_END(Props)v2f vert (a2v v) {v2f o;UNITY_SETUP_INSTANCE_ID(v);o.pos = UnityObjectToClipPos( v.vertex);o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);o.worldNormal  = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;TRANSFER_SHADOW(o);UNITY_TRANSFER_FOG(o,o.pos);return o;}float4 frag(v2f i) : SV_Target { float3 worldNormal = normalize(i.worldNormal);float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));float3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));float3 lightColor = _LightColor0.rgb;float4 c = tex2D (_MainTex, i.uv);float3 albedo = c.rgb * _Color.rgb;float Alpha = c.a*_Color.a;float Specular = _Shininess;UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);float3 floatDir = normalize(worldLightDir + worldViewDir);float3 ndl = max(0,dot(worldNormal, worldLightDir));      //光照系数float ndh = max(0, dot(worldNormal, floatDir));          //高光系数float ndv = max(0, dot(worldNormal, worldViewDir));     //边缘光系数//控制光影比例float diff = smoothstep(_RampThreshold - ndl, _RampThreshold + ndl, ndl);float interval = 1 / _ToonSteps;//简化颜色,划分色阶float level = round(diff * _ToonSteps) / _ToonSteps;float ramp ;//平滑过渡色阶ramp = interval * smoothstep(level - _RampSmooth * interval * 0.5, level + _RampSmooth * interval * 0.5, diff) + level - interval;ramp = max(0, ramp);ramp *= atten;//使用颜色叠加,对高光部分和阴影部分叠色_SColor = lerp(_HColor, _SColor, _SColor.a);float3 rampColor = lerp(_SColor.rgb, _HColor.rgb, ramp);//高光计算float spec = pow(ndh, Specular * 128.0) * _Gloss;spec *= atten;spec = smoothstep(0.5 - _SpecSmooth * 0.5, 0.5 + _SpecSmooth * 0.5, spec);//边缘光计算float rim = (1.0 - ndv) * ndl;rim *= atten;rim = smoothstep(_RimThreshold - _RimSmooth * 0.5, _RimThreshold + _RimSmooth * 0.5, rim);float4 color;float3 diffuse = albedo * lightColor * rampColor; //叠加颜色float3 specular = _SpecColor.rgb * lightColor * spec;float3 rimColor = _RimColor.rgb * lightColor * _RimColor.a * rim;color.rgb = diffuse + specular + rimColor;color.a = Alpha;UNITY_APPLY_FOG(i.fogCoord, color);return color;}ENDCG}Pass {Tags { "LightMode"="ForwardAdd" }Blend One OneCGPROGRAM#pragma multi_compile_fwdadd    #pragma multi_compile_instancing#pragma enable_d3d11_debug_symbols      #pragma vertex vert#pragma fragment frag#include "Lighting.cginc"#include "AutoLight.cginc"float4 _Color;float4 _HColor;float4 _SColor;sampler2D _MainTex;float4 _MainTex_ST;float _RampThreshold;float _RampSmooth;float _ToonSteps;float _SpecSmooth;float _Shininess;float _Gloss;float4 _RimColor;float _RimThreshold;float _RimSmooth;struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;float4 tangent : TANGENT;UNITY_VERTEX_INPUT_INSTANCE_ID}; struct v2f {float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;SHADOW_COORDS(3)};v2f vert (a2v v) {v2f o;o.pos = UnityObjectToClipPos( v.vertex);o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);o.worldNormal  = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;TRANSFER_SHADOW(o);return o;}UNITY_INSTANCING_BUFFER_START(Props)UNITY_INSTANCING_BUFFER_END(Props)float4 frag(v2f i) : SV_Target { float3 worldNormal = normalize(i.worldNormal);#ifdef USING_DIRECTIONAL_LIGHTfixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);#elsefixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);#endiffloat3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));float3 lightColor = _LightColor0.rgb;float4 c = tex2D (_MainTex, i.uv);float3 albedo = c.rgb * _Color.rgb;float Alpha = c.a*_Color.a;float Specular = _Shininess;#ifdef USING_DIRECTIONAL_LIGHTfixed atten = 1.0;#else#if defined (POINT)float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;#elif defined (SPOT)float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;#elsefixed atten = 1.0;#endif#endiffloat3 floatDir = normalize(worldLightDir + worldViewDir);float3 ndl = max(0,dot(worldNormal, worldLightDir));      float ndh = max(0, dot(worldNormal, floatDir));           float diff = smoothstep(_RampThreshold - ndl, _RampThreshold + ndl, ndl);float interval = 1 / _ToonSteps;float level = round(diff * _ToonSteps) / _ToonSteps;   float ramp = interval * smoothstep(level - _RampSmooth * interval * 0.5, level + _RampSmooth * interval * 0.5, diff) + level - interval;ramp = max(0, ramp);  ramp *=atten;float spec = pow(ndh, Specular * 128.0) * _Gloss;spec *= atten;spec = smoothstep(0.5 - _SpecSmooth * 0.5, 0.5 + _SpecSmooth * 0.5, spec);float4 color;float3 diffuse = albedo * lightColor * ramp;float3 specular = _SpecColor.rgb * lightColor * spec;color.rgb = specular + diffuse;color.a = Alpha;return color;}ENDCG}pass {Tags{ "LightMode" = "ShadowCaster"}CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_shadowcaster#pragma enable_d3d11_debug_symbols#include "UnityCG.cginc"struct v2f{V2F_SHADOW_CASTER;};v2f vert(appdata_full v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)return o;}float4 frag(v2f o) :SV_Target{SHADOW_CASTER_FRAGMENT(o)}ENDCG}}
}

人物渲染篇(二) —— 基础卡通渲染 下相关推荐

  1. java rabbitmq 并发_RabbitMQ消息中间件 高级篇二 高并发情况下保障消息投递可靠性...

    RabbitMQ消息中间件技术精讲9 高级篇二 高并发场景下,消息的延迟投递做二次确认进行回调检查来保障生产者消息投递成功的可靠性 在上一篇文章中,我们介绍了BAT大厂中一种方式保障生成者消息投递可靠 ...

  2. 小米平板2无线网卡服务器,二手平板踩坑 篇二:400多收下小米平板2——屏幕通病有点要命...

    二手平板踩坑 篇二:400多收下小米平板2--屏幕通病有点要命 2020-04-11 21:34:22 2点赞 6收藏 7评论 又是一次踩坑,这个平板我总体感觉还是不错的,屏幕有点难受 价格就不说了, ...

  3. 人物渲染篇(一) —— 基础卡通渲染 上

    人物渲染篇开篇,暂停下手边的Java Web,决定开始练习角色的渲染,以后会尝试更多的效果 shaderlab编写前后对比: 这一次用到的知识点复习&总结 梯度漫反射 float3 diffu ...

  4. 「UnityShader笔记」08. 基础卡通渲染—渐变纹理

    Part1.效果图 Part2.方法简介 渐变纹理是一种可以用来实现卡通渲染效果的技术,其原理十分简单,使用光照模型计算结果,在一个一维的渐变纹理上进行采样 以半兰伯特模型为例,其表达式为 0.5 * ...

  5. 渲染篇二:知己知彼——解锁浏览器背后的运行机制

    知己知彼--解锁浏览器背后的运行机制 从本章开始,我们的性能优化探险也正式进入到了"深水区"--浏览器端的性能优化. 平时我们几乎每天都在和浏览器打交道,在一些兼容任务比较繁重的团 ...

  6. Unity下的日式卡通渲染实现-阴影篇(二)

    这边文章讲述的是项目中用到的一些卡通渲染阴影相关技术. 一.SDF面部阴影 SDF这个概念具体是什么意思了?可以去观看闫令琪在B站上的101课程,有一节专门讲述了SDF的定义和混合SDF能够产生什么效 ...

  7. NPR——卡通渲染(三)

    NPR--卡通渲染 1.1 Illustrative Rendering in Team Fortress 2 [1] 1.1.1 <军团要塞2>插画风格抽象描述 1.1.2 视觉无关的光 ...

  8. 卡通渲染及其相关技术总结

    原文链接https://blog.uwa4d.com/archives/usparkle_cartoonshading.html 这是侑虎科技第246篇原创文章,感谢作者洛城供稿,欢迎转发分享,未经作 ...

  9. Unity Shader - Simple Toon Shading - 简单卡通渲染

    文章目录 最终效果 - Final Effect 无光照,只有纹理与主色调 Shader 加描边 - Outline GIF Shader 添加光影 - RecieveShadow 自身接收阴影 Sh ...

  10. 二次元卡通渲染之描边

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

最新文章

  1. 疫情下跨境电商逆势增长,大数据告诉你如何抓住跨境网购新趋势
  2. Angular中实现路由跳转并通过get方式传递参数
  3. Linux:如何获取打开文件和文件描述符数量
  4. c语言整行乘非整形等于什么意思,C语言--整形升级寻常算术转换
  5. 「权威发布」2019年大学生电子设计竞赛获奖名单
  6. 可视化编码_编码:可视化位图
  7. c语言第六章条件型循环结构,C语言课件(第六章 循环结构)
  8. ubuntu php命令行模式,在命令行ubuntu 16.04上切换php版本
  9. 打开微信键盘自动弹出_微信一打开就弹出键盘 微信打字键盘怎么恢复
  10. Pytorch测试模型的GFLOPs和Param大小
  11. 干细胞研究最新进展(2022年4月)
  12. 电脑桌面的计算机图双击打不开,win10双击电脑图标打不开必须右键打开
  13. 无线局域网和蜂窝移动网络_为什么 iPhone 的数据流量叫做「蜂窝移动网络」?...
  14. 常用的dede标签小总结
  15. Hamming Distance (汉明距离)
  16. 【设计模式】单例模式是什么?如何实现单例模式?单例模式常见问题?
  17. 小观插值逼近的龙格现象
  18. 关键帧与地图点(二):关键帧
  19. java入门习题,3000米长的绳子,每天减一半,问多少天这个绳子会小于5米?不考虑小数。
  20. 移动办公oa管理软件的五大选择要素有哪些?

热门文章

  1. 基于Html+Css+javascript的网页制作——音乐舞蹈学校(5页面) 计算机毕设网页设计源码
  2. paypal php 对接
  3. 戴尔笔记本无线网络无法连接
  4. 推荐几个常用常玩的小游戏网址包括4399.com
  5. python命令行进入帮助模式_python的help()用法
  6. linux 快照工具,技术预览:CentOS 7中利用Snapper GUI管理系统快照
  7. layim之刷新群聊列表
  8. Failed to load resource: the server responded with a status of 404 (Not Found) favicon.ico文件找不到
  9. Ps|无规律渐变效果
  10. 统计学---总体与样本