顶点着色器主要是处理顶点的平移,缩放等位置或者大小相关的函数。
片段着色器主要处理的是像素的颜色。
我们先来看一个简单的例子。
首先我们通过"Create->3D Object->Plane"来创建一个面板。
案例一
我们来通过一个Shader来把这个面板变成红色
我们创建一个材质并修改名字为RedMaterial。在创建一个Shader并修改名字为RedShader。并把RedShader指定给RedMaterial。
我们来看一下RedShader中的代码:
Shader "Custom/RedShader"
{
SubShader {
//Pass 通道 主要是实现一些顶点和片段着色器功能
Pass {
//CG程序开始
CGPROGRAM
//声明顶点着色器函数名字为vert
#pragma vertex vert
//声明片段着色器的名字为frag
#pragma fragment frag
float4 vert(float4 v:POSITION) : SV_POSITION {
//将点转换到视图投影面上
return mul (UNITY_MATRIX_MVP, v);
}
fixed4 frag() : SV_Target {
//返回一个红色的颜色值
return fixed4(1.0,0.0,0.0,1.0);
}
//CG程序结束
ENDCG
}
}
}
一个Shader程序可以有多个SubShader,但最终只会执行一个,执行的时候GPU会从第一个SubShader开始检测支不支持该SubShader的功能,若支持,便执行它。若不支持,继续检测下一个SubShader。
Pass 通道 主要是实现一些顶点和片段着色器功能。一个SubShader中可以有多个Pass,这几个Pass都会执行。
声明顶点和片段的函数代码如下:
//声明顶点着色器函数名字为vert
#pragma vertex vert
//声明片段着色器的名字为frag
#pragma fragment frag
其中函数的名字我们是可以随便起的。
我们来看一下顶点着色器函数vert
其中SV_POSITION表示返回类型是一个点。
我们看到在顶点着色器函数中只有一句代码:
return mul (UNITY_MATRIX_MVP, v);
mul是矩阵相乘的函数。UNITY_MATRIX_MVP是model、view、projection三个矩阵相乘出来的4x4的 矩阵。
v是一个float4的变量,可理解成4x1的矩阵,两者相乘,则得出一个float4,这个值就是视角窗口的坐标值。
再来看一下片断着色器函数frag
SV_Target 表示返回的是一个颜色值。unity文档中是这样解释的:
然后就很容易衣接着一句话了,返回一个指定的颜色值
//返回一个红色的颜色值
return fixed4(1.0,0.0,0.0,1.0);
案例二 屏幕坐标
我们来实现一下这样的效果
下面是Shader代码
Shader "Custom/WindowCoordinates/Base" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
float4 vert(appdata_base v) : POSITION {
return mul (UNITY_MATRIX_MVP, v.vertex);
}
fixed4 frag(float4 sp:VPOS) : SV_Target {
return fixed4(sp.xy/_ScreenParams.xy,0.0,1.0);
}
ENDCG
}
}
}
我们看到有两个我们之前没见过的字段
appdata_base结构
unity文档中对appdata_base解释如下
我们看到这个结构体包括顶点的位置,法线,纹理坐标
_ScreenParams结构存有场景的宽高
我们在返回颜色值的时候进行了如下运算,就可以达到效果了。
return fixed4(sp.xy/_ScreenParams.xy,0.0,1.0);
案例三 纹理坐标
我们来实现下面的效果
我们来看一下Shader代码
Shader "Custom/TextureCoordinates/Base" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
struct vertexInput {
float4 vertex : POSITION;
float4 texcoord0 : TEXCOORD0;
};
struct fragmentInput{
float4 position : SV_POSITION;
float4 texcoord0 : TEXCOORD0;
};
fragmentInput vert(vertexInput i){
fragmentInput o;
o.position = mul (UNITY_MATRIX_MVP, i.vertex);
o.texcoord0 = i.texcoord0;
return o;
}
fixed4 frag(fragmentInput i) : SV_Target {
return fixed4(i.texcoord0.x,0.0,0.0,1.0);
}
ENDCG
}
}
}
这里出现了个新字段 texcoord0,这个存储了当前纹理的信息,其实就算我们没有指定纹理,我们也有一个默认的纹理,就像我们新创建出来的白色的Plane。
我们在片断着色器frag中把当前纹理坐标的x值作为颜色的红色值进行输出,纹理坐标值的范围是0-1.
案例四 纹理
在顶点和片段着色器中使用纹理和前面讲过的在表面着色器中使用纹理一样
我们来实现下面的效果
我们看一下Shader代码
Shader "Custom/TextureCoordinates/ActualTexture" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
struct vertexInput {
float4 vertex : POSITION;
float4 texcoord0 : TEXCOORD0;
};
struct fragmentInput{
float4 position : SV_POSITION;
float4 texcoord0 : TEXCOORD0;
};
fragmentInput vert(vertexInput i){
fragmentInput o;
o.position = mul (UNITY_MATRIX_MVP, i.vertex);
o.texcoord0 = i.texcoord0;
return o;
}
fixed4 frag(fragmentInput i) : SV_Target {
return tex2D(_MainTex, i.texcoord0.xy);
}
ENDCG
}
}
}
我们看到我们在属性Properties中定义了一个纹理字段_MainTex,我们会在材质的Inspector中看到如下的接口,我们可以把一张纹理图片赋值上去。
然后我们定义了两个结构体vertexInput和fragmentInput用于顶点着色器vert和片段着色器frag的输入输出。
上面tex2D 函数带有两个参数:一个取样器和一个包含2维坐标的两位浮点数,取样器就就是在这个坐标中进行取样。材质的坐标范围从左上角(0,0)到右下角(1,1)。这两个向量元素不用x、y表示而通常用u、v表示。
然后其余的代码大家应该都能看懂了 ):。
顶点片段着色器基本语法其实是很简单的,主要是一些算法,这些算法最终影响的就是绘制到屏幕上的颜色值。这些算法的数据来源可能是顶点法线,切线,纹理坐标,角度值等
案例五 切线
我们用切线实现下面的效果
我们在unity中创建一个小球Sphere
下面是Shader代码
Shader "Debug/Tangents" {
SubShader {
Pass {
Fog { Mode Off }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// vertex input: position, tangent
struct appdata {
float4 vertex : POSITION;
float4 tangent : TANGENT;
};
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
};
v2f vert (appdata v) {
v2f o;
o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
o.color = v.tangent * 0.5 + 0.5;
return o;
}
fixed4 frag (v2f i) : SV_Target { return i.color; }
ENDCG
}
}
}
我们看到在结构体appdata中有这样一句
float4 tangent : TANGENT;
TANGENT就表示字段tangent是一个切线,然后我们在顶点着色器vert给顶点颜色赋值的时候用到了这个切线值
o.color = v.tangent * 0.5 + 0.5;
这样就达到了我们想要的效果。