Skip to content

数据类型 (Data Types)

OpenGL Shading Language(GLSL)定义了多种数据类型,也定义了用户自定义类型的手段。

基本类型

基本类型是 GLSL 中最基础的类型,非基本类型是这些基础类型的聚合。

INFO

本文档会提及双精度类型,这些仅在 OpenGL 4.0 或 ARB_gpu_shader_fp64 可用时可用。

标量类型

类型说明
bool布尔类型,值为 truefalse
int有符号 32 位二进制补码整数
uint无符号 32 位整数
floatIEEE-754 单精度浮点数
doubleIEEE-754 双精度浮点数

WARNING

GLSL 1.30 及以上版本才有上述具体的整数和浮点大小和格式规范。

向量类型

每种标量类型都有 2、3、4 分量的向量版本。其中 n 可以是 2、3 或 4:

类型说明
bvecn布尔向量
ivecn有符号整数向量
uvecn无符号整数向量
vecn单精度浮点向量
dvecn双精度浮点向量

向量值可以使用与标量值相同的数学运算符,这些运算符对每个分量执行逐分量操作。

分量访问(Swizzling)

可以使用以下语法访问向量的分量:

glsl
vec4 someVec;
someVec.x + someVec.y;

这被称为分量访问(swizzling)。可以使用 x、y、z、w,分别引用第一、第二、第三和第四个分量。

glsl
vec2 someVec;
vec4 otherVec = someVec.xyxx;
vec3 thirdVec = otherVec.zyy;

最多可以使用 4 个字母的任意组合创建该长度的向量(相同基本类型)。

分量访问也适用于左值:

glsl
vec4 someVec;
someVec.wzyx = vec4(1.0, 2.0, 3.0, 4.0); // 反转顺序
someVec.zx = vec2(3.0, 5.0);

WARNING

使用分量访问设置值时,不能使用相同的分量两次。如 someVec.xx = vec2(4.0, 4.0); 是不允许的。

有三组分量掩码:xyzwrgba(用于颜色)、stpq(用于纹理坐标)。这三组没有实际区别,只是语法糖。不能在单次分量访问操作中混合使用不同组的名称。

OpenGL 4.2 或 ARB_shading_language_420pack 起,标量也可以分量访问:

glsl
float aFloat;
vec4 someVec = aFloat.xxxx;

矩阵类型

所有矩阵类型都是浮点类型,可以是单精度或双精度。其中 nm 可以是 2、3 或 4:

类型说明
matnxmn 列 m 行的矩阵(注意与数学约定相反!)
matnmatnxn 的简写,n×n 方阵

双精度矩阵使用 dmat 前缀(如 dmat4x4)。

OpenGL 以列主序(column-major)格式存储矩阵——即元素在列中连续存储。

分量访问不适用于矩阵。可以使用数组语法访问矩阵字段:

glsl
mat3 theMatrix;
theMatrix[1] = vec3(3.0, 3.0, 3.0); // 设置第二列为全 3.0
theMatrix[2][0] = 16.0; // 设置第三列第一个元素为 16.0
theMatrix[1].yzx = vec3(3.0, 1.0, 2.0); // 第一个数组访问结果是向量,可以分量访问

不透明类型

不透明类型(Opaque Types)表示着色器以某种方式引用的外部对象。不透明变量没有与常规类型相同的"值";它们是引用真实数据的标记。

不透明类型变量只能以两种方式之一声明:

  1. 作为全局作用域的 uniform 变量
  2. 作为 in 限定的函数参数

不透明类型不能是左值。

采样器

采样器类型是表示绑定到 OpenGL 上下文的纹理的不透明 GLSL 类型。每种纹理类型都有对应的采样器类型。

详见 采样器

图像

图像变量引用存储在纹理中的特定类型图像,用于着色器内的任意加载/存储操作。

原子计数器

原子变量表示缓冲区对象中可以进行原子操作的内存位置。

隐式转换

某些值可以隐式转换为某些类型:

  • 有符号整数可以隐式转换为无符号整数(反之不行)
  • 任一整数类型可以转换为浮点数
  • 整数和浮点数可以转换为双精度

如果向量或矩阵包含的基本类型可以隐式转换,则它们也可以隐式转换。

数组

基本类型可以分组为元素序列,称为数组。通常必须用常量表达式初始化的大小来声明数组。

glsl
uniform float myValues[12];

myValues.length(); // 返回 12

