UnityShader入门

UnityShader入门

一月 10, 2022

总结

基本操作

平铺和偏移

1
2
3
4
5
6
7
//properties
_Texture("Texture",2D)="white"{}
//SubPass/Pass
sampler2D _Texture;
float4 _Texture_ST;
o.uv=v.uv*_Texture_ST.xy+_Texture_ST.zw;

顶点坐标系MVP转换

1
2
3
4
float4 pos_world = mul(unity_ObjectToWorld, v.vertex);
float4 pos_view = mul(UNITY_MATRIX_V,pos_world);
float4 pos_clip = mul(UNITY_MATRIX_P,pos_view);
o.pos=pos_clip;

或者

1
o.pos=UnityObjectToClipPos(v.vertex);

CG变量用法

1
2
3
float = 32//坐标
half = 16//uv,大部分向量
fixed =8//颜色

图像处理

面剔除

单独写死

1
Cull off

在面板上显示

1
2
[Enum(UnityEngine.Rendering.CullMode)]_CullMode("CullMode",Float)=2//Properties
Cull [_CullMode]//SubPass/Pass

图片铺在模型上

图片将平铺到x与y组成的平面上

1
o.uv=v.vertex.xy*_Texture_ST.xy+_Texture_ST.zw;

xy变成yx时

相当与x轴旋转180度y轴旋转-90度

1
o.uv=v.vertex.yx*_Texture_ST.xy+_Texture_ST.zw;
1
float4 pos_world = mul(unity_ObjectToWorld, v.vertex);

加面解决圆面失真的问题

水波的制作

先制作动态圆盘

1
2
3
4
5
6
_Float("Float",Float)=0.0
_Vector("Vector",Vector)=(1,1,1,1)
_Texture("Texture",2D)="white"{}//Properties
half gradient=tex2D(_Texture,i.uv+_Time.y*_Vector.xy).x;
clip(gradient-_Float);
return gradient.xxxx;//fragment

噪声

1
2
3
_NoiseTex("NoiseTex",2D)="white"{}//Properties
half noise=1.0f-tex2D(_NoiseTex,i.uv+_Time.y*_Vector.wz).x;
clip(gradient-_Float-noise);//fragment

完善颜色,完整代码

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
Shader"MyShader/03shader"
{
Properties{
_Float("Float",Float)=0.0
_Range("Range",Range(0,1))=0.0
_Vector("Vector",Vector)=(1,1,1,1)
_Color("Color",Color)=(0.5,0.5,0.5,0.5)
_Texture("Texture",2D)="white"{}
_NoiseTex("NoiseTex",2D)="white"{}
[Enum(UnityEngine.Rendering.CullMode)]_CullMode("CullMode",Float)=2
}
SubShader{
Pass{
Cull [_CullMode]
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include"UnityCG.cginc"
struct appdata{
float4 vertex:POSITION;
float2 uv:TEXCOORD0;
// float4 color:COLOR;
};
struct v2f{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
};
sampler2D _Texture;
float4 _Texture_ST;
float _Float;
float4 _Vector;
sampler2D _NoiseTex;
float2 _NoiseTex_ST;
fixed4 _Color;

v2f vert(appdata v)
{
v2f o;
float4 pos_world = mul(unity_ObjectToWorld, v.vertex);
float4 pos_view = mul(UNITY_MATRIX_V,pos_world);
float4 pos_clip = mul(UNITY_MATRIX_P,pos_view);
//o.pos=pos_clip;
o.pos=UnityObjectToClipPos(v.vertex);
o.uv=v.uv.xy*_Texture_ST.xy+_Texture_ST.zw;
return o;
}

half4 frag(v2f i):SV_Target
{
half gradient=tex2D(_Texture,i.uv+_Time.y*_Vector.xy).x;
half noise=1.0f-tex2D(_NoiseTex,i.uv+_Time.y*_Vector.wz).x;
clip(gradient-_Float-noise);
return _Color;
}

ENDCG
}

}
}

半透明混合

参数意思

