赞
踩
卡通材质
卡通着色也叫Non-photorealisticrendering非真实渲染,通常一些3D游戏用来做一些卡通风格的游戏,一般来说特点主要有两点,一是描边,二是风格化着色,表现为明暗渐变过渡为非线性梯度着色。如下图所示:火影忍者和无主之地
先做描边
关于描边网上有不少资料,这里就其中比较常用的Rim lighting和法线外拓来实现;
1.Rim lighting
原理在之前XRAY材质有提到过,通过视线和物体法线夹角的点积来获得轮廓。只不过这里我需要做一个线条比较硬一点的描边,因此需要对获取到的轮廓做个取整:
i.color=floor(i.color*2);
另外外轮廓的颜色需要做lerp处理而不是乘法叠加,否则黑色叠上去是看不到的;
col=lerp(col,i.color*_OutlineColor,i.color);
VF版本代码01:
- Shader "PengLu/Toon/OutlineOnePassVF" {
- Properties {
- _MainTex ("Base (RGB)", 2D) = "white" {}
- _OutlineColor("OutlineColor",Color) = (0,1,1,1)
- _RimPower ("Rim Power", Range(0.1,8.0)) = 2
-
- }
- SubShader {
- Tags { "Queue"="Geometry" "RenderType"="Opaque" }
- LOD 200
-
- Pass
- {
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
-
- float4 _OutlineColor;
- float _RimPower;
- sampler2D _MainTex;
- float4 _MainTex_ST;
- struct appdata_t {
- float4 vertex : POSITION;
- float2 texcoord : TEXCOORD0;
- float4 color:COLOR;
- float4 normal:NORMAL;
- };
-
- struct v2f {
- float4 pos : SV_POSITION;
- float2 texcoord : TEXCOORD0;
- float4 color:COLOR;
- } ;
- v2f vert (appdata_t v)
- {
- v2f o;
- o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
- o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
- float3 viewDir = normalize(ObjSpaceViewDir(v.vertex));
- float rim = 1 - saturate(dot(viewDir,v.normal ));
- o.color = pow(rim,_RimPower);
- return o;
- }
- float4 frag (v2f i) : COLOR
- {
- fixed4 col = tex2D(_MainTex, i.texcoord);
- i.color=floor(i.color*2);
- col=lerp(col,i.color*_OutlineColor,i.color);
- return col;
- }
- ENDCG
- }
-
- }
- FallBack "Diffuse"
- }
VF版本代码01效果:
主要方法是在shader里面用两个pass渲染物体两次,第一个pass顶点外拓,用来打底作为描边,第二个pass正常渲染物体。下面这个例子做了一些改良:分别从顶点和法线方向挤出,然后做判断进行插值。
第一个pass作如下处理:
Cull Front 裁剪了物体的前面(对着相机的),把背面挤出
ZWrite On 像素的深度写入深度缓冲,如果关闭的话,物体与物体交叠处将不会被描边,因为此处无z值后渲染的物体会把此处挤出的描边“盖住”
在处理顶点的函数vert中把点挤出
float3 dir=normalize(v.vertex.xyz);
建立一个float3方向变量dir,把该点的位置作为距离几何中心的方向的单位向量
float3 dir2=v.normal;
dir2为法线方向
D=dot(dir,dir2);
D为计算该点位置朝向和法线方向的点积,通过正负值可以确定是指向还是背离几何中心的,正为背离,负为指向
dir=dir*sign(D);
乘上正负值,真正的方向值
dir=dir*_Factor+dir2*(1-_Factor);
把该点位置朝向与法线方向按外部变量_Factor的比重混合,来控制挤出多远
v.vertex.xyz+=dir*_Outline;
把物体背面的点向外挤出
- v2f vert (appdata_full v) {
- v2f o;
- float3 dir=normalize(v.vertex.xyz);
- float3 dir2=v.normal;
- float D=dot(dir,dir2);
- dir=dir*sign(D);
- // dir=dir*_Factor+dir2*(1-_Factor);
- dir=lerp(dir,dir2,_Factor);
- v.vertex.xyz+=dir*_Outline;
- o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
- return o;
- }
- float4 frag(v2f i):COLOR
- {
- float4 c = _OutlineColor;
- return c;
- }
VF版本代码02:
- //one directional light
- Shader "PengLu/Toon/ToonOneLightVF" {
- Properties {
- _Color("Main Color",color)=(1,1,1,1)
- _OutlineColor("Outline Color",color)=(0.1,0.1,0.2,1)
- _MainTex ("Base (RGB)", 2D) = "white" {}
- _Outline("Thick of Outline",range(0,0.1))=0.01
- _Factor("Factor",range(0,1))=0.5
- _ToonEffect("Toon Effect",range(0,1))=0.5
- _Steps("Steps of toon",range(0,9))=3
- }
- SubShader {
-
- pass{
- Tags{"LightMode"="Always"}
- Cull Front
- ZWrite On
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
- float _Outline;
- float _Factor;
- float4 _Color,_OutlineColor;
-
- struct v2f {
- float4 pos:SV_POSITION;
- };
-
- v2f vert (appdata_full v) {
- v2f o;
- float3 dir=normalize(v.vertex.xyz);
- float3 dir2=v.normal;
- float D=dot(dir,dir2);
- dir=dir*sign(D);
- // dir=dir*_Factor+dir2*(1-_Factor);
- dir=lerp(dir,dir2,_Factor);
- v.vertex.xyz+=dir*_Outline;
- o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
- return o;
- }
- float4 frag(v2f i):COLOR
- {
- float4 c = _OutlineColor;
- return c;
- }
- ENDCG
- }
- pass{
- Tags{"LightMode"="ForwardBase"}
- Cull Back
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
-
- float4 _LightColor0;
- float4 _Color;
- float _Steps;
- float _ToonEffect;
- sampler2D _MainTex;
- float4 _MainTex_ST;
-
- struct v2f {
- float4 pos:SV_POSITION;
- float3 lightDir:TEXCOORD0;
- float3 viewDir:TEXCOORD1;
- float3 normal:TEXCOORD2;
- float2 texcoord:TEXCOORD3;
- };
- v2f vert (appdata_full v) {
- v2f o;
- o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
- o.normal=v.normal;
- o.lightDir=ObjSpaceLightDir(v.vertex);
- o.viewDir=ObjSpaceViewDir(v.vertex);
- o.texcoord=TRANSFORM_TEX(v.texcoord, _MainTex);
- return o;
- }
- float4 frag(v2f i):COLOR
- {
- float4 c=1;
- float3 N=normalize(i.normal);
- float diff=max(0,dot(N,i.lightDir));
- diff=(diff+1)/2;
- diff=smoothstep(0,1,diff);
- float toon=floor(diff*_Steps)/_Steps;
- diff=lerp(diff,toon,_ToonEffect);
- c=_Color*_LightColor0*(diff);
- c*=tex2D(_MainTex, i.texcoord);
- return c;
- }
- ENDCG
- }
- }
- }
1.floor函数去整离散着色
卡通着色主要是frag函数里面处理
要做梯度着色,需要有渐变,因此需要先实现漫反射颜色
- float3 N=normalize(i.normal);
- float diff=max(0,dot(N,i.lightDir));
求出正常的漫反射颜色
diff=(diff+1)/2;
因为只有一个直射光没考虑环境光和其他光的,因此需要做亮化处理把暗部提亮。
diff=smoothstep(0,1,diff);
使颜色平滑的在[0,1]范围之内
float toon=floor(diff*_Steps)/_Steps;
把颜色做离散化处理,把diffuse颜色限制在_Steps种(_Steps阶颜色),简化颜色,这样的处理使色阶间能平滑的显示
diff=lerp(diff,toon,_ToonEffect);
通过控制卡通化程度值_ToonEffect,调节卡通与写实的比重
VF版本代码03:
- Shader "PengLu/Toon/ToonOnePassVF" {
- Properties {
- _MainTex ("Base (RGB)", 2D) = "white" {}
- _OutlineColor("OutlineColor",Color) = (0,1,1,1)
- _RimPower ("Rim Power", Range(0.1,8.0)) = 2
- _ToonEffect("Toon Effect",range(0,1))=0.5
- _Steps("Steps of toon",range(0,9))=3
- }
- SubShader {
- Tags { "Queue"="Geometry" "RenderType"="Opaque" }
- LOD 200
-
- Pass
- {
- Tags{"LightMode"="ForwardBase"}
- Cull Back
-
-
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
-
- float4 _OutlineColor;
- float _RimPower;
- float4 _LightColor0;
- float _Steps;
- float _ToonEffect;
- sampler2D _MainTex;
- float4 _MainTex_ST;
-
- struct v2f {
- float4 pos : SV_POSITION;
- float3 lightDir:TEXCOORD0;
- float3 viewDir:TEXCOORD1;
- float3 normal:TEXCOORD2;
- float2 texcoord:TEXCOORD3;
- float4 color:COLOR;
- } ;
- v2f vert (appdata_full v)
- {
- v2f o;
- o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
- o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
- o.normal=v.normal;
- o.lightDir=normalize(ObjSpaceLightDir(v.vertex));
- o.viewDir=normalize(ObjSpaceViewDir(v.vertex));
- float rim = 1 - saturate(dot(o.viewDir,v.normal ));
- o.color = pow(rim,_RimPower);
- return o;
- }
- float4 frag (v2f i) : COLOR
- {
- fixed4 col = tex2D(_MainTex, i.texcoord);
- // i.color=smoothstep(0,1,i.color*2);
- i.color=floor(i.color*2);
- col=lerp(col,i.color*_OutlineColor,i.color);
- float3 N=normalize(i.normal);
- float diff=max(0,dot(N,i.lightDir));
- diff=(diff+1)/2;
- diff=smoothstep(0,1,diff);
- float toon=floor(diff*_Steps)/_Steps;
- diff=lerp(diff,toon,_ToonEffect);
- col*=_LightColor0*(diff);
- return col;
- }
- ENDCG
- }
-
- }
- FallBack "Diffuse"
- }
VF版本代码03效果:
2.RampMap 采样着色
RampMap采样着色是通过映射一张一维纹理(如下图)来实现卡通化着色,相对floor函数,RampMap映射方式比较容易控制卡通效果,美术师只需修改贴图就能修改卡通渐变的过渡等效果。
RampMap映射的代码基本和floor函数方式相同,只有离散方式不同,关键代码如下:
float toon=tex2D(_ToonMap,float2(diff,0.5)).r;
将diff作为uv坐标在贴图上u方向查询获得对应的颜色。
VF版本代码04:
- //one directional light
-
- Shader "PengLu/Toon/ToonRampMapVF" {
- Properties {
- _Color("Main Color",color)=(1,1,1,1)
- _OutlineColor("Outline Color",color)=(0.1,0.1,0.2,1)
- _MainTex ("BaseTex", 2D) = "white" {}
- _ToonMap("Ramp Map",2D)="white"{}
- _Outline("Thick of Outline",range(0,0.1))=0.02
- _Factor("Factor",range(0,1))=0.5
- _ToonEffect("Toon Effect",range(0,1))=0.5
- }
- SubShader {
- pass{
- Tags{"LightMode"="Always"}
- Cull Front
- ZWrite On
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
- float _Outline;
- float _Factor;
- float4 _OutlineColor;
-
- struct v2f {
- float4 pos:SV_POSITION;
- };
-
- v2f vert (appdata_full v) {
- v2f o;
- float3 dir=normalize(v.vertex.xyz);
- float3 dir2=v.normal;
- float D=dot(dir,dir2);
- dir=dir*sign(D);
- dir=dir*_Factor+dir2*(1-_Factor);
- v.vertex.xyz+=dir*_Outline;
- o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
- return o;
- }
- float4 frag(v2f i):COLOR
- {
- float4 c=_OutlineColor;
- return c;
- }
- ENDCG
- }//end of pass
-
- pass{
- Tags{"LightMode"="ForwardBase"}
- Cull Back
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
-
- sampler2D _ToonMap;
- sampler2D _MainTex;
- float4 _MainTex_ST;
- float4 _LightColor0;
- float4 _Color;
- float _ToonEffect;
-
- struct v2f {
- float4 pos:SV_POSITION;
- float3 lightDir:TEXCOORD0;
- float3 normal:TEXCOORD1;
- float2 texcoord:TEXCOORD2;
- };
-
- v2f vert (appdata_full v) {
- v2f o;
- o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
- o.normal=v.normal;
- o.lightDir=ObjSpaceLightDir(v.vertex);
- o.texcoord=TRANSFORM_TEX(v.texcoord, _MainTex);
- return o;
- }
- float4 frag(v2f i):COLOR
- {
- float4 c=1;
- float3 N=normalize(i.normal);
- float3 lightDir=normalize(i.lightDir);
- float diff=max(0,dot(N,lightDir));
- diff=(diff+1)/2;
- diff=smoothstep(0,1,diff);
- float toon=tex2D(_ToonMap,float2(diff,0.5)).r;
- diff=lerp(diff,toon,_ToonEffect);
- c=_Color*_LightColor0*(diff);
- c*=tex2D(_MainTex,i.texcoord);
- return c;
- }
- ENDCG
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。