前言
在上一篇文章中我们初步了解了 GLSL ES 的基本语法,那么本篇文章就和大家一起学习 GLSL ES 的数据类型。
Let's go!!!
正文
数据类型
· 标量(Scalar)
标量表示只有大小没有方向的量。
- 关键字
关键字 | 含义 |
---|---|
int | 有符号整型(Signed Integer) |
uint | 无符号整型(Unsigned Integer) |
float | 单精度浮点型(Single Floating-Point) |
bool | 布尔型(Boolean) |
声明并赋值:
代码语言:javascript复制int age = 18; // 整型
uint hello = 3u; // 无符号整型,在后面数字加 u
float pi = 3.14; // 浮点型
bool isMe = true; // 布尔型
- 类型转换
标量类型之间可以互相转换:
- 将
float
类型的值转换为int
和uint
时小数点后面的值将会被忽略,要注意负的float
类型的值不能转换为uint
类型。 - 当
int
、uint
或float
类型的值转换为bool
类型时,0
或0.0
将会被转换为false
,所有非零的值都会被转换为true
。 - 当
bool
类型的值转换为int
、uint
或float
类型时,false
将会被转换为0
或0.0
,true
会被转换为1
或1.0
。
我们可以使用标量类型的构造函数来进行类型转换:
代码语言:javascript复制int a = 1;
float b = float(a); // 1.0
int c = int(b); // 1
uint d = uint(c); // 1u
bool e = bool(d); // true
当尝试将非标量值转换为标量值时,实际处理的将会是非标量值的第一个元素:
代码语言:javascript复制vec3 a = vec3(0.1, 0.2, 0.3);
float b = float(a); // 0.1
—▼—
· 向量(Vector)
在 GLSL 中向量一般用于储存顶点坐标、颜色或纹理坐标数据。
一个向量可以包含 2 到 4 个分量(Component),分量的类型也可以是以上基础类型中的任意一个,一般情况下我们使用浮点型 vecn
就已经足够了。
- 关键字
> 下面表格中的 n
为分量的个数
关键字 | 含义 | 举例 |
---|---|---|
vecn | 包含 n 个 float 类型分量的向量 | vec2、vec4 |
ivecn | 包含 n 个 int 类型分量的向量 | ivec2、ivec4 |
uvecn | 包含 n 个 uint 类型分量的向量 | uvec2、uvec4 |
bvecn | 包含 n 个 bool 类型分量的向量 | bvec2、bvec4 |
- 创建向量
我们可以使用不同的构造函数来创建相应的向量:
代码语言:javascript复制vec2 coord = vec2(0.5, 0.5); // 含有 2 个 float 类型分量的向量
ivec3 police = ivec3(1, 1, 0); // 含有 3 个 int 类型分量的向量
bvec4 hello = bvec4(true, false, true, false); // 含有 4 个 bool 类型分量的向量
只传入一个参数的情况下会自动将其他值也设为第一个参数:
代码语言:javascript复制vec3 three = vec3(0.1); // 等同于 vec3(0.1, 0.1, 0.1)
也可以使用一个向量作为参数传给另一个向量构造函数:
代码语言:javascript复制vec2 two = vec2(0.1, 0.2);
vec3 three = vec3(two, 0.3); // vec3(0.1, 0.2, 0.3)
而将“大”向量作为参数来创建“小”向量(降维)会自动抛弃多余的值:
代码语言:javascript复制vec4 four = vec4(0.1, 0.2, 0.3, 0.4);
vec3 three = vec3(four); // 等同于 vec3(0.1, 0.2, 0.3),抛弃了 0.4
- 分量(Component)
通过分量名来获取向量中的第 1 到第 4 个分量,包括:
> 实际上所有向量都可以使用 rgba
、rgba
和 stpq
分量名,但是为了代码的严谨性和可读性,建议使用相应的分量名
分量名(对应第 1 - 4 个分量) | 使用场景 |
---|---|
x, y, z, w | 顶点坐标向量 |
r, g, b, a | 颜色向量 |
s, t, p, q | 纹理坐标向量 |
使用 .
运算符加分量名来获取向量的分量:
// 顶点坐标
vec2 coord = vec2(1.0, 0.5);
float x = coord.x; // 1.0
float y = coord.y; // 0.5
float z = coord.z; // Error! 不存在
// 颜色
vec4 color = vec4(0.6, 0.8, 1,0, 0.5);
float r = color.r; // 0.6
float a = color.a; // 0.5
// 纹理坐标
vec4 texCoord = vec4(0.2, 0.4, 0.6, 0.8);
float t = texCoord.t; // 0.4
float p = texCoord.p; // 0.6
- 重组(Swizzling)
另外,你还可以使用同一组分量名的任意组合来创建一个新的向量:
代码语言:javascript复制vec4 coord = vec4(0.1, 0.2, 0.3, 0.4);
vec2 one = coord.xx; // vec2(0.1, 0.1)
vec2 two = coord.xy; // vec2(0.1, 0.2)
vec3 three = coord.xzw; // vec3(0.1, 0.3, 0.4)
vec4 four = coord.wzyx; // vec4(0.4, 0.3, 0.2, 0.1)
vec4 boom = coord.xyzw coord.wzyx; // vec4(0.5, 0.5, 0.5, 0.5)
vec4 hello = vec4(coord.zyx, 0.0); // vec4(0.3, 0.2, 0.1, 0.0)
—▼—
· 矩阵(Matrix)
一种类似于表格的复合数据类型,矩阵最多能够支持 4 列 4 行的数据,且其元素只能够为 float
类型。
- 关键字
> 下面表格中的 n
和 m
皆为 2 到 4 的任意数字
关键字 | 含义 | 举例 |
---|---|---|
matnxn /matn | 表示一个 n 列 n 行的浮点型矩阵 | mat2、mat3、mat3x3 |
matnxm | 表示一个 n 列 m 行的浮点型矩阵 | mat2x3、mat4x3 |
- 创建矩阵
使用不同的构造函数来创建相应的矩阵:
代码语言:javascript复制// 创建一个 2x2 的矩阵
mat2 two = mat2(0.1, 0.2, // 第一列
0.3, 0.4); // 第二列
代码语言:javascript复制// 创建一个 3x3 的矩阵
mat3 three = mat3(0.1, 0.2, 0.3, // 第一列
0.4, 0.5, 0.6, // 第二列
0.7, 0.8, 0.9); // 第三列
只传入一个参数的情况下会自动补零:
代码语言:javascript复制mat2 two = mat2(1.0); // 等同于 mat2(1.0, 0.0, 0.0, 0.0)
mat3 three = mat2x3(1.0); // 等同于 mat3(1.0, 0.0, 0.0, 0.0, 0.0, 0.0)
我们也可以向构造函数传入向量来创建矩阵:
代码语言:javascript复制vec2 a = vec2(1.0, 0.0);
vec2 b = vec2(0.5, 0.1);
mat2 four = mat2(a, b); // 等同于 mat2(1.0, 0.0, 0.5, 0.1)
// 创建一个 2x3 的矩阵
mat2x3 haha = mat2x3(a, 0.3,
b, 0.2); // 等同于 mat2x3(1.0, 0.0, 0.3, 0.5, 0.1, 0.2)
降维操作会自动抛弃多余的元素,升维则会自动补零:
代码语言:javascript复制// 伪代码
mat3x3(mat4x4); // 保留参数的前 3 列前 3 行的元素
mat2x3(mat4x2); // 保留参数的前 2 列前 2 行的元素,第 3 行补零
- 获取元素
可以通过 []
操作符来获取矩阵的某个元素(下标从 0 开始):
mat3 three = mat3(0.1, 0.2, 0.3, // 第一列
0.4, 0.5, 0.6, // 第二列
0.7, 0.8, 0.9); // 第三列
float el = three[0][2]; // 获取第一列第三行的元素:0.3
也可以通过分量名来获取元素:
代码语言:javascript复制float el = three[0].z; // 同上,获取第一列第三行的元素:0.3
—▼—
· 采样器(Sampler)
在 GLSL 中我们需要通过采样器来获取纹理的信息。
采样器只能在 Shader 外部的宿主语言中通过 OpenGL 的 API 来进行赋值。
- 关键字
> 采样器的类型较多,这里只列出了常见的几个
关键字 | 含义 |
---|---|
smapler2D | 用来访问 2D 纹理的句柄 |
sampler3D | 用来访问 3D 纹理的句柄 |
samplerCube | 用来访问立方体映射纹理的句柄 |
sampler2DArray | 用来访问 2D 纹理数组的句柄 |
> 剩下不常用的还有 samplerCubeShadow
、sampler2DShadow
、sampler2DArrayShadow
、isampler2D
、isampler3D
、isamplerCube
、isampler2DArray
、usampler2D
、usampler3D
、usamplerCube
、usampler2DArray
。
采样器必须使用 uniform
关键字来修饰(关于 uniform
我们后面会说到):
uniform sampler2D texture;
通过内置的 texture
函数获取颜色:
uniform sampler2D myTexture;
// 通过内置的 texture 函数获取 myTexture 纹理 uv_0 坐标处的颜色
vec4 color = texture(myTexture, uv_0);
—▼—
· 结构体(Structure)
GLSL 允许你使用 struct
关键字来自定义一个新的类型,新的自定义类型可以包含其他已经定义的类型:
// 定义一个名为 circle 的类型,包含一个浮点型成员和一个四维向量成员
struct circle {
float radius;
vec4 color;
};
// 创建一个 circle 类型的变量
circle myCircle;
// 单独给 radius 赋值
myCircle.radius = 0.5;
定义新的结构体时可以包含已经定义的结构体,但是不能够在结构体中定义新的结构体:
代码语言:javascript复制// 结构体 A
struct A {
float f;
};
// 结构体 B
struct B {
A a; // 可
A; // Error! 禁止匿名成员
struct { ... }; // Error! 不可
}
—▼—
· 数组(Array)
GLSL 支持一维数组,只需要在变量名称后面接上一对方括号[]
。
数组的长度必须大于 0,可以使用字面量或者整型常量:
代码语言:javascript复制// 字面量
float values[3];
// 整型常量
const int COUNT = 3;
float values2[COUNT];
作为函数的返回值或参数的类型:
代码语言:javascript复制// 返回值类型
float[5] getValues() { ... }
// 参数类型
void setValues(float[2] values) { ... }
使用相应的构造函数初始化数组:
代码语言:javascript复制float a[2] = float[2](0.1, 0.2);
// 另外下面这 4 种方式是一样的
float b[3] = float[3](0.1, 0.2, 0.3);
float b[] = float[3](0.1, 0.2, 0.3);
float b[3] = float[](0.1, 0.2, 0.3);
float b[] = float[](0.1, 0.2, 0.3);
同样通过 []
运算符来获取数组中的元素(下标从0 开始):
float a[3] = float[3](0.1, 0.2, 0.3);
float b = a[1]; // 0.2
通过数组的 length
函数可以获取数组的长度(int
类型):
int a[3] = int[3](1, 2, 3);
int b = a.length(); // 3
—▼—
· 空(Void)
void
一般用于函数返回值或参数的声明:
void main(void) {
// ...
}
相关资料
- OpenGL ES Registry(OpenGL ES 资料页)https://www.khronos.org/registry/OpenGL/index_es.php
- OpenGL ES 3 Quick Reference Card(OpenGL ES 3 快速参考卡片)https://www.khronos.org/files/opengles3-quick-reference-card.pdf
- GLSL ES Specification 3.00(GLSL ES 规范 3.0)https://www.khronos.org/registry/OpenGL/specs/es/3.0/GLSL_ES_Specification_3.00.pdf
- OpenGL ES 3.0 Online Reference Pages(OpenGL ES 3.0 在线参考页)https://www.khronos.org/registry/OpenGL-Refpages/es3.0/