one 此输入的值是 one。该值用于使用源或目标的颜色的值。
Zero 此输入的值是 zero。该值用于删除源或目标值。
SrcColor GPU 将此输入的值乘以源颜色值。
SrcAlpha GPU 将此输入的值乘以源 Alpha 值。
DstColor GPU 将此输入的值乘以帧缓冲区的源颜色值。
DstAlpha GPU 将此输入的值乘以帧缓冲区的源 Alpha 值。
OneMinusSrcColor GPU 将此输入的值乘以(1 - 源颜色)。
OneMinusSrcAlpha GPU 将此输入的值乘以(1 - 源 Alpha)。
OneMinusDstColor GPU 将此输入的值乘以(1 - 目标颜色)。
OneMinusDstAlpha GPU 将此输入的值乘以(1 - 目标 Alpha)。

常见混合类型

1
2
3
4
5
6
7
8
Blend SrcAlpha OneMinusSrcAlpha // 传统透明度
Blend SrcAlpha One
//以上两种常用
Blend One OneMinusSrcAlpha // 预乘透明度
Blend One One // 加法
Blend OneMinusDstColor One // 软加法
Blend DstColor Zero // 乘法
Blend DstColor SrcColor // 2x 乘法

代码修改

1
Tags { "Queue" = "Transparent" }
1
2
ZWrite Off
Blend SrcAlpha One//预乘透明度
1
2
3
half3 col=_Color.xyz*_Float;
half alpha=saturate(tex2D(_Texture,i.uv).r*_Color.a*_Float);
return half4(col,alpha);

边缘光(fresnel)简版的菲涅尔方程

菲涅尔边缘光实现

口述: 菲涅耳系数=pow (1.0f-saturate(dot(世界坐标到局部坐标的法线向量,世界坐标的顶点到摄像头的单位向量)) ,梯度系数)

1
2
3
o.normal_world=normalize( mul(float4(v.normal,0),unity_WorldToObject));//世界坐标到局部坐标的法线向量
float4 pos_world = mul(unity_ObjectToWorld, v.vertex);
o.view_world=normalize(_WorldSpaceCameraPos.xyz-pos_world);//世界坐标的顶点到摄像头的单位向量
1
2
3
4
5
6
7
8
float3 normal_world=normalize(i.normal_world);
float3 view_world=normalize(i.view_world);//光栅化后向量需要标准化
float NdotV=saturate(dot(normal_world,view_world));
half3 col=_Color.xyz*_Float;
float fresnel=pow(1.0f-NdotV,_Rim);//菲涅尔系数
half alpha=saturate(_Float*fresnel);

return half4(col,alpha);
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
Pass{
ZWrite Off
Blend SrcAlpha One//预乘透明度
Cull [_CullMode]
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include"UnityCG.cginc"
struct appdata{
float4 vertex:POSITION;
float2 uv:TEXCOORD0;
float3 normal:NORMAL;
// float4 color:COLOR;
};
struct v2f{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
float3 normal_world:TEXCOORD1;
float3 view_world:TEXCOORD2;
};
sampler2D _Texture;
float4 _Texture_ST;
fixed4 _Color;
float _Float;
float _Rim;
v2f vert(appdata v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.normal_world=normalize( mul(float4(v.normal,0),unity_WorldToObject));
float4 pos_world = mul(unity_ObjectToWorld, v.vertex);
o.view_world=normalize(_WorldSpaceCameraPos.xyz-pos_world);
o.uv=v.uv*_Texture_ST.xy+_Texture_ST.zw;
return o;
}

half4 frag(v2f i):SV_Target
{
float3 normal_world=normalize(i.normal_world);
float3 view_world=normalize(i.view_world);
float NdotV=saturate(dot(normal_world,view_world));
half3 col=_Color.xyz*_Float;
float fresnel=pow(1.0f-NdotV,_Rim);
half alpha=saturate(_Float*fresnel);

return half4(col,alpha);
}

预先写深度

修除内部透明小Bug,预先将前面的深度写入,但不写入颜色信息,用上面的pass第二遍时将后面的透明的给剔除掉,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Pass {
Cull Off
ZWrite On //深度写入
ColorMask 0//不写颜色信息
CGPROGRAM
float4 _Color;
#pragma vertex vert
#pragma fragment frag

float4 vert(float4 vertexPos : POSITION) : SV_POSITION
{
return UnityObjectToClipPos(vertexPos);
}

float4 frag(void) : COLOR
{
return _Color;
}
ENDCG
}