通过这里,你可以学习到UnityShader基本语法,CG语义,GPU渲染流水线等编写着色器的基础知识
开发环境:Unity5.0或者更高
1. Unity shader 基本语法
2. 顶点着色器与片元着色器参数传递
3. 基本shader语义
4. GPU渲染流水线
Camera
与Cube
Material
和Shader
Shader "Toturial/SolidColor"
{
SubShader
{
Pass
{
CGPROGRAM
ENDCG
}
}
}
此时Console出现了两条警告信息,第一条表示该Shader代码中的所有SubShader都不支持,第二条表示需要该Shader对GPU顶点着色器和片元着色器进行编程,在
Shader warning in 'Toturial/SolidColor': Toturial/SolidColor shader is not supported on this GPU (none of subshaders/fallbacks are suitable)
Shader warning in 'Toturial/SolidColor': Both vertex and fragment programs must be present in a shader snippet. Excluding it from compilation
代码说明
管线说明(重要!!!)
- Unity将顶点数据、顶点颜色等属性传入Shader ,进入GPU顶点着色器处理,之后进入裁剪阶段,裁剪完成后数据将进入片元着色器,最后输出到屏幕
- 其中顶点着色器和片元着色器是可编程的(
表示可以在Shader中写代码进行控制
)- 裁剪阶段是可配置的(
表示可以在Shader中使用一些特定的语法进行设置 例如 Cut off
)
在我们对顶点着色器(Vertex Shader)以及片元着色器(Fragment Shader)进行编程之前,需要先从获取对象的属性,例如图片的顶点位置和颜色,模型的顶点位置、法线方向、颜色等。
我们通过语义绑定来获取模型数据或者图片的位置和颜色
语义说明
类型 变量名 :POSITION;
获取输入的顶点(例如:float4 vertex : POSITION)
类型 变量名 : COLOR
获取输入的颜色,SpriteRenderer 和Image组件上的Color:SV_POSITION
顶点着色器输出的顶点位置,SV表示System Value的缩写:SV_TARGET
片元着色器输出的值
了解了语义之后,我们需要定义函数为顶点着色器和片元着色器进行编程控制。
宏定义
#pragma vertex vert
定义顶点着色器,vert为函数名 可以改成其他的
#pragma fragment frag
定义片元着色器 ,frag为函数名 可以改成其他的
之后我们在CGPROGRAM … ENDCG中写入如下代码:
Shader "Toturial/SolidColor"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 vertex : POSITION): SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
fixed4 frag() : SV_TARGET
{
return fixed4(0,0,1,1);
}
ENDCG
}
}
}
其中UnityObjectToClipPos(vertex)表示将顶点位置从对象空间转换到裁剪空间,这一步是为了将3D的场景最终输出到一张2D的屏幕上。参考Unity官方文档 https://docs.unity3d.com/cn/current/Manual/SL-BuiltinFunctions.html
最终返回了蓝色fixed4(0,0,1,1);
,之后给场景中的Cube添加材质Custom_SolidColor
上面的代码中我们只用到了顶点位置:POSITION
语义,当我们需要多个参数的时候,例如还需要颜色,法线方向等我们可以使用结构体
给顶点着色器函数传参
修改 CGPROGRAM … ENDCG 中的代码,首先我们定义一个结构体名为a2v,他是application to vertex 的缩写,意思是表示数据从应用传递到顶点着色器
struct a2v
{
float4 vertex : POSITION;
fixed4 color : COLOR;
};
我们再定义一个结构体用于将数据从顶点着色器输出后传递到片元着色器,结构体名为v2f ,它是vertex to fragment的缩写,
struct v2f
{
float4 pos : SV_POSITION;
fixed4 color : COLOR;
};
之后修改顶点着色器函数返回值和参数
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.color;
return o;
}
完整代码
Shader "Toturial/SolidColor"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct a2v
{
float4 vertex : POSITION;
fixed4 color : COLOR;
};
struct v2f
{
float4 pos : SV_POSITION;
fixed4 color : COLOR;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.color;
return o;
}
fixed4 frag(v2f i) : SV_TARGET
{
return fixed4(0,0,1,1);
}
ENDCG
}
}
}
在场景中创建一个Image与2D Sprite
将Image
组件的Material设置为Custom_SolidColor
将SpriteRenderer
的Material设置为Custom_SolidColor
此时出现了一个报错
Material 'Custom_SolidColor' with Shader 'Toturial/SolidColor' doesn't have a texture property '_MainTex'
这是因为图片组件会将Sprite设置给Shader中的_MainTex,所以我们在Properties中需要定义一个变量名为_MainTex类型为2D的参数,将以下代码写在SubShader上面。
Properties
{
_MainTex("MainTex",2D) = "white"{}
}
代码说明
接下来我们给材质属性添加一个设置颜色的功能,首先在Properties属性添加Color
Properties
{
_MainTex("MainTex",2D) = "white"{}
_Color("Color",Color) = (1,1,1,1)
}
之后我们需要在CGPROGRAM… ENDCG 中定义变量用来获取Properties里的_Color值,在 struct a2v上面定义
#pragma vertex vert
#pragma fragment frag
fixed4 _Color;
struct a2v
{
修改frag函数
fixed4 frag(v2f i) : SV_TARGET
{
return _Color;
}
Shader "Toturial/SolidColor"
{
Properties
{
_MainTex("MainTex",2D) = "white"{}
_Color("Color",Color) = (1,1,1,1)
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
fixed4 _Color;
struct a2v
{
float4 vertex : POSITION;
fixed4 color : COLOR;
};
struct v2f
{
float4 pos : SV_POSITION;
fixed4 color : COLOR;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.color;
return o;
}
fixed4 frag(v2f i) : SV_TARGET
{
return _Color;
}
ENDCG
}
}
}
最终看到效果
接下来我们将使用Image.Color和SpriteRenderer修改材质的颜色,因为我们在a2v
结构体中对color
进行了:Color
语义绑定,之后将a2v结构体传入顶点着色器vert,
在顶点着色器中实例化了一个v2f
结构体并对color
进行赋值,之后将v2f传递到片元着色器
中,所以我们需要把片元着色器frag
中的返回值修改为i.color
就可以达到效果。
fixed4 frag(v2f i) : SV_TARGET
{
return i.color;
}
修改完后我们修改场景中的Image组件上的颜色为#FFDA00
,将SpriteRenderer上的颜色修改为#3889F1
Shader "Toturial/SolidColor"
{
Properties
{
_MainTex("MainTex",2D) = "white"{}
_Color("Color",Color) = (1,1,1,1)
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
fixed4 _Color;
struct a2v
{
float4 vertex : POSITION;
fixed4 color : COLOR;
};
struct v2f
{
float4 pos : SV_POSITION;
fixed4 color : COLOR;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.color;
return o;
}
fixed4 frag(v2f i) : SV_TARGET
{
return i.color;
}
ENDCG
}
}
}