GLSL 语言
GLSL中提供了许多内建的函数,来方便我们的使用。可以在官方手册中查找相关的函数GLSL官方手册。
注释
单行注释://
多行注释:/**/
变量
GLSL的变量命名方式和C语言一样,变量名称可以使用数字、下划线、字母组成。但是不能以数字开头。需要注意的是GLSL的变量并不能以gl_作为前缀,原因是其官方保留了这个前缀作为GLSL的内部变量。
基本类型
GLSL中除了整型、布尔类型、浮点类型外,它还引入了一些着色器用到的数据基本类型,他们同样能作为结构体累不的类型,具体看下图。
数据类型 | 类型描述 |
---|---|
void | 跟C语言的void类似,表示空类型,也可作为函数的返回值,表示的是无返回值返回 |
bool | 布尔类型,返回的是true或者false的表达式 |
int | 整型 |
folat | 浮点类型 |
bvecn | 表示包含n个bool类型的分量,eg:bvec2、bvec3、bvec4 |
ivecn | 表示包含n个int类型的分量,eg:ivec2、ivec3、ivec4 |
mat2、mat2x2 | 2x2的浮点矩阵类型 |
mat3、mat3x3 | 3x3的浮点矩阵类型 |
mat4x4 | 4x4的浮点矩阵类型 |
matnxm | nxm的浮点矩阵类型,eg:mat2x3(2列3行的矩阵)、mat3x4(3列4行的浮点矩阵)。OpenGL的矩阵是列主顺序的 |
sampler1D | 用于内奸的纹理函数中引用指定的1D纹理句柄,只可以作为一致变量或者函数中使用 |
sampler2D | 2维纹理句柄 |
sampler3D | 3维纹理句柄 |
samplerCube | cube map纹理句柄 |
sampler1DShadow | 一维深度纹理句柄 |
sampler2DShadow | 二维深度纹理句柄 |
结构体
结构体可以组合基本数据类型和数组来自定义结构体,在定义一个结构体的同时,也可以定义一个结构体实例给它,或者后面再定义也是可以的。
- 1,使用
=
符号来给结构体赋值 - 2,使用
==
或!=
来判断两个结构体是否相同,只有两个结构体中的所有成员变量都相同时,两个结构体才相等,和iOS一样使用点语法进行访问。
结构体中至少要包含一个成员变量,固定大小的数组也是可以为成员的,GLSL语言中是不允许结构体嵌套的,但是如果结构体已经预先声明的话,也是可以嵌套到定义的结构体中。
struct velocity {
vec3 direction;
}velo;
数组
在GLSL中只能定义一维数组,数组的类型可以是GLSL中的任何基本数据类型,我们也可以在定义数组的是时候指定显示大小的数组,这个数组可以是参数也可以是返回值。
vec4 lightPositions[8];
vec4 lightPos[] = lightPositions;
获取数组长度大小可以用length,例如:lightPositions.length // 打印结果为8
修饰符
修饰符 | 修饰符描述 |
---|---|
const | 常量值修饰符,必须在声明时初始化,只读属性,不可写 |
attribute | 表示只读的顶点数据,只用在顶点着色器中,数据来自当前的顶点状态或者顶点数组,它修饰的属性必须是全局范围声明的属性,也就是说不能在函数内部,一个attribute修饰的属性可以是浮点类型的标量、向量、或者矩阵。重点是:attribute不能修饰数组和结构体 。 |
uniform | 一致变量,在着色器执行期间一致变量的值是不变的,与const常量不同的是,这个值在编译时期是未知的且有着色器外部进行初始化,一致变量在顶点着色器和片段着色器之间数据是共享的,所以它修饰的属性只能是全局范围进行声明的属性值。 |
varying | 顶点着色器的输出,例如纹理坐标和颜色,(插值后的数据)作为片段着色器的只读输入数据,同样必须是全局范文声明的全局变量,可以是浮点类型的标量、向量、举证,但同样不能修饰数组和结构体 。 |
centoridvarying | 在没有多重采样的情况下,与varying的特性是一样的,而在多重采样时,centoridvarying在光栅化的图形内部进行求值,而不是在片段中心的固定位置求值。 |
invariant | 用于表示定点着色器的输出和任何匹配片段着色器的输入,在不同的着色器中计算产生的值必须是一致的,所有的控制流和数据流,写入一个invariant变量也是一致的。编译器为了保证结果是完全一致,需要放弃那些可能会导致不一致值的潜在优化,所以,除非必要,不要使用这个修饰符,在多通道渲染中必变z-fighting 时可能会被用到。 |
in | 用在函数的参数中,目的是表示函数时输入的,在函数中改变这个值,并不会影响对调用函数,相当于是C语言中的传值,是一个默认修饰符。 |
out | 用在函数中,修饰的参数表示是输出参数,值是会改变的 |
inout | 用在函数的参数,表示这个蚕食既是输入参数也是输出参数 |
内置变量
内置变量可以与固定的函数功能进行交互,使用签不需要声明
顶点着色器可用的内置变量。
内置变量名称 | 类型 | 描述 |
---|---|---|
gl_Color | vec4 | 输入属性, 表示顶点的主颜色 |
gl_SeconddaryColor | vec4 | 输入属性,表示的是顶点的辅助颜色 |
gl_Normal | vec3 | 输入属性,表示顶点的法线值 |
gl_Vertex | vec4 | 输入属性,表示物体空间的顶点位置 |
gl_MultiTexColor | vec4 | 输入属性,表示的是顶点的第n个纹理的坐标 |
gl_FogCoord | float | 输入属性,表示顶点的雾坐标 |
gl_Position | vec4 | 输出属性,变换后的顶点位置,用于后面的裁剪等操作,所有的顶点着色器都必须写这个值 |
gl_ClipVertex | vec4 | 输出坐标,用于用户裁剪平面的裁剪 |
gl_PointSize | float | 点的大小 |
gl_FrontColor | vec4 | 正面的主颜色的varying输出 |
gl_BackColor | vec4 | 背面主颜色的varying的输出 |
gl_FrontSecondaryColor | vec4 | 正面的辅助颜色的varying的输出 |
gl_BackSecondaryColor | vec4 | 背面的辅助颜色的varying |
gl_TexCoord[] | vec4 | 纹理坐标的数组varying输出 |
gl_FogFragCoord | float | 雾坐标的varying输出 |
片段着色器的内置变量
内置变量名称 | 类型 | 描述 |
---|---|---|
gl_Color | vec4 | 包含主颜色的插值,只读输入属性 |
gl_SecondarycOLOR | vec4 | 包含辅助颜色的插值,只读输入属性 |
gl_TexCoord[] | vec4 | 包含纹理坐标数组的插值,只读输入属性 |
gl_FogFragCoord | float | 包含了雾坐标的插值,只读输入属性 |
gl_FragCoord | float | 只读输入属性,表示窗口的x、y、z和1/w |
gl_FrontFaciong | bool | 只读输入属性,如果是窗口正面图元的一部分,则这个值为true |
gl_PointCoord | vec2 | 点精灵的二维空间坐标范围在(0.0, 0.0)到(1.0, 1.0)之间,仅用于点图元和点精灵开启的情况下。 |
gl_FragData[] | vec4 | 使用glDrawBuffers输出的数据数组。不能与gl_FragColor结合使用。 |
gl_FragColor | vec4 | 输出的颜色用于随后的像素操作 |
gl_FragDepth | float | 输出的深度用于随后的像素操作,如果这个值没有被写,则使用固定功能管线的深度值代替 |
表达式
GLSL语言的操作符与C语言相似,优先级如下表从高到底排列
操作符(优先级: ) | 描述 |
---|---|
() | 用于表达式组合,函数的调用,构造 |
[] | 数组下标,向量或矩阵选择器 |
. | 结构体和向量的成员选择(点语法) |
++ - | 前缀或后缀的自增自减的操作符 |
"+" "-" ! | 一元操作符,表示正、负、逻辑非(有双引号是因为对应操作符在markdown中是特使符号) |
*/ | 乘、除操作符 |
”+“、”-“ | 二元操作符, 表示加减操作 |
<> <= >= == != | 小于,大于,小于等于, 大于等于,等于,不等于 判断符 |
&& | 逻辑与 |
?: | 条件判断符 |
= += –= *= /= | 赋值操作符 |
, | 表示序列 |
数组访问
GLSL数组下表同样也是从0开始, 其范围是在[0, length-1],若数组越界,就会被系统定义为行为未定义, 当然,如果着色器的编译器在编译时识别数组越界,就会提示编译失败。
vec4 myColor, ambient, didduse[6], specular[6];
myColor = ambient + didduse[4] + specular[4];
构造函数
构造函数可以用于初始化包含多个成员的变量、数组、结构体,构造函数也可以使用在表达式中
vec3 myNormal = vec3(1.0, 1.0, 1.0);
greenTint = myColor + vec3(0.0, 1.0, 0.0);
ivec4 myColor = ivec4(255);
成分选择
向量中单独的成分是可以通过{x,y,z,w}、{r,g,b,a}或者{s,t,p,q}的记法来表示,这些不同的记法用于顶点、颜色、纹理坐标。在成分选择中还可以使用特殊方式,比如重复使用向量中的元素、颠倒顺序等, 但是如果是在赋值时,就不能重复使用。
// 调换顺序
vec3 yxz = myVec.yxz;
// 重复使用
vec4 mySstt = myVec.sstt;
// 在赋值时, 可以自主选择想要的顺序,但是不能重复成分
vec4 myColor = {0.0, 1.0, 2.0, 1.0};
myColor.x = -0.1;
myColor.yz = vec2(3.0, 5.0);
myColor.wx = vec2(1.0, 3.0);
// 不合法的使用
myColor.zz = vec2(2.0, 3.0);
我们可以使用下标来访问向量矩阵中的元素,如果越界,那么当前行为就会被定义为未定义的行为事件。
循环
同C语言和C++语言一样,GLSL语言也提供了for、while、do/while
的循环方式,continue
则继续下一次循环,break
这停止并结束当前循环。
for (l = 0, l < macCount, l++) {
if (!arr[l])
continue;
a += arr[l];
}
while(i < maxCount) {
sum += ary[i];
i++;
}
控制流
if (a > 0) {
a = c;
} else {
a =b;
}
discard
在片段着色器中一种特殊的控制流即:discard
。在片段着色器中使用discard
会退出片段着色器,不会再继续执行片段着色器,片段也不会写入帧缓冲区中。
if (color.a < 0.9)
discard; // 退出当前片段着色器,不会再执行之后的的相关渲染工作
main
每个shader中必须要有一个main()
函数,main
函数中的void参数是可选的,但如果返回值是void时,那么就是必须的。
void main(void) {
....
}
GLSL中的函数,必须是全局范围定义和声明的,不能在函数定义中国声明或者定义函数,函数必须要有返回值类型,但参数是可选的,参数可以是int
、out
、inout
、const
等。
// 声明函数
// 函数声明
bool isAnyNegative(const vec4 v) {
bool isNegative = isAnyNegative(gl_Color);
}
// 定义函数
bool isAnyNegative(const vec4 v) {
if (v.x < 0.0 || v.y < 0.0 || v.z < 0.0 || v.w < 0.0) {
return true;
} else {
return false;
}
}
在GLSL中函数名称是可以重名的,只需要参数个数或者类型不同即可
> float min(float a, float b) { return a+b; } vec3 min(vec3 v1, vec3 v2) { return v1+v2; }
运算符
运算符(优先级: ) | 运算符 | 说明 |
---|---|---|
1 | () | 聚组:a*(b+c) |
2 | []、()、++、-- | []是数组下标;()是函数调用:fun(arg1, arg2);. 则是链式访问,比如myStruct.a;++或者--则表示自增和自减。 |
3 | ++、--、+、—、! | ++自增,--自减,正负号(一般正号不写)a ,-a;!a表示取反 |
4 | *、/ | 乘或除运算符 |
5 | +、- | 加减数学运算符 |
6 | < > <= >= | 关系运算符 |
7 | == != | 相等性运算符 |
8 | && | 逻辑与 |
9 | ^^ | 逻辑排他或(用处基本等于!=) |
10 | II | 逻辑或 |
11 | ? : | 三目运算符 |
12 | = += -= *= /= | 赋值与复合赋值 |
13 | , | 顺序分配符 |
glsl中是没有隐式类型转换的,所有在glsl中要求的是任何表达式的左右值类型必须是一致的。
总结
在图形管道中GLSL是可直接执行OpenGL的着色语言的,着色器中最常用的两种着色器:顶点着色器(该着色器是将形状转换到真实的3D坐标系中)和片元着色器(主要是计算最终渲染的颜色和其他属性)。
顶点着色器回顾
顶点着色器的操作是3D空间的坐标并且每个顶点都会调用一次这个函数,其目的是设置gl_Position
变量,这个变量是一个特殊的全局内置变量,是用来存储当前顶点的位置;
void main() {
gl_Position = ...;
}
这个
void main()
函数时定义全局的gl_Position
变量的标准方式,所有在这个函数中代码都会被着色器执行,比如:将3D空间中的位置投射在2D屏幕上时,这些信息do偶会被保存在计算结果的变量中。
顶点着色器的内置变量:
gl_Position
:顶点着色器的裁剪空间输出位置向量,只要是要在屏幕上显示,就需要在顶点着色器红设置gl_Position
。
gl_pointSize
: 比如它其中一个图元是 GL_POINTS
,说明每一个顶点都是一个图元,都会被渲染成一个点,我们可以同OpenGL中的glPointSize函数来设置渲染出来点的大小,也可以在顶点着色器中修改这个值。换句话说,它能对每个顶点设置不同的值,用来设置像素的size,是个float类型。
gl_VertexID
:gl_Position
和gl_PointSize
都是输出变量。因为他们的值是作为顶点着色器的输出被外界读取,我们可以对他们进行写入,从而改变结果,而gl_VertexID
就可以用来进行读取操作。
片段着色器回顾
片段(或纹理)着色器在计算时定义了一个像素的rgba颜色值,每个像素只会调用一次片段着色器,这个着色器的作用是设置gl_FragCoord
,这个变量同样属于内置变量,它的z分量等于对应片段着色器的深度值。
gl_FragCoord
的x分量和y分量记录的是当前片段在屏幕空间桌表系统下的x、y坐标。
void main() {
// 计算的结果就包含了rgba的颜色信息
gl_FragCoord = ...;
}
网友评论