WebGL从零开始(五)—— 复习下GLSL ES 语言的语法

8/20/2026 WebGL

GLSL ES(OpenGL ES Shading Language)是 WebGL 的核心,它的语法类似 C/C++,但针对 GPU 并行计算做了很多特殊限制。

# 1.打印向量的方法

GLSL 运行在 GPU 上,没有 console.log 或 print 这样的控制台输出函数。

# 常用调试方法:

  • 颜色可视化(最常用):将向量(如位置、法线)直接赋值给 gl_FragColor。例如 gl_FragColor = vec4(normal, 1.0);,通过观察屏幕上的颜色来验证数据是否正确。
  • 编码到 Alpha 通道:如果数据超出 0~1 范围,可以将其映射到颜色空间,或者利用 Alpha 通道传递数据,在 JS 端通过 readPixels 读取。
  • 条件高亮:使用 if 判断特定条件,满足时输出高亮颜色(如红色),不满足输出黑色。

颜色法:通过将向量赋值给片元着色器,然后通过抓取像素的方法获取上面的 rgba 值,即可获取到,示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>GLSL语法测试</title>
  <style>
    html,
    body {
      padding: 0;
      margin: 0;
      box-sizing: border-box;
      overflow: hidden;
    }
    #canvas {
      font-size: small;
    }
  </style>
</head>
<body>
  
  <canvas id="canvas"></canvas>
  <script id="vertexSharderSource" type="notjs" >
    void main() {
      gl_Position = vec4(0,0,0,1);
      gl_PointSize = 512.0;
    }
  </script>
  <script  id="fragmentShaderSource" type="notjs" >
    precision mediump float;
    vec4 v = vec4(1,2,3,4) + vec4(5,6,7,8);
    void main() {
      gl_FragColor = v/255.0; // 因为 gl_FragColor 中1个单位的分量就相当于 画布中255的分量值,所以要除以 255
    }
  </script>

  <script type="module">
    import { initProgram } from '../utils/utils.js'
    const canvas = document.getElementById('canvas');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    const gl = canvas.getContext('webgl');
    // 获取着色器代码
    const vsShaderSource = document.getElementById('vertexSharderSource').innerText;
    const fsShaderSource = document.getElementById('fragmentShaderSource').innerText;
    // 初始化着色器和着色程序
    initProgram(gl, vsShaderSource, fsShaderSource);

    // 声明颜色
    gl.clearColor(0, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);

    // 绘制点
    gl.drawArrays(gl.POINTS, 0, 1);

    // 像素容器
    const pixel = new Uint8Array(4)
    // 抓取像素 readPixels(x,y,width, height, format, type, pixels, dstOffset, Optional)
    gl.readPixels(canvas.width/2, canvas.height/2, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
    console.log(pixel);
  </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

由于目标点的坐标是 gl_Position = vec4(0,0,0,1); 在画布中心,readPixels 方法的 x,y就是canvas.width/2, canvas.height/2; 然后取 1 个单位的像素,readPixels 方法的 width, height就是 1; 这里获取 RGBA 属性,所以 format 用的 gl.RGBA; 由于用的 Uint8Array 存取像素的颜色值,则对应的 type 值为 gl.UNSIGNED_BYTE

打印多个向量的值:通过不同层级的渲染不同颜色,然后抓取对应距离的像素。

# 2.变量

# 基础

GLSL 是强类型语言,不支持隐式类型转换(例如 float a = 1; 会报错,必须写 1.0)

  • 基本类型:void, bool, int, float。
  • 向量类型:vec2, vec3, vec4(浮点),ivec(整型),bvec(布尔)。
  • 矩阵类型:mat2, mat3, mat4。
  • 变量限定符:
    • attribute / in:顶点着色器专属,接收来自 CPU 的顶点数据(每个顶点不同)。
    • uniform:全局变量,接收 CPU 传来的批次数据(如 MVP 矩阵、时间,一次绘制中所有顶点相同)。
    • varying / out & in:用于顶点着色器向片元着色器传递数据,GPU 会自动进行插值。

# 类型转换

  • 浮点转整数:int(float),如 int a = int(1.0);
  • 布尔转整数:int(bool),如 int a = int(true);
  • 整数转浮点:float(int),如 float b = float(1);
  • 布尔转浮点:float(bool),如 float b = float(true);
  • 整数转布尔:bool(int),如 bool c = bool(1);
  • 浮点转布尔:bool(float),如 bool c = bool(0.0);

# 3.运算符与 Swizzling(分量重组)

# 基础运算

+, -, *, / 在向量之间是逐分量(Component-wise)进行的。 例如 vec3(1,2,3) * vec3(4,5,6) 结果是 vec3(4,10,18)。

# Swizzling(极其重要)

通过字母快速提取或重组向量分量。

  • 坐标集:x, y, z, w
  • 颜色集:r, g, b, a
  • 纹理集:s, t, p, q 示例:
vec4 color = vec4(1.0, 0.5, 0.2, 1.0); 
vec3 rgb = color.rgb; 
vec4 swapped = color.bgra;
1
2
3

# 4.for循环

由于 GPU 需要并行执行,GLSL ES 对 for 循环有严格限制(为了支持编译器将其展开)

  • 循环变量必须在初始化表达式中声明,且只能是 int 或 float。
  • 条件表达式必须是循环变量与常量的比较(如 i < 10)。
  • 循环表达式只能是 i++, i--, i += 常量, i -= 常量。
  • 循环体内不能修改循环变量。

注:WebGL 1.0 (GLSL ES 1.0) 不支持 while 和 do-while。

# 5.函数

定义方式与 C 语言类似:返回类型 函数名(参数) { ... }。 参数修饰符:in(默认,传入), out(传出), inout(传入且可修改)。 支持重载(同名函数,参数类型/数量不同)。

限制:不能递归;返回的结构体中不能包含数组。

# 6.作用域与生命周期

  • 局部作用域:在 {} 内声明的变量,离开块即销毁。
  • 全局作用域:在函数外声明,整个 Shader 文件可见。
  • GPU 生命周期:uniform 在一次 drawCall 中保持不变;attribute 随顶点变化;varying 在光栅化阶段由顶点值插值生成。
Last Updated: 9/16/2026, 9:23:32 AM