参考文章1:https://www.cnblogs.com/lixiang-share/p/5025662.html
参考文章2:https://blog.csdn.net/weixin_39706943/article/details/81485758
1.Shader简述
(1)CPU(Central Processing Unit,中心处理器):主要负责操作系统和应用程序。
(2)GPU(Graphics Processing Unit,图形处理器):主要负责跟显示相关的数据处理。
(3)与显示相关数据处理使用GPU的优点:GPU具有高并行结构,所以GPU在处理图形数据和复杂算法方面拥有比CPU更高的效率。CPU大部分面积为控制器和寄存器,与之相比,GPU拥有更多的ALU(Arithmetic Logic Unit,逻辑运算单元)用于数据处理,这样的结构适合对密集型数据进行并行处理。
(4)Shader(着色器):可编程图形管线(图形管线:计算机处理图形显示的处理流水线)。
(5)Shader主要分为:Vertex Shader和fragment Shader,即定点Shader和片段Shader。
(6)Shader 的主流编程语言。主流的Shader编程语言主要有HLSL、GLSL、CG。下面简单说一下区别:HLSL(High Level Shader Language)是微软基于DX的作品,只能运行在Windows平台上。GLSL(OpenGL Shading Language),OpenGL着色语言,是用来在OpenGL中着色编程的语言(OpenGL是个定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口),是跨平台的着色器语言。到这里,我们已经可以发现有一个比较麻烦的问题出现了,就是我们底层图形驱动限制了上层的编程语言,一旦想要改动图形驱动库,那就不得不重写整个Shader Files,此时CG就应运而生了,CG在HLSL和GLSL上做了进一步封装,屏蔽了上层的着色器语言对底层图形库的依赖。
(7)Unity Shader。ShaderLab其实是Unity对Shader语法结构的一种包装,其中支持三种Shader:surface Shader、Vertex and Fragment Shader 和 Fixed function shader 。Fixed function shader 是一种比较“保守”的Shader(兼容性最好),vertex and fragment Shader可以只用HLSL或GLSL或CG语言区编写,surface shader是对Vertex and fragment的一种语法包装,最终也会被翻译中Vertex and fragment Shader。(以上更具体信息的可以参考官方文档http://docs.unity3d.com/Manual/index.html)
2.图形学基础
(1)3D数学基础:见https://mp.csdn.net/postedit。
(2)简单的图形学应用:光照剔除、漫反射和高光的实现方式。
(3)光照剔除:我们的视角即从物体到摄像机的向量,如果法线N与视线E形成的角度小于90度,那么观察者应是大约是在正面,反之大于90度应在该面的反面,此时应是无法观察到物体的(法线的求得使用向量差乘,角度计算可以使用向量的点乘),这时候就需要将其剔除了。
(4)漫反射(Diffuse 是投射在几盒体表面上的光向各个方向反射的现象),可以简单理解成光照对物体表面颜色的影响(在Unity中默认的Shader其实就是漫反射加环境光的综合作用)。计算光照对物体颜色的影响:使用法线和光向量(必须先标准化)的点乘作为影响该区域颜色的因子,这样再乘以该光源的颜色信息就可以得到对应受光照影响后的颜色了。
(5)高光(Specular 光源照射到物体然后反射到人的眼睛里时,物体上最亮的那个点就是高光),从定义就可以得出高光其实和反射光与视角相互作用形成的,同样的我们在计算高光也是利用同样的原理:由入射光求反射光、再计算反射光和视向量的点乘得出影响因子,最后算出高光强度。
3.Unity Shader语法基础
(1)Cg基本数据类型
float 32位浮点数
half 16位浮点数
int 32位整型
fixed 12位定点数
bool 布尔数据
simpler* 纹理对象的句柄( the handle to a texture object ) ,分为 6 类:
sampler, sampler1D, sampler2D, sampler3D, samplerCUBE, 和 samplerRECT
string 字符类型(几乎不使用)
例如float4,bool4等
向量数据类型,向量长度不能超过4元,可以有float1,float2,float3,float4,没有float5及以上
例如float4x4,float2x3等
矩阵数据类型,最大维数不超过4*4阶矩阵
(2)类型转换:
Cg 中的类型转换和 C 语言中的类型转换很类似。 C 语言中类型转换可以是强制类型转换,也可以是隐式转换,如果是后者,则数据类型从低精度向高精度转换。在 Cg 语言中也是如此。
float a = 1.0;
half b = 2.0;
float c = a+b; //等价于 float c = a + (float)b;
当有类型变量和无类型常量数据进行运算时,该常量数据不做类型转换,例如:
float a = 1.0;
float b = a + 2.0; //2.0 为无类型常量数据,编译时作为 float 类型
Cg 语言中对于常量数据可以加上类型后缀,表示该数据的类型,例如:
float a = 1.0;
float b = a + 2.0h; //2.0h 为 half 类型常量数据,运算是需要做类型转换
常量的类型后缀有3种:
f:表示float
h:表示half
x:表示fixed
(3)数组
数组数据类型在Cg中的作用:作为函数的形参,用于大量数据的传递,例如:顶点参数数组、光照参数数据等。
(4)结构体
结构体的声明以关键字 struct 开始,然后紧跟结构体的名字,接下来是一个大括号,并以分号结尾(不要忘了分号) 。大括号中是结构体的定义,分为两大类:成员变量和成员函数。
struct myAdd
{
float val;
float add(float x)
{
return val + x;
}
};
myAdd s;
使用符号“.”引用结构体的成员变量和成员函数:
float a = s.value;
float b = s.add(a);
一般来说 ,Cg 的源代码都会在文件首部定义二个结构体,分别用于定义输人和输出的类型,这二个结构体定义与普通的 C 结构定义不同,除了定义结构体成员的数据类型外,还定义了该成员的绑定语义类型( Binding Semantics) ,所谓绑定语义类型是为了与宿主环境进行数据交换的时候识别不同数据类型的。 目前Cg 支持的绑定语义类型包括 POSTION 位置 ) , COLOR( 颜色 ) , NORMAL( 法向量 ) , Texcoord( 纹理坐标 ) 等类型。
(5)Cg语言操作符
1.关系操作符:<、<=、==、>=、>、!=
2.逻辑操作符:&&、||、!
3.数学操作符:+、-、~、*、/、%、++、--、+=、-=、*=、/=
4.移位操作符
Cg 语言中的移位操作符,功能和 C 语言中的一样,也可以作用在向量上,但是向量类型必须是 int 类型。例如:
int2 a = int2(0.0,0.0);
int2 b = a>>1;
5.Swizzle 操作符
可以使用 Cg 语言中的 swizzle 操作符( . )将一个向量的成员取出组成一个新的向量。 swizzle 操作符被 GPU 硬件高效支持。 swizzle 操作符后接 x 、 y 、 z 、 w ,分别表示原始向量的第一个、第二个、第三个、第四个元素; swizzle操作符后接 r 、 g 、 b 和 a 的含义与前者等同。不过为了程序的易读性,建议对于表示颜色值的向量,使用 swizzle 操作符后接 r 、 g 、 b 和 a 的方式。
举例如下:
float4(a, b, c, d).xyz 等价于 float3(a, b, c)
float4(a, b, c, d).xyy 等价于 float3(a, b, b)
float4(a, b, c, d).wzyx 等价于 float4(d, c, b, a)
float4(a, b, c, d).w 等价于 float d
值得注意的是, Cg 语言中 float a 和 float1 a 是基本等价的,两者可以进行类型转换; float 、 bool 、 half 等基本类型声明的变量也可以使用 swizzle 操作符。例如:
float a = 1.0;
float4 b = a.xxxx;
注意: swizzle 操作符只能对结构体和向量使用,不能对数组使用,如果对数组使用 swizzle 操作符则会出现错误信息: error C1010: expression left of . ” x ” is not a struct or array 。
要从数组中取值必须使用 [] 符号。例如:
float a[3] = {1.0,1.0,0.0};
float b = a[0]; // 正确
float c = a.x; // 编译会提示错误信息
(6)控制流语句
条件语句有: if 、 if-else ;循环语句有: while 、 for 。 break 语句可以和在 for 语句中使用。