Unity卡通着色Shader实现
另外一篇已经被实现的边缘着色
Unity——Shader实现边缘发光效果 | mao的博客 (mao1mao2mao3mao4.github.io)
法线外拓的三种方式 来自文心一言的回答
要将模型空间下的法线转换到视角空间下,你需要使用模型到视图矩阵(UNITY_MATRIX_MV
),但需要注意法线变换的特殊性。法线变换需要使用逆转置矩阵(Inverse Transpose Matrix)来保持其垂直性。Unity提供了一个宏UNITY_MATRIX_IT_MV
(尽管你之前的代码中名称有误,但这里是为了说明正确的使用方式),它实际上已经包含了模型到视图矩阵的逆转置。
1.模型空间下
2.视角空间下
3.裁剪空间下
基础阴影实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| Shader "Unlit/07" { Properties { _MainTex ("Texture", 2D) = "white" {} _Diffuse("Diffuse", Color) = (1,1,1,1) _OutLine("Outline",Range(0,1)) = 0.5 _OutLineColor("OutLineColo",Range(0,1)) = 0.1 _Steps("Steps",Range(1,30)) = 1 _ToonEffect("ToonEffect",Range(0,1)) = 0.5 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 pass { Cull Front
CGPROGRAM #pragma vertex vert #pragma fragment frag
float _OutLine; float _OutLineColor;
float4 vert (appdata_base v) : SV_POSITION { v.vertex += v.normal * _OutLine; return UnityObjectToClipPos(v.vertex); }
fixed4 frag () : SV_Target { return _OutLineColor; } ENDCG }
Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fog
#include "UnityCG.cginc" #include "UnityLightingCommon.cginc "
struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; float3 worldNormal : TEXCOORD3; float3 worldPos : texcoord4;
};
sampler2D _MainTex; float4 _MainTex_ST;
float4 _Diffuse;
float _OutLine; float _OutLineColor;
float _Steps;
float _ToonEffect;
v2f vert (appdata_tan v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz; o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); return o; }
fixed4 frag (v2f i) : SV_Target { float4 texColor = tex2D(_MainTex,i.uv);
float3 environmentLightColor = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); float3 lightDir =normalize(UnityWorldSpaceLightDir(i.worldPos)); float3 diffuseColor = _LightColor0.rbg * _Diffuse.rbg * (dot(lightDir,i.worldNormal) *0.5 + 0.5) * texColor.rbg;
diffuseColor = smoothstep(0,1,diffuseColor);
float3 toon = floor(diffuseColor * _Steps) / _Steps; diffuseColor = lerp(toon,diffuseColor,_ToonEffect);
float3 resColor = environmentLightColor + diffuseColor;
float col = float4(resColor,1); return col; } ENDCG } }
FallBack "Diffuse" }
|
边缘光
在世界空间下求得模型边缘
1 2 3 4 5
| float rim = 1- dot(viewDir,i.worldNormal);
float3 rimColor = _RimColor * pow(rim , 1/_RimPower);
|
XRay
人物被物体遮挡,透过遮挡物看见人物的功能
实际上是调整深度和深度写入中的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| Pass {
ZWrite Off ZTest Greater Blend SrcAlpha One
CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc"
float4 _XRayColor; float _XRayPower;
struct v2f { float4 vertex : SV_POSITION; float3 worldNormal : TEXCOORD1; float3 worldPos: TEXCOORD2; };
v2f vert(appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
return o; }
float4 frag(v2f i):SV_Target { float3 normal = normalize(i.worldNormal); float3 pos = i.worldPos;
float3 viewDir = normalize(UnityWorldSpaceViewDir(pos)); float3 lightDir = normalize(UnityWorldSpaceLightDir(pos)); float rim = 1 - dot(viewDir,normal);
return _XRayColor * pow(rim,1/_XRayPower); } ENDCG }
|
请注意,所有透明有关的shader要正确设置自己的渲染顺序
这里通过固有关键字名称+数字的形式,不能有括号
1
| Tags { "Queue"="Geometry+600" }
|
Shader优化
卡通场景
杂项知识
- 使用的内容和人物2DShader一致,新添加了对法线的使用
Unity —— Shader入门,法线贴图 | mao的博客 (mao1mao2mao3mao4.github.io)
1 2
| UsePass "Unlit/07/OutLine"
|
- Properties属性栏中,属性,例如:_MainTex,这样的属性名,如果在ShaderA,B中都有声明,A中首先引用一张图片,那么B在Unity操作窗口中同样会有这一张图片的引用
雪的制作
Properties属性添加
1 2 3
| _Snow("Snow",Range(0,1))= 0.5 _SnowColor("SnowColor",Color) = (1,1,1,1) _SnowDir("SnowDir",vector) = (1,1,1,1)
|
Shader宏定义
1 2 3 4 5 6 7 8 9
| #pragma vertex vert #pragma fragment frag
#pragma mutil_compile __ _SNOW_ON
|
Shader效果实现
1 2 3 4 5 6 7 8 9 10 11
| #if _SNOW_ON if(dot(worldNormal,_SnowDir.xyz) > lerp(1,-1,_Snow)) { resColor.rbg = _SnowColor.rbg;
} else { resColor.rbg = resColor.rbg; } #endif
|
C#脚本宏控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class ShaderSnow : MonoBehaviour { private static string SNOW_ON = "SNOW_ON"; [MenuItem("Tools/Shader/打开或则关闭雪的宏")] public static void OpenRimLight() { if(Shader.IsKeywordEnabled(SNOW_ON)) { Shader.DisableKeyword(SNOW_ON); } else { Shader.EnableKeyword(SNOW_ON); } } }
|
C#全局变量控制
1 2 3 4
| string Snow = "_Snow"; Shader.SetGlobalFloat(Snow, 1.0f);
|