Shader结构(路径)
Shader "Shader Test/Chapter 5/SimpleShader" {
}
代码的第一行通过 Shader 语义定义了这个 Unity Shader 的名字 —— “Shader Test/Chapter 5/SimpleShader”;
保持良好的命名习惯有助于我们在为材质球选择 Shader 时快速找到自定义的 Unity Shader;
Properties 语义并不是必需的,可以选择不声明任何材质属性。
Shader 的简单实现(基本格式)
Shader "Shader Test/Chapter 5/SimpleShader"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// 顶点着色器代码
// POSITION : SV_POSITION均为 CG/HLSL 中的语义(semantics)
// POSITION : 输入模型的顶点坐标
// SV_POSITION:输出裁剪空间下中的顶点坐标
float4 vert(float4 v:POSITION) :SV_POSITION
{
// 把顶点坐标从模型空间转换到裁剪空间中
// 5.6版本前写法 mul(UNITY_MAREIX_MVP, v);
// UNITY_MATRIX_MVP 矩阵是 Unity 内置的模型·观察·投影矩阵
return UnityObjectToClipPos(v);
}
// 片元着色器代码
// SV_Target : HLSL 的系统语义,把输出的颜色存储到一个渲染目标中,输出到默认的帧缓存中
fixed4 frag() : SV_Target
{
return fixed4(0.5,0.5,0.5,0.5);
}
ENDCG
}
}
}
Shader 的实现(结构体定义多参数输入)
Shader "Shader Test/Chapter 5/SimpleShader"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct a2v
{
// POSITION 语义告诉Unity,用模型空间的顶点填充 vertex 变量
float4 vertex : POSITION;
// NORMAL 语义告诉Unity,用模型空间的法线方向填充 normal 变量
float3 normal : NORMAL;
// TEXCOORD0 语义告诉Unity,用模型的第一套纹理坐标填充 texcoord 变量
float4 texcoord : TEXCOORD0;
};
// 使用一个结构体定义顶点着色器的输出
struct v2f
{
// SV_POSITION 语义告诉Unity,pos 里包含了顶点在裁剪空间中的位置信息
float4 pos : SV_POSITION;
// COLOR0 语义可以用于存储颜色信息
float3 color :COLOR0;
};
v2f vert(a2v v)
{
// 声明输出结构
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
// v.normal 包含了顶点的法线方向,其分量范围在[-1.0, 1.0]
// 下面的代码把分量范围映射到[0.0, 1.0]
// 存储到o.color中传递给片元着色器
o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
// 将插入后的 i.color 显示到屏幕上
return fixed4(i.color, 1.0);
}
ENDCG
}
}
}
模型数据的来源
Unity 会根据这些语义来填充这个结构体。 对于顶点着色器的输入, Unity 支持的语义有: POSITION, TANGENT, NORMAL, TEXCOORDO,TEXCOORD1, TEXCOORD2, TEXCOORD3, COLOR 等。
POSJTION, TANGENT, NORMAL 这些语义中的数据来源:
是有使用该材质的 Mesh Render 组件提供的。在每帧调用 Draw Call 时,Mesh Render 组件会把他负责的渲染的模型数据发送给 Unity Shader。(模型通常包含了一组三角面片,每个三角面片由三个顶点构成,每个顶点包含了顶点位置、法线、切线、纹理坐标、顶点颜色等数据。)
自定义结构体
为了创建一个自定义的结构体,我们必须使用如下格式来定义它:
struct StructName {
Type Name : Semantic;
Type Name : Semantic;
......
};
顶点着色器和片元着色器之间的通信
结构体 a2v:包含顶点着色器需要的模型数据
结构体 v2f:用于顶点着色器和片元着色器之间传递信息
顶点着色器的输出结构中,必须包含一个变量,它的语义是 SV_POSITION。否则渲染器将无法得到裁剪空间中的顶点坐标,也就无法把顶点渲染到屏幕上。
注:顶点着色器是逐顶点调用的,而片元着色器是逐片元调用的。 片元着色器中的输入实际上是把顶点着色器的输出进行插值后得到的结果。
Shader 的实现(属性使用)
实现在材质面板显示一个颜色拾取器,从而可以直接控制模型在屏幕上显示的颜色。
新增颜色属性前:
新增颜色属性后:
Shader "Shader Test/Chapter 5/SimpleShader"
{
Properties
{
// 声明一个 Color 类型的属性
_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;
float3 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
}
}
}
新增了 Properties 语义块,并在其中声明了一个属性_Color,它的类型是Color, 初始值是(1.0, 1.0, 1.0, 1.0), 对应白色。 为了在CG代码中可以访问它,我们还需要在CG代码片段中提前定义一个新的变量,这个变量的名称和类型必须与 Properties 语义块中的属性定义相匹配;
ShaderLab中属性的类型和CG中变揽的类型之间的匹配关系如表:
uniform 关键字
uniform fixed4 _Color;
uniform 关键词是 CG 中修饰变量和参数的一种修饰词,它仅仅用于提供一些关于该变量的初始值是如何指定和存储的相关信息(这和其他一些图像编程接口中的 uniform 关键词的作用不太一样)。在 Unity Shader 中, uniform 关键词是可以省略的。