1. 引言

GLSL(OpenGL Shading Language)是一种用于编写着色器程序的编程语言,语法和C语言比较类似。

GLSL于2003年首次引入,作为OpenGL图形API的一部分,用于编写着色器程序。GLSL不仅在OpenGL中使用,还在其他图形API中得到了广泛应用,包括Vulkan和WebGL等。

2. 数据类型

2.1 基本数据类型

  • 整数类型:int(32位整数)、uint(无符号32位整数)
  • 布尔值:bool
  • 浮点类型:float(单精度浮点数)、double(双精度浮点数)
  • GLSL没有指针、字符、字符串

    //true/false
    bool bDone = false;
    int iValue = 42;
    uint uiValue = 32u;
    float fValue = 3.14159f;

2.2 矢量数据类型

矢量数据类型,用于同时处理多个数值。这些数据类型是浮点数或整数的数组,通常用于执行并行计算或者处理顶点和颜色等向量数据。

  • vec2、vec3和vec4:表示2维、3维和4维浮点型向量。例如,vec3可以用来表示三维空间中的坐标。
  • ivec2、ivec3和ivec4:表示2维、3维和4维整型向量。
  • uvec2、uvec3和uvec4:表示2维、3维和4维无符号整型向量。
  • bvec2、bvec3和bvec4:表示2维、3维和4维bool型向量。
vec4 v1;      // 未初始化的向量
vec4 v2 = vec4(1,2,3,4);  // 用四个单独的数值初始化
vec4 v3 = vec4(0,0,0,0);  // 用四个0初始化

我们可以对向量进行各种操作,包括加法、减法、乘法等:

v1 = v2 + v3;   // 向量加法
vec4 v4 = v1;    // 向量复制
v1 += vec4(10,10,10,10);  // 向量加法赋值
v1 *= 5;        // 向量乘法

此外,我们还可以对向量的各个分量进行单独的操作:

v1.x = 3.0f;    // 设置向量的x分量
v1.y = 4.0f;    // 设置向量的y分量
v1.z = 5.0f;    // 设置向量的z分量
v1.w = 1.0f;    // 设置向量的w分量

我们还可以对向量的各个分量进行一起操作:

v1.xy = vec2(1,2);
v1.xyz = vec3(1,2,3);

// 获取向量的元素,可以通过r,g,b,a来获取向量中元素。
v2.r = 1.0f;
v2 = vec4(1.0f,1.0f,1.0f,1.0f);

// 获取向量元素,可以s,t,p,q来获取纹理坐标控制
v1.st=vec2(1.0,2.0);v1. st = v2. st:

// 不可以如下,xt不允许混合
v1.st = v2.xt 

// 向量数据类型还可swizzle (调换)操作
// 将颜色数据RGB顺序转化BGR顺序
v1.bgra = v2.rgba;

// 向量的一次性所有分量操作
v1.x = v2.x + 5.0f;
v1.xyz = v2.xyz + vec3(5.0f,4.0f,1.0f);

2.3 矩阵类型

所有的矩阵类型只支持浮点数,不支持整数、bool类型。

mataxb, a = [2,3,4], b = [2,3,4], a是列数,b是行数

类型描述
mat2, mat2x2两行两列
mat3, mat3x3三行三列
mat4, mat4x4四行四列
mat2x3三行两列
mat2x4四行两列
mat3x2两行三列
mat3x4四行三列
mat4x2两行四列
mat4x3三行四列
mat4 m1;
vec4 v2;
vec4 vOutPos ;

// 矩阵和向量相乘
m1 = mat4(
    1.0,1.0,1.0,1.0,
    1.0,1.0,1.0,1.0,
    1.0,1.0,1.0,1.0,
    1.0,1.0,1.0,1.0,
)
vOutPos = v2 * m1;
m1 = mat4(1.0f);

3. 变量和常见限定符

3.1 变量声明方式

在GLSL中,变量是用于存储和操作数据的关键元素。在声明变量时,需要指定其数据类型、名称以及可能的限定符:

