Shader语法基础

在写光照的语法之前,我们应该知道shader内部的运行函数,以及运行的顺序是什么。
看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Shader “MyShader/Shader1”
{
//这里我们需要设置你渲染时需要的各种属性
//shader属性会出现在关联材质球的选项里面
Properties{

}
//subshader其实应该分成俩部分,一部分是给显卡足够高级的用户另一部分则是为了满足
// 显卡不是那么高级用户的
SubShader{
// *pass通道与subshader的执行方式有着很大的不同,subshader只会执行一次,
//但是每有一个pass通道则会执行一次且从上到下依次执行
Pass{
CGPROGRAM
//do something
ENDCG
}
//如果所有的subshader都执行失败,则最终执行fallback
FallBack"Diffuse"
}

光照

漫反射(逐顶点)

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
Shader "hxsd/DiffuseVertex"
{
Properties
{
_DiffuseColor("漫反射颜色", Color) = (1, 1, 1, 1)
}
SubShader
{
Pass
{
//设定光照模式为前向模式(才能正常获取光的颜色和光的方向)
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//加载Cg语言的脚本,用来处理光照参数
//处理光照的Cg库文件(cginc扩展名),目录在Unity的安装目录下Editor/Data/CGIncludes/Lighting.cginc
#include "Lighting.cginc"
//导入材质颜色
fixed4 _DiffuseColor;
//如果在Cg编程中,顶点或片元着色器接收多个数值的时候,一般会用结构体实现
//从CPU接收到的数据
struct c2v
{
float4 vertex : POSITION; //从CPU接收到的模型空间下的点的位置
float3 normal : NORMAL; //从CPU接收到的当前点的模型空间下的法线向量
};
//因为在顶点着色器中,需要计算裁剪空间下点的位置和Phong着色计算出来的兰伯特定律计算后的颜色
struct v2f
{
float4 pos : SV_POSITION; //经过顶点着色器计算后的,当前点的裁剪空间下的位置
fixed3 color : COLOR; //经过兰伯特定律计算后的当前点的颜色
};
//高洛徳着色(逐顶点光照),光照计算应该编写在顶点着色器中
v2f vert(c2v data)
{
//顶点着色器传递给片元着色器的数据结构体声明
v2f r;
//必须做的:将点从模型空间下,转换到裁剪空间下
//mul(UNITY_MATRIX_MVP, data.vertex)
r.pos = UnityObjectToClipPos(data.vertex);
//兰伯特定律计算
//漫反射光照 = 光源的颜色 * 材质的漫反射颜色 * MAX(0, 标准化后物体表面法线向量 • 标准化后光源方向向量)
//光照是在世界中发生,需要将所有的数值,变换到世界坐标系下,再运算
//_Object2World矩阵是Unity提供的,用于转换模型空间下到世界空间下的转换矩阵
//因为法线传递过来的是3x1的列矩阵,_Object2World是4x4的齐次矩阵,如果想做矩阵乘法,需要将齐次矩阵,变成3x3矩阵
fixed3 worldNormal = normalize(mul((float3x3)unity_ObjectToWorld, data.normal));
//获得直射光的光方向
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

//公式运算
fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * max(0, dot(worldNormal, worldLightDir));

//根据Phong光照模型,将环境光追加在最终计算后的颜色上
r.color = UNITY_LIGHTMODEL_AMBIENT.xyz + diffuse;

return r;
}

fixed4 frag(v2f data) : SV_Target
{
//将顶点着色器计算好的颜色,传递给GPU
return fixed4(data.color, 1.0);
}

ENDCG
}
}

Fallback "Diffuse"
}

//实际上半兰伯特的光照和漫反射十分的相像,只是最后的光照公式不同而已,这里我们直接
//就用半兰伯特来表示逐像素的漫反射(因为懒)

半兰伯特(逐像素)

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

Shader "Custom/BLBT"
{
Properties
{
_DiffuseColor("漫反射颜色",Color) =(1,1,1,1 )
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _DiffuseColor;
struct c2v
{
float4 vertex : POSITION;
float3 nolmal : NORMAL;
};
struct v2f
{
float4 pos: SV_POSITION;
//fixed3 color : COLOR;
float3 worldNormal : NORMAL;//经过矩阵转换后世界空间下的法线向量
};
//将点从模型空间转换到裁剪空间下
//将渲染点对应的法线,从模型空间下转换到世界空间下
v2f vert(c2v data)
{
v2f r;
//因为法线传递过来的是3x1的列矩阵,_Ibject2Woirld是4x4的齐次矩阵,如果想做矩阵乘法
//需要将齐次矩阵变成3x3的矩阵
r.pos = UnityObjectToClipPos(data.vertex);
//几何运算 在定点着色器中完成 再讲运算好的数值传递给片源着色器
//必须做的,将点从模型空间下,转换到裁剪空间下
r.worldNormal = mul((float3x3)unity_ObjectToWorld,data.nolmal);
return r;
}
//phong着色(逐像素光照) 光照计算应该编写在片源着色器中
fixed4 frag (v2f data ) : SV_Target
{
//兰伯特定律
//漫反射光照 = 光源的颜色 * 漫反射的颜色 *max(0,标准后的物体表面的法线向量,标准后的光源方向向量)
//世界空间下的 表面法线向量标准话
fixed3 worldNormal = normalize(data.worldNormal);
//获得直射光的方向 标准化向量
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * (dot(worldNormal,worldLightDir)*0.5 +0.5);
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.xyz + diffuse;
return fixed4(color,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}

高光反射(逐顶点)

*值得注意的是这里的高光反射是只有那个高光的计算和漫反射是俩回事不要弄混。

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
Shader "Custom/高光(逐顶点)"
{
Properties
{

_SpecularColor("高光反射材质颜色",Color) =(1,1,1,1 )
_Gloss("光晕系数",Range(4,256)) = 10
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM

#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"


fixed4 _SpecularColor;//高光材质颜色
float _Gloss; //光晕系数

struct c2v
{
float4 vertex : POSITION;
float3 nolmal : NORMAL;
};
//因为在定点着色器中 运算phong光照
struct v2f
{
float4 pos: SV_POSITION;
fixed3 color : COLOR; // 经过Phong光照模型公式计算后的当前点的颜色
};
//将点从模型空间转换到裁剪空间下
//将渲染点对应的法线,从模型空间下转换到世界空间下
v2f vert(c2v data)
{
v2f r;
//因为法线传递过来的是3x1的列矩阵,_Ibject2Woirld是4x4的齐次矩阵,如果想做矩阵乘法
//需要将齐次矩阵变成3x3的矩阵
r.pos = UnityObjectToClipPos(data.vertex);
//高光反射运算
//公式 高光光照 = 光源的颜色 * 材质高光反射颜色 * [max(0,标准后的观察方向向量标准后的反射方向)]^光晕系数


//光反射方向 reflect(入射光的方向,当前点的法线方向)
//标准后的法相方向向量
fixed3 worldNormal = normalize(mul((fixed3x3)unity_ObjectToWorld,data.nolmal));

//观察的方向向量(相机的点 - 被渲染点的位置,在世界空间下)
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld,data.vertex).xyz) ;

//_WorldSpaceLightPos0是物体到光的方向
//光线反射的方向
fixed3 refDir= normalize( reflect( -_WorldSpaceLightPos0.xyz,worldNormal)) ;

fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb *pow(max(0,dot(viewDir,refDir)),_Gloss);

r.color = UNITY_LIGHTMODEL_AMBIENT.xyz + specular;
return r;
}

//phong着色(逐像素光照) 光照计算应该编写在片源着色器中
fixed4 frag (v2f data ) : SV_Target
{
return fixed4(data.color,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}

高光反射(逐像素)

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
Shader "Custom/高光(逐像素)"
{
Properties
{
_SpecularColor("高光反射材质颜色",Color) =(1,1,1,1 )
_Gloss("光晕系数",Range(4,256)) = 10
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM

#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"


fixed4 _SpecularColor;//高光材质颜色
float _Gloss; //光晕系数

struct c2v
{
float4 vertex : POSITION;
float3 nolmal : NORMAL;
};
//因为在定点着色器中 运算高光反射的颜色
struct v2f
{
float4 pos: SV_POSITION;
float3 normal : NORMAL; //世界空间下法线的向量
float4 worldPos : TEXCOORD0;//借用纹理的语义,实现世界空间下点的位置传递
};
//将点从模型空间转换到裁剪空间下
//将渲染点对应的法线,从模型空间下转换到世界空间下
v2f vert(c2v data)
{
v2f r;
//因为法线传递过来的是3x1的列矩阵,_Ibject2Woirld是4x4的齐次矩阵,如果想做矩阵乘法
//需要将齐次矩阵变成3x3的矩阵
r.pos = UnityObjectToClipPos(data.vertex);

r.worldPos =mul(unity_ObjectToWorld,data.vertex);
r.normal = mul((fixed3x3)unity_ObjectToWorld,data.nolmal);
return r;
//世界空间下,点的位置

/*
//高光反射运算
//公式 高光光照 = 光源的颜色 * 材质高光反射颜色 * [max(0,标准后的观察方向向量标准后的反射方向)]^光晕系数

//光反射方向 reflect(入射光的方向,当前点的法线方向)
//标准后的法相方向向量
fixed3 worldNormal = normalize(mul((fixed3x3)unity_ObjectToWorld,data.nolmal));

//观察的方向向量(相机的点 - 被渲染点的位置,在世界空间下)
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld,data.vertex).xyz) ;

//_WorldSpaceLightPos0是物体到光的方向
//光线反射的方向
fixed3 refDir= normalize( reflect( -_WorldSpaceLightPos0.xyz,worldNormal)) ;

fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb *pow(max(0,dot(viewDir,refDir)),_Gloss);
r.color = UNITY_LIGHTMODEL_AMBIENT.xyz + specular;
return r;
*/
}

//phong着色(逐像素光照) 光照计算应该编写在片源着色器中
fixed4 frag (v2f data ) : SV_Target
{
fixed3 worldNormal = normalize(data.normal);
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - data.worldPos.xyz) ;

//计算光线反射方向
fixed3 refDir= normalize( reflect( -_WorldSpaceLightPos0.xyz,worldNormal)) ;

//高光基本公式
fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb *pow(max(0,dot(viewDir,refDir)),_Gloss);

fixed3 color = UNITY_LIGHTMODEL_AMBIENT.xyz + specular;
return fixed4(color,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}

phong光照(逐顶点)

终于到了将漫反射和高光反射合二为一的时候了
其实就是把他俩相加起来而已

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
Shader "hxsd/PhongVertex"
{
Properties
{
_DiffuseColor("漫反射材质颜色", Color) = (1, 1, 1, 1)
_SpecularColor("高光反射材质颜色", Color) = (1, 1, 1, 1)
_Gloss("光晕系数", Range(4, 256)) = 10
}

SubShader
{
Pass
{
//设定光照模式为前向模式(才能正常获取光的颜色和光的方向)
Tags { "LightMode" = "ForwardBase" }

CGPROGRAM
#pragma vertex vert
#pragma fragment frag

//加载Cg语言的脚本,用来处理光照参数
//处理光照的Cg库文件(cginc扩展名),目录在Unity的安装目录下Editor/Data/CGIncludes/Lighting.cginc
#include "Lighting.cginc"

//导入漫反射材质颜色
fixed4 _DiffuseColor;
//导入高光材质颜色
fixed4 _SpecularColor;
//导入高光光晕大小系数
float _Gloss;

//如果在Cg编程中,顶点或片元着色器接收多个数值的时候,一般会用结构体实现
//从CPU接收到的数据
struct c2v
{
float4 vertex : POSITION; //从CPU接收到的模型空间下的点的位置
float3 normal : NORMAL; //从CPU接收到的当前点的模型空间下的法线向量
};

//因为在顶点着色器中,运算Phong光照模型运算
struct v2f
{
float4 pos : SV_POSITION; //经过顶点着色器计算后的,当前点的裁剪空间下的位置
fixed3 color : COLOR; //经过Phong光照模型运算后的当前点的颜色
};

//注意朱老师会把场景的灯开关关掉(小心)
//高洛徳着色(逐顶点光照),光照计算应该编写在顶点着色器中
v2f vert(c2v data)
{
//顶点着色器传递给片元着色器的数据结构体声明
v2f r;

//必须做的:将点从模型空间下,转换到裁剪空间下
r.pos = UnityObjectToClipPos(data.vertex);

//高光反射运算
//高光光照 = 光源的颜色 * 材质高光反射颜色 * 〖MAX(0,标准化后的观察方向向量 • 标准化后的反射方向)〗^ 光晕系数
//光反射方向:reflect(入射光的方向,当前点的法线方向)

//光照是在世界中发生,需要将所有的数值,变换到世界坐标系下,再运算
//_Object2World矩阵是Unity提供的,用于转换模型空间下到世界空间下的转换矩阵
//因为法线传递过来的是3x1的列矩阵,_Object2World是4x4的齐次矩阵,如果想做矩阵乘法,需要将齐次矩阵,变成3x3矩阵
fixed3 worldNormal = normalize(mul((float3x3)unity_ObjectToWorld, data.normal));

//观察方向(相机的位置点 - 被渲染点的位置,在世界空间下)
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, data.vertex).xyz);

//_WorldSpaceLightPos0是物体到光的方向
//光线反射的方向
fixed3 refDir = normalize(reflect(normalize(-_WorldSpaceLightPos0.xyz), worldNormal));

//高光计算公式
fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(viewDir, refDir)), _Gloss);

////////////////////////////////////////////////////////////////////////////////////////////////////

//兰伯特定律计算
//漫反射光照 = 光源的颜色 * 材质的漫反射颜色 * MAX(0, 标准化后物体表面法线向量 • 标准化后光源方向向量)

fixed3 diffuse = _LightColor0.rgb * _DiffuseColor * max(0, dot(worldNormal, normalize(_WorldSpaceLightPos0.xyz)));

//Phong光照模型,颜色的叠加
r.color = UNITY_LIGHTMODEL_AMBIENT.xyz + diffuse + specular;

return r;
}

fixed4 frag(v2f data) : SV_Target
{
//将顶点着色器计算好的颜色,传递给GPU
return fixed4(data.color, 1.0);
}

ENDCG
}
}

Fallback "Diffuse"
}

phong光照(逐顶点)

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
Shader "hxsd/PhongPixel"
{
Properties
{
_DiffuseColor("漫反射材质颜色", Color) = (1, 1, 1, 1)
_SpecularColor("高光反射材质颜色", Color) = (1, 1, 1, 1)
_Gloss("光晕系数", Range(4, 256)) = 10
}

SubShader
{
Pass
{
//设定光照模式为前向模式(才能正常获取光的颜色和光的方向)
Tags { "LightMode" = "ForwardBase" }

CGPROGRAM
#pragma vertex vert
#pragma fragment frag

//加载Cg语言的脚本,用来处理光照参数
//处理光照的Cg库文件(cginc扩展名),目录在Unity的安装目录下Editor/Data/CGIncludes/Lighting.cginc
#include "Lighting.cginc"

//漫反射材质颜色
fixed4 _DiffuseColor;
//导入高光材质颜色
fixed4 _SpecularColor;
//导入高光光晕大小系数
float _Gloss;

//如果在Cg编程中,顶点或片元着色器接收多个数值的时候,一般会用结构体实现
//从CPU接收到的数据
struct c2v
{
float4 vertex : POSITION; //从CPU接收到的模型空间下的点的位置
float3 normal : NORMAL; //从CPU接收到的当前点的模型空间下的法线向量
};

//因为在顶点着色器中,运算高光反射颜色
struct v2f
{
float4 pos : SV_POSITION; //经过顶点着色器计算后的,当前点的裁剪空间下的位置
float4 worldPos : TEXCOORD0; //借用纹理的语义,实现世界空间下,点的位置传递
float3 normal : NORMAL; //世界空间下,法线的方向
};

//注意朱老师会把场景的灯开关关掉(小心)
//高洛徳着色(逐顶点光照),光照计算应该编写在顶点着色器中
v2f vert(c2v data)
{
//顶点着色器传递给片元着色器的数据结构体声明
v2f r;

//必须做的:将点从模型空间下,转换到裁剪空间下
r.pos = UnityObjectToClipPos(data.vertex);

//世界空间下,点的位置
r.worldPos = mul(unity_ObjectToWorld, data.vertex);

//世界空间下,法线的方向向量
r.normal = mul((float3x3)unity_ObjectToWorld, data.normal);

return r;
}

fixed4 frag(v2f data) : SV_Target
{
//将顶点着色器传递过来的法线向量,标准化
fixed3 worldNormal = normalize(data.normal);
//使用相机位置减去顶点着色器计算的世界坐标系下的点的位置,并标准化
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - data.worldPos.xyz);
//计算光线反射方向
fixed3 refDir = normalize(reflect(normalize(-_WorldSpaceLightPos0.xyz), worldNormal));
//高光计算公式
fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(viewDir, refDir)), _Gloss);

//漫反射计算
fixed3 diffuse = _LightColor0.rgb * _DiffuseColor * max(0, dot(worldNormal, normalize(_WorldSpaceLightPos0.xyz)));

fixed3 color = UNITY_LIGHTMODEL_AMBIENT.xyz + diffuse + specular;

//将顶点着色器计算好的颜色,传递给GPU
return fixed4(color, 1.0);
}

ENDCG
}
}

Fallback "Diffuse"
}