1.一个最简单的顶点/片元着色器
1.1 顶点/片元着色器的基本结构
//定义Shader的位置和名称
Shader " Unity Shaders Book/Chapter 5/ Simple Shader"
{
//可以不定义Properties,但是必须有至少一个SubShader
SubShader
{
Pass
{
//使用CGPROGRAM和ENDCG包裹CG代码片段
CGPROGRAM
//编译指令,指明顶点着色器代码是vert函数
#pragma vertex vert
//编译指令,指明片元着色器代码是frag函数
#pragma fragment frag
//顶点着色器函数,v为形参名称,POSITION为CG/HLSL的语义,代表将模型的顶点坐标输入,SV_POSITION也是CG/HLSL的语义,代表输出为裁剪空间中的顶点坐标
float4 vert(float4 v : POSITION) : SV_POSITION
{
return UnityObjectToClipPos(v);
}
//片元着色器函数,SV_Target语义代表将输出颜色存储到一个渲染目标中
fixed4 frag() : SV_Target
{
return fixed4(1.0, 0.0, 0.0, 1.0);
}
ENDCG
}
}
}
1.2 模型数据从哪里来
在着色器中使用语义(semantics)定义数据的来源,想得到更多的数据,我们可以定义一个结构体参数获取这些数据:
Shader " Unity Shaders Book/Chapter 5/ Simple Shader"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//使用结构体定义参数
struct a2v
{
//POSITION语义,代表使用模型空间的顶点坐标填充这个变量
float4 vertex : POSITION;
//NORMAL语义,代表使用模型空间的法线方向填充这个变量
float3 normal : NORMAL;
//TEXCOORD0语义,代表使用模型的第一套纹理坐标填充这个变量
float4 texcoord : TEXCOORD0;
};
//顶点着色器函数
float4 vert(a2v v) : SV_POSITION
{
return UnityObjectToClipPos(v.vertex);
}
fixed4 frag() : SV_Target
{
return fixed4(1.0, 0.0, 0.0, 1.0);
}
ENDCG
}
}
}
1.3 顶点着色器和片元着色器之间的通信
Shader " Unity Shaders Book/Chapter 5/ Simple Shader"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
//在结构体中存储顶点着色器的输出
struct v2f
{
//SV_POSITION语义代表这里存储了裁剪空间的位置信息
float4 pos : SV_POSITION;
//COLOR0语义用于存储颜色信息
fixed3 color : COLOR0;
};
v2f vert(a2v v)
{
//声明一个结构体
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//根据发现方向计算一个颜色并存储
o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//将插值后的颜色显示到屏幕上
return fixed4(i.color, 1.0);
}
ENDCG
}
}
}
注意:
1)在结构体v2f中,必须包含语义为SV_POSITION的float4类型变量,否则片元着色器无法得到片元的位置信息;
2)顶点着色器输出的v2f结构体中存储的信息和片元着色器中输入的v2f结构体中的信息并不一致,片元着色器得到的点可能是三角片元内的点,它的信息是经过三角形顶点的插值得到的.
1.4 如何使用属性
Shader " Unity Shaders Book/Chapter 5/ Simple Shader"
{
Properties
{
//声明一个color类型的属性
//属性名_Color, 在Shader属性面板中显示名称Color Tint,属性类型Color, 属性默认值(1.0, 1.0, 1.0, 1.0)
_Color("Color Tint", Color) = (1.0, 1.0, 1.0, 1.0)
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//在CG代码中,需要定义一个与属性名称相同,类型匹配的变量,才能使用这个变量
fixed4 _Color;
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
fixed3 color : COLOR0;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 c = i.color;
//使用_Color属性控制颜色输出
c *= _Color.rgb;
return fixed4(c, 1.0);
}
ENDCG
}
}
}
ShaderLab的属性类型和Cg的变量类型匹配关系如下:
2.Unity提供的内置文件
在Unity中,包含文件的后缀是.cginc.在编写Shader是,可以使用#include指令将这些文件包含进来,类似于C#包含其他命名空间.#include指令在CGPROGRAM和ENDCG中间的代码块中定义.
3.Unity提供的Cg/HLSL语义
3.1 语义的概念
语义(semantics)是一个赋给Shader输入和输出的字符串,这个字符串表达了这个参数的含义.通常情况下,语义可以让我们指定Shader从哪里读取数据,或者指定Shader将数据输出到哪里.
一些语义有特殊的含义规定,如TEXCOORD0,但是其他语义则没有这样的含义,如COLOR0,这些语义修饰的变量Shader并不关心它是用来做什么的,我们可以自己决定这些变量的用途.
在DirecX 10以后,出现了一种新的语义类型:系统数值语义(system-value semantics).系统数值语义都是以SV(system value)开头的,如SV_POSITION.在Unity中有时系统数值语义和普通语义是可以替换的,但是最好使用系统数值语义,因为某些平台上必须使用系统数值语义.
3.2 Unity支持的语义
3.3 CG的数据类型
Cg支持7种基础数据类型:
1)float, 32位浮点数据
2)half, 16位浮点数据
3)fixed, 12位定点数据
4)int, 32位整形数据
5)bool, 布尔数据
6)sampler*, 纹理对象的句柄,分为6类:sampler, sampler1D, sampler2D, sampler3D, samplerCUBE, samplerRECT
7)string, 字符类型(几乎不会用到)