文章目录
Unity-shader学习笔记(一)
1 顶点着色器(Vertex Shader)
实现的是用一种通用的编程方式去操作顶点。一般在shader中都会将顶点着色器的参数封装为一个结构体,
struct appdata_t{
......
UNITY_VERTEX_INPUT_INSTANCE_ID
}
结构体中的属性都是MeshRenderer(网格渲染器)在每次DrawCall的时候传递过来,我们在结构体中需要用到什么属性,就在结构体中定义什么属性,一般包含一下属性:
float4 vertex:POSITION//顶点位置
float3 normal:NORMAL//顶点法线方向
float4 texcoord:TEXCOORD0//第一纹理坐标
float4 texcoord1:TEXCOORD1//第二纹理坐标
float4 tangent:TANFENT//切线向量
float4 color:COLOR0//每个顶点(per-vertex)颜色
在Unity3D的UnityCG.cginc中,有几种常见的定点结构体定义
appdata_base{//包含顶点位置,法线和一个纹理坐标。
float4 vertex:POSITION;//顶点位置
float3 normal:NORMAL;//顶点法线方向
float4 texcoord:TEXCOORD;//纹理坐标
UNITY_VERTEX_INPUT_INSTANCE_ID//为顶点实例化一个ID
};
appdata_tan{//包含顶点位置,切线,法线和一个纹理坐标。
float4 vertex:POSITION;//顶点位置
float3 normal:NORMAL;//顶点法线方向
float4 texcoord:TEXCOORD;//纹理坐标
float4 tangent:TANGENT;//切线
UNITY_VERTEX_INPUT_INSTANCE_ID//为顶点实例化一个ID
};
appdata_full{//包含位置、法线、切线、顶点色和两个纹理坐标。
float4 vertex:POSITION;//顶点位置
float4 tangent:TANGENT;//切线
float3 normal:NORMAL;//顶点法线方向
float4 texcoord:TEXCOORD0;//纹理坐标
float4 texcoord1:TEXCOORD1;//第二纹理坐标
float4 texcoord2:TEXCOORD2;//第三纹理坐标
float4 texcoord3:TEXCOORD3;//第四纹理坐标
fixed4 color:COLOR0;//顶点颜色
UNITY_VERTEX_INPUT_INSTANCE_ID//为顶点实例化一个ID
};
appdata_img{//包含位置和一个纹理坐标。
float4 vertex:POSITION;//顶点位置
float4 texcoord:TEXCOORD0;//纹理坐标
UNITY_VERTEX_INPUT_INSTANCE_ID//为顶点实例化一个ID
};
基于上述四个已有的结构体,可以发现vertex顶点位置是都有的,所以在我们自己定义一个顶点结构体的时候,至少要有vertex顶点位置的信息。
其次,上述四种结构体都是都是用于顶点着色器的输入的,就可以发现,顶点着色器的输入有:顶点位置、顶点切线、顶点法线、纹理坐标。
我们在自定义了一个顶点着色器的输入参数的结构体后,我们同时也需要定义一个用于将数据输出至片元着色器的结构体,一般命名为v2f
struct v2f{
float4 pos:SV_POSITION;//输出顶点在裁剪空间中的位置
float4 texcoord:TEXCOORD; //输出纹理坐标
fixed4 color:COLOR0;//输出颜色
......
UNITY_VERTEX_INPUT_INSTANCE_ID//为顶点实例化一个ID
UNITY_VERTEX_OUTPUT_STEREO//声明该顶点是否位于视线域中,来判断这个顶点是否输出到片段着色器
}
v2f仅仅是一个顶点的信息,最终这个顶点着色器输出的内容是由之后的函数来决定,例如下面这个完整的顶点座着色器:
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;//返回的是一个对象
}
在这个顶点着色器中,我们就可以发现,输出的对象o的颜色是根据输入的对象v的法线进行插值计算处理。
在《Unity Shader入门精要》一书中讲三角形遍历时,提到了对三角形三个顶点进行插值,它这里就是对其深度进行插值处理,这里的深度就是顶点的Z值。
数据从顶点着色器输出后,进行裁剪(Clipping)、屏幕映射(Screen Coordinates)操作后传送至三角形遍历,在这个阶段会根据上一个阶段的计算结果对顶点所构成的几何图形进行像素检查,检查每个像素是否会被几何图形所覆盖,如果覆盖就会生成一个片元,同时使用顶点信息对覆盖区域的像素进行插值处理。其输出结果是每一个片元的状态合集,包括屏幕坐标、深度信息、法线、纹理坐标等,这些数据会被用于计算每个像素的最终颜色。
2 片元着色器(Fragment Shader)
还有一个名字叫做像素着色器(Pixel Shader)。由于前面阶段的所有操作都不会对屏幕上每个像素进行真正意义上的颜色值的处理,只会产生一系列的数据来表示这个几何图形是如何覆盖屏幕上的像素的,此时就需要片元着色器进行颜色上的处理。
根据前面的分析我们知道输入片元着色器的数据是在三角形遍历阶段对顶点信息进行插值得到的结果,输出的就是最终的像素颜色值,以RGB的向量形式给出。同时它还会对顶点着色器阶段得到的顶点的纹理坐标进行纹理采样。
片元着色器的最主要的缺点就是它仅可以影响单个片元。
3 逐片元操作(Per-Fragment Operation)
也称作输出合并阶段(Output-Merger )。
这是个奇妙的阶段,正如别名中的Merger,我们要去合并,那么合并的是什么。
我们知道这几个阶段都是在GPU中进行处理的,但并不是所有的片元都能进入GPU,还需要先解决每个片元的可见性问题。所以,逐片元操作的第一步就是决定每个片元的可见性,在这一步会涉及很多测试工作,最常见的也是被使用最多的就是模板测试(Stencil Test)和深度测试(Depth Test)。
3.1 模板测试
什么是模板测试呢?举个例子,我们都知道印刷的时候需要制版,之后才能进行印刷。同理,每次模板测试或者模板缓冲,都需要制作好特定的模板,然后套上模板在屏幕上进行绘制,具体流程如下: