GLSL(OpenGL Shading Language)是一种用于 OpenGL 图形 API 的着色语言,专门用于编写顶点着色器和片段着色器(像素着色器)的代码,GLSL 采用了类似于 C 语言的语法结构,使得熟悉 C 语言的程序员能够较快地上手。
GLSL 基本类型
标量
构造标量的方式和 C 语言一致:
float floatValue = 1.0;
bool booleanValue = false;
向量
构造向量时的规则如下:
- 若向向量构造器提供了一个标量,则向量的所有值都会设定为该标量值。
- 若提供多个标量值或向量,则从左到右使用提供的值赋值。前提是标量或向量的数量之和要等于向量构造器的数量。
vec4 myVec4 = vec4(1.0); // myVec4 = {1.0, 1.0, 1.0, 1.0}
vec2 myVec2 = vec2(0.5, 0.5); // myVec2 = {0.5, 0.5}
vec4 newVec4 = vec4(1.0, 1.0, myVec2);// newVec4 = {1.0, 1.0, 0.5, 0.5}
向量可以通过 rgba、stpq 或 xyzw 进行访问,向量可能代表了一个空间坐标(x,y,z,w),或者代表了一个颜色(r,g,b,a),再或者代表一个纹理坐标(s,t,p,q) 。
可以同时访问多个角标,非常灵活:
vec4 myVec4_0 = vec4(1.0); // myVec4_0 = { 1.0, 1.0, 1.0, 1.0 }
vec4 myVec4 = vec4(1.0, 2.0, 3.0, 4.0); // myVec4 = { 1.0, 2.0, 3.0, 4.0 }
float x = myVec4.x; // x = 1.0;
vec3 myVec3_0 = myVec4.xyz; // myVec3_0 = { 1.0, 2.0, 3.0 }
vec3 myVec3_1 = myVec4.rgb; // myVec3_1 = { 1.0, 2.0, 3.0 }
vec3 myVec3_2 = myVec4.zyx; // myVec3_2 = { 3.0, 2.0, 1.0 }
vec3 myVec3_3 = myVec4.xxx; // myVec3_3 = { 1.0, 1.0, 1.0 }
矩阵
在 GLSL 内可构造 mat[2..4] 来表示 2 阶到 4 阶的矩阵。
矩阵构造有如下的规则:
- 若只为矩阵构造器提供了一个标量,则该值会构造矩阵对角线上的值
- 矩阵可以由多个向量构造
- 矩阵可以由单个标量从左到右进行构造
mat4 marixt4x4 = mat4(1.0); // marixt4x4 = { 1.0, 0.0, 0.0, 0.0,
// 0.0, 1.0, 0.0, 0.0
// 0.0, 0.0, 1.0, 0.0
// 0.0, 0.0, 0.0, 1.0 }
vec2 col1 = vec2(1.0, 0.0);
vec2 col2 = vec2(1.0, 0.0);
mat2 matrix2x2 = mat2(coll1, col2);
// GLSL 是列矩阵存储,因此构造时,构造器会按照列顺序进行填充
mat3 matrix3x3 = mat3(0.0, 0.0, 0.0, // 第一列
0.0, 0.0, 0.0, // 第二列
0.0, 0.0, 0.0); // 第三列
矩阵可以通过索引访问不同的列:
mat2 matrix2x2 = mat2(0.0, 0.0, 0.0, 0.0);
vec4 myVec4 = vec4(matrix2x2[0], matrix2x2[1]);
vec2 myVec2 = matrix2x2[0];
// 访问第一列的第一个元素
float value = matrix2x2[0][0];
matrix2x2[1][1] = 2.0;
结构体
结构体的形成和 C 语言类似,可由不同数据类型聚合而成:
struct myStruct
{
vec4 position;
vec4 color;
vec2 uv;
};
构造结构体的代码示例如下:
myStruct structVar = myStruct(vec4(0.0, 0.0,0.0,0.0), vec4(1.0, 1.0, 1.0, 1.0), vec2(0.5, 0.5));
数组
数组的用法和 C 语言类似,规则如下:
- 数组必须声明长度
- 数组不能在声明的同时初始化
- 数组必须由常量表达式初始化
- 数组不能用 const 修饰
- 不支持多维数组
数组声明和初始化的代码示例如下:
float array[4];
for(int i =0; i < 4; i ++)
{
array[i] = 0.0;
}
基础类型运算
glsl 没有隐式类型转换。
int a = 2.0; //错误
int a = 1.0+2;//错误
float a =2;//错误
float a =2.0+1;//错误
bool a = 0; //错误
vec3 a = vec3(1.0, 2.0, 3.0) * 2;//错误
下面来分别说说可能遇到的情况:
1.float 与 int:
float 与 float , int 与int 之间是可以直接运算的, 但 float 与 int 不行,它们需要进行一次显示转换。
以下表达式都是正确的:
int a=int(2.0);
float a= float(2);
int a=int(2.0)*2 + 1;
float a= float(2)*6.0+2.3;
2.float 与 vec(向量) mat(矩阵):
vec, mat 这些类型其实是由 float 复合而成的,当它们与float运算时,其实就是在每一个分量上分别与float进行运算, 这就是所谓的逐分量运算。
下面枚举了几种 float 与 vec,mat 运算的情况:
vec3 a = vec3(1.0, 2.0, 3.0);
mat3 m = mat3(1.0);
float s = 10.0;
vec3 b = s * a; // vec3(10.0, 20.0, 30.0)
vec3 c = a * s; // vec3(10.0, 20.0, 30.0)
mat3 m2 = s * m; // = mat3(10.0)
mat3 m3 = m * s; // = mat3(10.0)
3. vec(向量) 与 vec(向量):
两向量间的运算首先要保证操作数的阶数都相同,逐分量运算,否则不能计算。
例如: vec3*vec2 vec4+vec3 等都是不行的。
vec3 a = vec3(1.0, 2.0, 3.0);
vec3 b = vec3(0.1, 0.2, 0.3);
vec3 c = a + b; // = vec3(1.1, 2.2, 3.3)
vec3 d = a * b; // = vec3(0.1, 0.4, 0.9)
4. vec(向量) 与 mat(矩阵):
要保证操作数的阶数相同,且vec与mat间只存在乘法运算。
它们的计算方式和线性代数中的矩阵乘法相同, 不是逐分量运算。
vec2 v = vec2(10., 20.);
mat2 m = mat2(1., 2., 3., 4.);
vec2 w = m * v; // = vec2(1. * 10. + 3. * 20., 2. * 10. + 4. * 20.)
...
vec2 v = vec2(10., 20.);
mat2 m = mat2(1., 2., 3., 4.);
vec2 w = v * m; // = vec2(1. * 10. + 2. * 20., 3. * 10. + 4. * 20.)
向量与矩阵的乘法规则如下:
5. mat(矩阵) 与 mat(矩阵):
要保证操作数的阶数相同。
在mat与mat的运算中, 除了乘法是线性代数中的矩阵乘法外, 其余的运算任为逐分量运算。
简单说就是只有乘法是特殊的,其余都和vec与vec运算类似。
mat2 a = mat2(1., 2., 3., 4.);
mat2 b = mat2(10., 20., 30., 40.);
mat2 c = a * b; //mat2(1.*10.+3.*20.,2.*10.+4.*20.,1.* 30.+3.*40.,2.* 30.+4.*40.);
mat2 d = a+b;//mat2(1.+10.,2.+20.,3.+30.,4.+40);
矩阵乘法规则如下:
限定符
精度限定符
GLSL 引入了精度限定符,用于指定整型或浮点型变量的精度。
在 Shader 头部声明的精度应用于整个 Shader,是所有基于浮点型的变量的默认精度,同时也可以定义单个变量的精度。
在 Shader 中如果没有指定默认精度,则所有的整型和浮点型变量都采用高精度计算。
GLSL 支持的精度限定符包括以下几种:
代码示例如下:
highp mat4 cc_matWorld;
mediump vec2 dir;
lowp vec4 cc_shadowColor;
存储限定符
存储限定符用于描述变量在管线中的作用。
uniform
在一个渲染过程内声明的 uniform 不能重复。
例如在顶点着色器中定义了变量 variableA,variableA 也会存在于片段着色器且值相同,那么也就是 variableA 不能在片元着色器中再次定义。
varying
varying 是由顶点着色器输出并传输给片元着色器的变量。
在管线的作用下,变量值并不会和顶点着色器输出的保持一致,而是由管线进行插值,
参数限定符
GLSL 中函数的参数限定符包括以下几种:
限定符举例:
void scaleSize(inout vec2 size, in float scale){
size = size * scale;
}
void scaleSize(out vec2 oSize, vec2 iSize, float scale){
oSize = iSize * scale;
}
vec2 scaleSize(vec2 iSize, float scale){
return iSize * scale;
}
内置变量
glsl 程序使用一些特殊的内置变量与硬件进行沟通。
他们大致分成两种 一种是 input类型,他负责向硬件(渲染管线)发送数据. 另一种是output类型,负责向程序回传数据以便编程时需要。
在 vertex Shader 中:
output 类型的内置变量:
在 fragment Shader 中:
input 类型的内置变量:
output 类型的内置变量:
内置函数
GLSL 提供了非常丰富的函数库供我们使用。
通用函数:
下文中的 类型 T 可以是 float, vec2, vec3, vec4,且可以逐分量操作。
角度&三角函数:
指数函数:
下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作。
几何函数:
下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作。
进技术交流群,扫码添加我的微信:Byte-Flow
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/52894.html