初识GLSL
GLSL(Graphics Library Shader Language)是一种用于编写着色器程序的编程语言,特别用于在图形处理单元(GPU)上执行图形渲染任务, 有以下几个特点:
- 类C的语法风格
- 强类型语言
- 方便向量和矩阵的计算
- 内置的数据结构,包括向量类型(vec2、vec3、vec4)、矩阵类型(mat2、mat3、mat4)等
第一个代码片段
当我们在ShaderToy中新建一个Shader会生成下面的代码:
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// 将坐标归一化到0-1之间
vec2 uv = fragCoord/iResolution.xy;
// 时间变化的颜色
vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
// 输出到屏幕
fragColor = vec4(col,1.0);
}上面的代码片段,我们定义了一个mainImage函数,这个函数是我们着色器程序的核心。这个函数接收两个参数fragColor和fragCoord。参数前的in和out表示函数输入和输出。
我们先来看fragCoord这个输入参数。正如前文提到的,ShaderToy内部帮我们实现了顶点着色器,直接给我们提供了输入。fragCoord是一个vec2类型的值,它代表了画布的XY坐标,左下角的值为(0,0)而右上角是 (iResolution.x, iResolution.y),如下图所示:

通常我们为了方便处理会把坐标归一化成(0,1)或者是(-1,1)
vec2 uv = fragCoord/iResolution.xy;接着是输出参数fragColor。fragColor是一个vec4类型的值,代表了片元处理阶段的最终输出颜色,vec4类型包含四个分量(r,g,b,a),分别代表红,绿,蓝还有透明通道(alpha),和CSS颜色常用的Hex Color不同,每个分量的取值范围通常是从0到1,其中0表示最小值(没有颜色或完全透明),1表示最大值(最大亮度或完全不透明)。
// vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
vec3 col = vec3(0.);点击编辑器左下角的编辑按钮或者用快捷键 Alt+Center 、Option+Enter, 就可以看到画布变成黑色了。
GLSL的语法
正如上面提到的,GLSL采用了类似于C语言的语法风格,因此我们不会过多介绍相似的部分。现在让我们重点关注GLSL为我们处理矩阵和矢量提供的语法。
GLSL矢量数据提供了多种分量选择器,这里以vec4为例, 其余vec2和vec3同理:
v.x和v.s以及v.r,v0表达的是同一个分量v.y和v.t以及v.g,v1表达的是同一个分量v.z和v.p以及v.b,v2表达的是同一个分量v.w和v.q以及v.a,v3表达的是同一个分量
另外GLSL还支持Swizzle,它是一种用于重新排列向量分量的操作。比方说:
v.yyyy
// 等价于 vec4(v.y, v.y, v.y, v.y)
v.bgra
// 等价于 vec4(v.b, v.g, v.r, v.a)在我们第一个代码片段中也有用到,当构造一个矢量或矩阵时可以一次提供多个分量,例如:
vec3 color = vec3(0., 0., 0.);
vec4(col,1.0)
// 等价于 vec4(col.r, col.g, col.b, 1)
vec4(0.)
// 等价于 vec4(0., 0., 0., 0.)此外,在执行矢量和矩阵运算时,GLSL的语法也相当简洁,例如常量和矢量的乘法。
vec2 a = vec2(1., 1.);
vec2 b = a * 2.0;
// b 现在是 vec2(2., 2.);
vec2 c = a - 1.
// c 现在是 vec2(0., 0.);
// 加减乘除同理它同样可以做矩阵乘法以及矢量和矩阵的乘法
mat4 a = mat4(1.0);
mat4 b = mat4(2.0);
mat4 c = a * b; // mat4(8.0);
vec4 v = vec4(1., 0., 1., 0.)
vec4 y = c * v; // vec4(16.);更多详细的GLSL语法可以去这里查看GLSL 规范