> Shader Vertex 的初步使用。

Basic

Example

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
Shader "Custom/vertexShader" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
#pragma surface surf Lambert vertex : vert
#pragma target 2.0

struct Input {
float2 uv_MainTex;
float4 vertColor;
};

fixed4 _Color;

void vert (inout appdata_full v,out Input o)
{
o.vertColor = v.color;
}

void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = IN.vertColor.rgb * _Color.rgb;
}
ENDCG
}
FallBack "Diffuse"
}

参数详情

  1. appdata_full 位于UnityCG.cginc . 顾名思义保存了顶点的绝大部分可用于计算的参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   struct appdata_full {
//顶点
float4 vertex : POSITION;
//切线
float4 tangent : TANGENT;
//法线
float3 normal : NORMAL;
//顶点坐标(字面意思,以此类推)
float4 texcoord : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
float4 texcoord2 : TEXCOORD2;
float4 texcoord3 : TEXCOORD3;
#if defined(SHADER_API_XBOX360) //4-5 移动端暂时就别想了
half4 texcoord4 : TEXCOORD4;
half4 texcoord5 : TEXCOORD5;
#endif
fixed4 color : COLOR;
};

这里还包含两个appdata_full的简化版,其实都一样 什么时候要用简化版?当然是越简化越省性能。

1
2
3
4
5
6
7
8
9
10
11
12
   struct appdata_base {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};

struct appdata_tan {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};

2. SurfaceOutput 位于Lighting.cginc . 这可以说是最常见的一个结构体了但还是稍微写下吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
   struct SurfaceOutput {
//本身的颜色
fixed3 Albedo;
//法线贴图
fixed3 Normal;
//自发光
fixed3 Emission;
//反光
half Specular;
//光泽度
fixed Gloss;
//透明
fixed Alpha;
};

Effect

效果展示

效果就是文章首页放的图了。下面的算法我就不解释了,毕竟这数学也不是靠别人能说清楚的。

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
 Shader "test/vertexAnimation" {
Properties {
_ColorA ("Color", Color) = (1,1,1,1)
_ColorB ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_TintAmount("TintAmount",Range(0,1)) = 0.5
_Speed("Wave Speed",Range(0.1,80)) = 1
_Frequency("Wave Frequency",Range(0,5)) = 2
_Amplitude("Wave Amplitude",Range(-1,1)) = 1
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
#pragma surface surf Lambert vertex:vert

#pragma target 2.0

sampler2D _MainTex;
fixed4 _ColorA;
fixed4 _ColorB;
float _TintAmount;
float _Speed;
float _Frequency;
float _Amplitude;

struct Input{
float2 uv_MainTex;
float4 vertColor;
};

void vert (inout appdata_full v,out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input,o);

float time = _Time * _Speed;
float waveValueA = sin(time + v.vertex.x * _Frequency) * _Amplitude;

v.vertex.xyz = float3(v.vertex.x , v.vertex.y + waveValueA , v.vertex.z);
v.normal = normalize(float3(v.normal.x + waveValueA,v.normal.y,v.normal.z));
o.vertColor = float4(waveValueA,waveValueA,waveValueA,255);
}


void surf (Input IN, inout SurfaceOutput o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
float3 tintColor = lerp(_ColorA,_ColorB,IN.vertColor).rgb;
o.Albedo = c.rgb * (tintColor * _TintAmount);
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}

WARNING

#pragma声明函数

在 #pragma 声明函数的时候,不要重名否则会报错。这是常识,引擎编译的时候会自动搜索所有编译期文件是否包含此函数。

in out inout

in out inout 是cg语言提供的输入输出关键字,用来表示图形硬件上的不同寄存器。
引用《GPU 编程与CG 语言之阳春白雪下里巴人》的一段话来概括:

1
2
3
4
5
6
7
8
1. in: 修辞一个形参只是用于输入,进入函数体时被初始化,且该形参值 的改变不会影响实参值,这是典型的值传递方式。
2. out: 修辞一个形参只是用于输出的,进入函数体时并没有被初始化,这 种类型的形参一般是一个函数的运行结果;
3. inout: 修辞一个形参既用于输入也用于输出,这是典型的引用传递。 举例如下:
void myFunction(out float x); //形参 x,只是用于输出
void myFunction(inout float x); //形参 x,即用于输入时初始化,也用于输出数据
void myFunction(in float x); //形参 x,只是用于输入
void myFunction(float x); /等价与 in float x,这种用法和 C\C++完全一致 也可以使用 return 语句来代替 out 修辞符的使用。输入\输出修辞符通常和语
义词一起使用,表示顶点着色程序和片段着色程序的输入输出。

使用Lambert的vertex

使用Lambert的vertex自定义函数的时候编辑器默认会报错,这个时候加入下面代码。这些代码可以看作是对Direcxtx11的hack。

1
UNITY_INITIALIZE_OUTPUT(Input,o);

使用_Time

如果直接使用_Time * Float 那么默认使用_Time.x

1
float4 _Time : Time (t/20, t, t*2, t*3), use to animate things inside the shaders