卡通风格
-
先一个Pass只渲染背面,黑色,沿法线膨胀,做轮廓线效果;
-
正式渲染Pass,漫反射采样一个逐渐变暗的纹理,做出硬边明暗。高光反射和一个阈值比较,大于则直接显示高光颜色。
Shader "My/Toon"
{
Properties
{
_Color ("Color", Color) =(1,1,1,1)
_SpeColor("SpeColor",Color)=(1,1,1,1)
_SpecThreshold("SpecThreshold",Range(0.1,1))=1
_MainTex("MainTex",2D)="white"{}
_ShadeTex("ShadeTex",2D)="white"{}
_OutlineWidth("OutlineWidth",Range(0.001,0.1))=0.01
_OutlineCol("OutlineColor",Color)=(0,0,0,1)
[Enum(UnityEngine.Rendering.CullMode)]_Cull("Cull",Float)=0
}
SubShader
{
Tags { "RenderType"="TransparentCutout""Queue"="AlphaTest"}
LOD 100
Pass{
Name "Outline"
Cull FrontCGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 vertex: SV_POSITION; }; float _OutlineWidth; fixed4 _OutlineCol; v2f vert(appdata_base v){ v2f o; v.vertex.xyz+= normalize(v.normal)* _OutlineWidth; o.vertex= UnityObjectToClipPos(v.vertex); return o; } fixed4 frag(v2f i): SV_Target{ return _OutlineCol; } ENDCG } Pass { Name "Main" Tags{"LightMode" = "ForwardBase"} Cull [_Cull] Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv:TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float2 uv:TEXCOORD0; float3 worldPos: TEXCOORD1; float3 worldNormal: TEXCOORD2; float3 viewDir: TEXCOORD3; float3 lightDir: TEXCOORD4; SHADOW_COORDS(5) }; fixed4 _Color; fixed4 _SpeColor; float _SpecThreshold; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _ShadeTex; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal=normalize(UnityObjectToWorldNormal(v.normal)); o.lightDir=-normalize(_WorldSpaceLightPos0.xyz); o.worldPos=mul(unity_ObjectToWorld,v.vertex); o.viewDir=normalize(_WorldSpaceCameraPos-o.worldPos); TRANSFER_SHADOW(o); o.uv=v.uv; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 texCol=tex2D(_MainTex,i.uv); //漫反射 float3 lightPos=normalize(_WorldSpaceLightPos0.xyz); fixed diff=dot(i.worldNormal,lightPos)*0.5+0.5; UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos); diff*=atten; fixed4 shadeCol=tex2D(_ShadeTex,float2(diff,0)); //高光 float3 reflDir=reflect(i.lightDir,i.worldNormal); float spec=step(_SpecThreshold ,dot(reflDir,i.viewDir)); fixed3 color=_Color.rgb* texCol.rgb*(UNITY_LIGHTMODEL_AMBIENT+ shadeCol) +_SpeColor.rgb* spec; return fixed4(color,texCol.a); //return UNITY_LIGHTMODEL_AMBIENT; } ENDCG } } Fallback "Diffuse"}

脸的下部有点黑,可以加一个漫反射明暗到渐变贴图u的系数,控制漫反射变黑的阈值。原神的效果比这个更亮。这个有点纸模感。


消融效果
- uv采样柏林噪声贴图,小于阈值则clip()不渲染;
Shader "My/Melt"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_NoiseTex("NoiseTex",2D)=""{}
_EdgeTex("EdgeTex",2D)=""{}
_EdgeWidth("EdgeWidth",Range(0,1))=0.01
_MeltThre("MeltThreshold",Range(0,1))=0
}
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _NoiseTex;
sampler2D _EdgeTex;
fixed _EdgeWidth;
fixed _MeltThre;
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed no= tex2D(_NoiseTex,i.uv);
clip(no- _MeltThre);
//噪声离阈值的差小于edge宽度时,开始显示边缘;
//把差值相对于edge宽度的比例转换到0-1,用于采样贴图
fixed edgeU=1- smoothstep(0,_EdgeWidth,no- _MeltThre);
fixed4 edgeCol= tex2D(_EdgeTex,fixed2(edgeU,0.5));
fixed4 col = tex2D(_MainTex, i.uv);
return lerp(col, edgeCol,edgeU);
}
ENDCG
}
}
}
水面
Shader "My/Water"
{
Properties{
_MainTex("MainTex",2D)=""{}
_NormalTex("NormalTex",2D)=""{}
_Cube("Cubemap",Cube)=""{}
_Distortion("Distortion",Range(0,1))=0
_XSpeed("XSpeed",Range(-0.1,0.1))=0.01
_YSpeed("YSpeed",Range(-0.1,0.1))=0.01
_Fresnel("Fresnel",Range(0,1))=1
}
SubShader{
Tags{"RenderType"="Opaque""Queue"="Transparent"}
GrabPass{}
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _NormalTex;
float4 _NormalTex_ST;
samplerCUBE _Cube;
sampler2D _GrabTexture;//GrabPass捕获的图像,默认名
float _Distortion;
fixed _XSpeed;
fixed _YSpeed;
float _Fresnel;
struct v2f
{
float4 pos:SV_POSITION;
float4 grabPos:TEXCOORD0;
float4 uv:TEXCOORD1;
float4 TtoW0: TEXCOORD3;
float4 TtoW1: TEXCOORD4;
float4 TtoW2: TEXCOORD5;
};
v2f vert(appdata_full v){
v2f o;
o.pos= UnityObjectToClipPos(v.vertex);
o.grabPos= ComputeScreenPos(o.pos);
o.uv.xy=v.texcoord.xy* _MainTex_ST.xy+ _MainTex_ST.zw;
o.uv.zw=v.texcoord.xy* _NormalTex_ST.xy+ _NormalTex_ST.zw;
float3 worldNormal= UnityObjectToWorldNormal(v.normal);
fixed3 worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
//
float3 worldTan= UnityObjectToWorldDir(v.tangent);
float3 worldBinormal= cross(normalize(worldTan),normalize(worldNormal))*v.tangent.w;
o.TtoW0=float4(worldTan.x,worldBinormal.x,worldNormal.x,worldPos.x);
o.TtoW1=float4(worldTan.y,worldBinormal.y,worldNormal.y,worldPos.y);
o.TtoW2=float4(worldTan.z,worldBinormal.z,worldNormal.z,worldPos.z);
return o;
}
fixed4 frag(v2f i):SV_Target{
float3 worldPos=float3(i.TtoW0.w,i.TtoW1.w,i.TtoW2.w);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//float4 normal= tex2D(_NormalTex,i.uv.zw);
////法线贴图的法线在切线空间
//float3 tanNormal=UnpackNormal(normal);
float2 speed= _Time.y*float2( _XSpeed,_YSpeed);
float4 packNormal= tex2D(_NormalTex,i.uv.zw+ speed);
fixed3 bump1= UnpackNormal(packNormal).rgb;
packNormal= tex2D(_NormalTex,i.uv.zw- speed);
fixed3 bump2= UnpackNormal(packNormal).rgb;
fixed3 bump= normalize( bump1+bump2);
////法线贴图的法线在世界
float3 worldNormal =
float3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump));
fixed3 albedo = tex2D(_MainTex, i.uv.xy);
float3 refl= reflect(-viewDir,worldNormal);
//兰伯特
//fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(worldNormal, normalize(_WorldSpaceLightPos0.xyz)));
//半角向量
//float3 halfA = normalize(normalize(viewDir) + normalize(_WorldSpaceLightPos0.xyz));
//高光反射
//fixed3 specularColor = _LightColor0.rgb* pow(max(0, dot(worldNormal, halfA)), _SpecularNum);
fixed4 reflCol= texCUBE(_Cube,refl)*tex2D(_MainTex,i.uv);
float2 offset= bump.xy*_Distortion;
i.grabPos.xy= offset*i.grabPos.z+ i.grabPos.xy;
fixed2 screenUV=(i.grabPos.xy+offset)/i.grabPos.w;
fixed4 grabCol= tex2D(_GrabTexture,screenUV);
fixed fresnel= _Fresnel+(1- _Fresnel)*
pow((1-dot(normalize(viewDir),normalize(worldNormal))),5);
float4 color=reflCol*(1-fresnel)+grabCol*fresnel;
return color;
}
ENDCG
}
}
}