数据类型 变量名;
  • 输入变量:从外部(客户端/上一个阶段着色器传递的属性/Uniform等)。
  • 输出变量:从任何着色器阶段进行写入的变量。

3.2 常见限定符

限定符是一种用于指定变量的作用域和存储方式的关键词。

限定符描述
<none>普通本地变量,外部不可见,不可访问
const编译常量,只读
in / varying前面着色器传过来的变量,通常用于在顶点着色器和片元着色器之间传递数据。它表明变量的值在这两个阶段之间插值
in / varying centroid前面着色器传过来的变量,使用质心差值
out / attribute传递到下一个着色器,或者在函数中指定返回值
out / attribute centroid传递到下一个着色器,使用质心差值
uniform声明全局变量,变量在整个着色器程序中都可见,通常用于将数据从应用程序传递给着色器

以下是一个使用限定符的变量声明示例:

uniform mat4 projectionMatrix; // 一个uniform矩阵
varying vec4 vertexColor; // 一个varying颜色向量
attribute vec3 position; // 一个attribute位置向量

3.3 内置变量

GLSL中有一些内置变量,它们在着色器程序中自动定义,用于与渲染管线和渲染过程进行通信。这些内置变量提供了关于顶点、片元和渲染状态的信息。以下是一些常见的GLSL内置变量:

  1. 顶点着色器内置变量:
  2. gl_Position: 表示顶点的位置,通常是经过变换后的坐标。该变量必须在顶点着色器中设置,以确定绘制的位置。
  3. gl_PointSize: 表示点粒子的大小。通常在点粒子渲染时使用。
  4. 片元着色器内置变量:
  5. gl_FragColor: 表示片元(像素)的颜色。通过设置这个变量的值,您可以确定渲染到屏幕上的像素颜色。
  6. gl_FragCoord: 表示片元的屏幕坐标。可以用于执行特定的片元操作,例如根据屏幕坐标更改片元颜色。
  7. gl_FrontFacing: 表示片元是否在正面(front-facing)或背面(back-facing)多边形上。这对于执行不同的片元操作,例如剔除背面多边形,非常有用。
  8. gl_FragDepth: 表示片元的深度值。在需要自定义深度值的情况下,可以使用这个变量。
  9. 通用内置变量:
  10. gl_TextureCoord[]: 表示纹理坐标的数组,用于访问纹理。根据使用的纹理单元和纹理类型,可能会有多个 gl_TextureCoord。
  11. gl_ModelViewMatrixgl_ProjectionMatrix: 表示模型视图矩阵和投影矩阵。这些矩阵通常用于变换顶点位置。
  12. gl_NormalMatrix: 表示法线矩阵,用于法线的变换。
  13. gl_LightSource[]: 表示光源的属性,例如位置、颜色和强度。可以用于执行光照计算。
  14. gl_LightModel: 表示光照模型,包括环境光和局部光照。

4. GLSL编程基础