调用 length() 函数产生常量表达式,除非变量是存储块中最后一个成员的无大小数组。

不透明数组

在 GLSL 3.30 中,采样器数组只能通过编译时常量表达式访问。

在 GLSL 4.00 及以上,访问不透明值的数组索引可以是非常量,但必须是动态一致的:

glsl
uniform sampler images[10];
uniform int imageCount;

void main()
{
  vec4 accum = vec4(0.0);
  for (int i = 0; i < imageCount; i++)
  {
    accum += texture(images[i], ...);
  }
}

多维数组

OpenGL 4.3 或 ARB_arrays_of_arrays 起,可以声明多维数组:

glsl
uniform vec3 multidim[5][2]; // 5 个元素的数组,每个元素是 2 个 vec3 的数组
uniform vec3[5][2] multidim1; // 等价写法

WARNING

数组在变量名上从左到右读取,然后类型名上的数组从左到右读取。所以 vec3[5] multidim2[2] 是 2 个元素的数组,每个元素是 5 个 vec3 的数组。

结构体

结构体定义方式与 C++ 类似:

glsl
struct Light
{
  vec3 eyePosOrDir;
  bool isDirectional;
  vec3 intensity;
  float attenuation;
} variableName; // 可选的变量声明

WARNING

GLSL 不支持匿名结构体(没有类型名称的结构体),结构体必须至少有一个成员声明。结构体不能在另一个结构体内定义,但可以使用之前定义的结构体作为成员。

结构体可以包含不透明类型,但此类结构体只能用于不透明类型可用的方式。

结构体字段使用标准的 "." 语法访问。

构造函数和初始化器

几乎所有类型的变量都可以用初始值初始化。例外包括:

  • 输入或输出限定变量
  • 任何不透明类型的变量
  • 接口块中声明的变量

字面值

字面值是写入着色器中的值:

  • 布尔字面值:truefalse
  • 整数字面值默认为 int 类型,使用 uU 后缀表示 uint
  • 浮点字面值默认为 float 类型,使用 fF 后缀
  • 双精度字面值使用 lfLF 后缀(GLSL 4.00+)

整数字面值可以使用八进制(前缀 0)或十六进制(前缀 0x)表示。

构造函数

复杂类型使用构造函数初始化:

glsl
typename(value1, value2, ...)

转换构造函数

基本标量类型构造函数可用于类型转换:

转换说明
boolfalse → 0/0.0,true → 1/1.0
bool0/0.0 → false,其他 → true
intuint保证保留位模式(可能改变符号)

向量构造函数

向量可以从单个值构造(所有分量都设为该值):

glsl
vec3(1.0) == vec3(1.0, 1.0, 1.0);

也可以从其他向量或标量组合构造:

glsl
vec4(vec2(10.0, 11.0), 1.0, 3.5);
vec3(vec4(1.0, 2.0, 3.0, 4.0)); // 截取前三个分量
vec4(vec3(1.0, 2.0, 3.0)); // 错误:分量不足

矩阵构造函数

  • 单个标量值:对角线初始化为该值,其余为 0。mat4(1.0) 是 4×4 单位矩阵
  • 多个值:按列主序填充
glsl
mat2(
  float, float,   // 第一列
  float, float);  // 第二列

mat4(
  vec4,           // 第一列
  vec4,           // 第二列
  vec4,           // 第三列
  vec4);          // 第四列

矩阵可以从另一个矩阵构造,对应元素被复制,未填充值使用单位矩阵填充。

数组构造函数

glsl
const float array[3] = float[3](2.5, 7.0, 1.5);
// 大小可省略
const float array[] = float[](2.5, 7.0, 1.5);

结构体构造函数

按定义顺序填充值:

glsl
struct Data
{
  float first;
  vec2 second;
};

Data dataValue = Data(1.4, vec2(16.0, 22.5));

初始化列表

GLSL 4.2 或 ARB_shading_language_420pack 起,可以使用初始化列表:

glsl
Data dataArray[3] = {
  {1.0, {-19.0, 4.5}},
  {-3.0, {2.718, 2.0}},
  {29.5, {3.142, 3.333}}
};

编译器会自动推导类型。初始化列表可以用于初始化任何类型,包括标量值。

接口块

使用 input、output 或 uniform 类型限定符的变量组可以分组为命名块。详见 接口块

基于 OpenGL Wiki 的本地化项目