知识点:
1.Shaders (重)
着色器是运行在 GPU 上的小程序。 这些程序针对图形管道的每个特定部分运行。 从基本意义上讲,着色器只不过是将输入转换为输出的程序。 着色器也是非常孤立的程序,因为它们不允许相互通信; 他们唯一的交流是通过他们的输入和输出。
2.GLSL(OpenGL着色语言OpenGL Shading Language)
着色器是用类 C 语言 GLSL 编写的。 GLSL 专为与图形一起使用而量身定制,并包含专门针对矢量和矩阵操作的有用功能。
着色器总是以版本声明开始,然后是输入和输出变量、制服及其主要功能的列表。 每个着色器的入口点都在其主函数处,我们处理任何输入变量并在其输出变量中输出结果。
3.Types
GLSL 与任何其他编程语言一样,具有用于指定我们想要使用的变量类型的数据类型。 GLSL 拥有我们从 C 语言中了解到的大多数默认基本类型:int、float、double、uint 和 bool。 GLSL 还具有两种我们将经常使用的容器类型,即向量和矩阵。
3.1 Vectors
GLSL 中的向量是一个包含 1、2、3 或 4 个分量的容器,适用于刚刚提到的任何基本类型。 它们可以采用以下形式(n 表示组件的数量):
vecn
: the default vector ofn
floats. (n个floats)bvecn
: a vector ofn
booleans.ivecn
: a vector ofn
integers.uvecn
: a vector ofn
unsigned integers.dvecn
: a vector ofn
double components.
3.2 重组(Swizzling)
向量这一数据类型也允许一些有趣而灵活的分量选择方式,叫做重组(Swizzling)。重组允许这样的语法:
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
你可以使用上面4个字母任意组合来创建一个和原来向量一样长的(同类型)新向量,只要原来向量有那些分量即可;然而,你不允许在一个vec2向量中去获取.z元素。我们也可以把一个向量作为一个参数传给不同的向量构造函数,以减少需求参数的数量:
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);
向量是一种灵活的数据类型,我们可以把用在各种输入和输出上。
4.Ins and outs
每个着色器都可以使用这些关键字指定输入和输出,并且只要输出变量与下一个着色器阶段的输入变量匹配,它们就会被传递。 不过,顶点着色器和片段着色器略有不同。
顶点着色器应该接收的是一种特殊形式的输入,否则就会效率低下。顶点着色器的输入特殊在,它从顶点数据中直接接收输入。为了定义顶点数据该如何管理,我们使用location这一元数据指定输入变量,这样我们才可以在CPU上配置顶点属性。我们已经在前面的教程看过这个了,layout (location = 0)。顶点着色器需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据。
5.Uniforms
Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。
- uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。
- 无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。
总结:Uniforms就是C++代码中,向shader中注入数据的一种方式
6.fragment interpolation (片段 插值)
在片段着色器中进行的所谓片段插值(Fragment Interpolation)。当渲染一个三角形时,光栅化(Rasterization)阶段通常会造成比原指定顶点更多的片段。光栅会根据每个片段在三角形形状上所处相对位置决定这些片段的位置。
基于这些位置,它会插值(Interpolate)所有片段着色器的输入变量。比如说,我们有一个线段,上面的端点是绿色的,下面的端点是蓝色的。如果一个片段着色器在线段的70%的位置运行,它的颜色输入属性就会是一个绿色和蓝色的线性结合;更精确地说就是30%蓝 + 70%绿。