4.1 语法和语句

  • GLSL语句必须以分号(;)结尾,表示语句的结束

    float x = 5.0;
  • 代码块通常使用大括号({})括起来,以表示作用域。

    if (condition) {
      // 代码块
    }
  • 注释: GLSL支持C风格的注释,包括单行注释(//)和多行注释(/ /)。
  • 大小写敏感: GLSL是大小写敏感的语言,因此变量名和函数名必须与其声明时的大小写完全匹配。
  • 赋值语句: 用于给变量赋值,通常采用以下形式:

    variable = expression;
  • 条件语句: GLSL支持if语句,允许根据条件执行不同的代码块。

    if (condition) {
      // 在条件为真时执行的代码
    } else {
      // 在条件为假时执行的代码
    }
  • 循环: GLSL支持for和while循环,允许多次执行相同的代码块,直到满足特定条件为止。

    for (int i = 0; i < 10; i++) {
      // 循循环执行的代码
    }
    
    while (condition) {
      // 循环执行的代码
    }
    
  • 表达式: 表达式是由操作数和操作符组成的计算单元。GLSL支持各种数学运算,例如加法、减法、乘法和除法。例如:

    float result = x + y * 2.0;
  • 函数调用: GLSL支持函数调用,允许执行预定义的内置函数或自定义函数。例如:
float distance = length(vec3(x, y, z));

4.2 运算符和函数

4.2.1 运算符

GLSL支持各种数学和逻辑运算符,用于执行不同类型的运算。一些常见的运算符包括:

  • 算术运算符:+(加法)、-(减法)、*(乘法)、/(除法)、%(取余)等。
  • 关系运算符:<(小于)、>(大于)、<=(小于等于)、>=(大于等于)、==(等于)、!=(不等于)等。
  • 逻辑运算符:&&(与)、||(或)、!(非)。
  • 位运算符:&(按位与)、|(按位或)、^(按位异或)、<<(左移位)、>>(右移位)等。
  • 赋值运算符:=、+=、-=、*=、/= 等。

4.2.2 函数

在GLSL中,函数的声明方式与其他编程语言类似,包括函数名称、参数列表和返回类型。以下是函数声明的基本格式:

返回类型 函数名称(参数类型 参数名称, 参数类型 参数名称, ...) {
    // 函数体
    return 返回值; // 可选
}
  • 返回类型: 这是指定函数返回的数据类型,可以是整数、浮点数、向量、矩阵或其他数据类型。如果函数不返回任何值,可以使用void作为返回类型。
  • 函数名称: 函数的名称是用来调用函数的标识符。
  • 参数列表: 参数列表包括函数接受的参数,每个参数由参数类型和参数名称组成。参数列表用括号括起来,多个参数之间用逗号分隔。
  • 函数体: 函数体包含函数执行的实际操作,即函数的主体。在函数体中,可以执行各种操作和计算。
  • 返回值: 函数可以返回一个值,这个值的类型必须与函数的声明中的返回类型匹配。如果函数声明中的返回类型是void,则可以省略return语句。

函数声明示例:

float add(float a, float b) {
    float result = a + b;
    return result;
}

4.2.3 main函数

在GLSL编程中,每个着色器程序都需要包含一个名为 main 的特殊函数,这个函数充当着色器程序的入口点。 main 函数是由OpenGL或其他图形库调用的函数,用于执行特定的着色器阶段(例如,顶点着色器或片元着色器)。以下是 main 函数的一般形式:

void main() {
    // 着色器程序的主要代码
}

4.2.4 内置函数

GLSL提供了广泛的内置函数,用于执行各种数学、向量和矩阵操作。一些常见的内置函数包括:

  • 数学函数:sin()、cos()、tan()、sqrt()、exp()、log() 等,用于执行常见的数学运算。
  • 向量函数:length()、normalize()、dot()、cross() 等,用于处理向量操作。
  • 矩阵函数:mat4()、transpose()、inverse() 等,用于执行矩阵操作。
  • 其它函数:GLSL还提供了各种其它函数,包括处理颜色、纹理、几何和辅助函数等。

这些内置函数帮助开发者执行各种复杂的数学和数据操作,从而实现图形渲染和通用计算任务。

5. GLSL代码文件形式

5.1 后缀名

GLSL并没有规定着色器语言后缀名,为了方便开发者理解,一般使用统一的后缀名对着色器进行区分。
比如一个项目中,可以使用以下后缀:

  • vsh: 顶点着色器
  • fsh: 片元着色器

5.2 文件中代码组织形式

一个着色器一般书写再同一个文件中,不能像c语言那样包含多个文件。

一个顶点着色器示例 some.vsh:

attribute vec4 position;
attribute vec2 textCoordinate;
uniform mat4 rotateMatrix;
varying lowp vec2 varyTextCoord;

void main(){
    varyTextCoord = textCoordinate;
    vec4 vPos = position;
    vPos = vPos * rotateMatrix;
    gl_Position = vPos;
}

一个片元着色器示例 some.fsh:

varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;

void main(){
    gl_FragColor = texture2D(colorMap, varyTextCoord);
}
分类: 音视频开发扫盲 标签: OpenGL着色器

评论

暂无评论数据

暂无评论数据

目录