概述
在学习笔记(一)中,说明了GPU渲染流水线中有三个可编程的部分,亦即四个着色器。分别是顶点着色器,曲面细分着色器,几何着色器,片元着色器。但是在Unity shader的教程中,我只看到了关于顶点着色器和片元着色器的编程,所以本文只记录关于顶点着色器和片元着色器的学习笔记。另外还有一个着色器需要提及,就是表面着色器(Surface Shader)。这是Unity自己创造的一种着色器类型,但实际上底层实现仍然是使用顶点/片元着色器。
Unity的着色器类型
在Project视图中创建一个Shader,会出现几种可选的类型:
- Standard Surface Shader:标准的表面着色器模板,包含一个标准光照模型
- Unlit Shader:不包含光照(但包含雾效)的基本的顶点/片元着色器
- Image Effect Shader:各种屏幕后处理模板
- Compute Shader:会产生一种特殊的Shader文件,这类Shader文件旨在利用GPU的并行性来进行一些与常规渲染流水线无关的计算
顶点着色器
在Unity Shader中,指定渲染流水中使用的顶点着色器代码如下:
Shader "Assets/Shaders/SimpleColorShader" {
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
float4 vert(float4 v : POSITION) : SV_POSITION {
return UnityObjectToClipPos(v);
}
ENDCG
}
}
}
其中,#pragma vertex表示指定一个顶点着色器,而vert就是实现的顶点着色器名称,亦即一个函数。函数的具体含义以后再做学习笔记。
一个顶点着色器接收一组模型顶点坐标输入,同时必须输出齐次裁剪空间的有颜色的顶点。从模型顶点坐标到齐次裁剪空间坐标的变换总共经历三个子变换,分别是世界变换(World Transformation),视图变换(View Transformation)和投影变换(Projection Transformation),每个变换都由一个矩阵完成,这三个矩阵统称:MVP变换矩阵。
注意上述代码中,有这么一行return UnityOjectToClipPos(v);
。从函数的名称上看,意思是将Unity游戏物体(Object)变换到裁剪(Clip)坐标。实际上,这段代码原本是return mul(UNITY_MATRIX_MVP, v);
。即将原输入顶点v
乘以MVP矩阵,变换到齐次裁剪空间坐标。Unity自动将这行代码替换成了UnityOjectToClipPos(v);
。可见,这两个代码实际上等效的,就是一个坐标变换。
片元着色器
这里的片元可以理解为像素,但并不是一个真正的像素,它包含了比像素更多的信息。关于片元着色器(Fragment Shader)在维基百科上有这么一段话:
Pixel shaders, also known as fragment shaders, compute color and other attributes of each "fragment" - a unit of rendering work affecting at most a single output pixel.
它表示,一个片元着色器计算每个片元的颜色和其他属性。一个片元着色器一次只处理一个片元,一次只输出一个片元的颜色值(affecting at most a single output pixel)。
一个完整的Unity Shader还需要包含片元着色器的代码,我们将片元着色器的代码加到上面Unity Shader代码中,变成:
Shader "Assets/Shaders/SimpleColorShader" {
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v : POSITION) : SV_POSITION {
return UnityObjectToClipPos(v);
}
float4 frag() : SV_Target {
return fixed4(1.0, 1.0, 1.0, 1.0);
}
ENDCG
}
}
}
第二个#pragma
指定片元着色器为frag()
函数。顶点着色器将坐标变换到齐次裁剪空间坐标系上,使得模型能够显示在屏幕上,然后片元着色器为模型显示在屏幕上的每个像素指定了一个颜色(1.0,1.0,1.0,1.0)。这很明显是一个RGBA值,全是 1 ,代表白色。
新建一个物体,新建一个材质,然后指定将这个Shader脚本拖到该材质下,最后将该材质赋予新建的物体。
我们会看到场景的物体就是纯白色。
注意,左边的球采用的是上述的shader,右边的球是使用默认的材质,默认的shader。为了放大区别,环境光强度我调高到了10,颜色调成了暗红色。显然,左边的球并不受环境光的影响,因为Shader代码中,压根儿没有对环境光因素做处理,仅仅就是输出了一个固定的颜色。
我们把片元着色器的返回的颜色值改成(0,0,1.0,1.0)。会发现球的颜色变了。
总结
在Unity Shader中编写顶点着色器和片元着色器的代码被包含在#CGPROGRAM
中,它指明下述代码实际上就是CG(C for graphics)的语法。但是CG的特性又不是全部都能在Unity中使用。不过仍然建议优先熟悉CG,这样编写Unity shader不至于一头雾水。