4.1 GLSL语言基础:类型与限定符


4.1 GLSL语言基础:类型与限定符

本节摘要:GLSL 是为大规模数据并行定制的类 C 语言,与 C 的差异全部围绕「一次定义、百万次并行执行」这个模型。本节解剖三组核心构造:向量矩阵原生类型(含分量访问语法)、存储限定符体系(in/out/uniform/buffer 各自的数据流向与生命周期)、以及内置变量与标准库。穿插「接口名不匹配」「精度混算」两个高发病的诊断。这是全册代码的语法地基。

从执行模型反推语言设计

先问一个问题:为什么 C 语言不能直接当着色器语言用?答案在执行模型。你的 GLSL 函数会被 GPU 在同一时刻对成千上万个顶点或片元并发调用,每个调用拿不同数据、彼此隔离。这意味着语言必须天然满足三条:无指针算术(内存模型完全不同)、函数无副作用(一个片元算的颜色不能直接污染邻居)、隐式并行(代码里没有循环,循环在调用维度上)。GLSL 每处与 C 的差异都能追溯到这三条。比如没有 malloc——GPU 显存的分配是显式资源(第 3 章的对象们),不是运行时堆。

向量与矩阵:一等公民

GLSL 把 vec2/vec3/vec4、mat2/mat3/mat4 做成原生类型,运算符直接重载——这不是语法糖,是「图形代码 90% 在做向量运算」这个领域事实的直译:

vec3 a = vec3(1.0, 2.0, 3.0); vec3 b = vec3(0.5); mat3 m = mat3(1.0); // 单参数构造的是单位矩阵的简写 vec3 c = m * a; // 矩阵乘向量:一次写完三个线性组合 float d = dot(a, b); // 点乘:光照计算的心脏 vec3 e = cross(a, b); // 叉乘:求法线的心脏 vec3 f = normalize(a); // 归一化:插值后的必修课(2.3 节铁律)

分量访问(swizzle)是 GLSL 最有辨识度的语法:向量可以像 a.xyza.rgba.st 这样取分量,三套名字(几何、颜色、纹理坐标)任选,还能重排与复制——a.bgra 反转颜色通道、vec2 uv = a.xy 截取前两维,都是地道写法。限定一条:同一个表达式里不能混用两套命名(a.rgz 非法),这是编译错误的高频来源之一。

精度上要警惕整型与浮点的楚河汉界:GLSL 里字面量 1 是整数、1.0 是浮点,vec3(1) 直接编译报错(新版宽松了些,但养成习惯写 1.0);整数除法截断、浮点比较不能用等号(要用误差区间)。着色器调试难,类型错误要在编译期全部抓干净,别留到运行期。

存储限定符:数据的三车道

限定符体系回答「数据从哪来、到哪去、活多久」。按数据流向分三车道:

图 4-1 着色器接口的三车道:数据怎么流进流出着色器

图 4-1 着色器接口的三车道:数据怎么流进流出着色器

三车道的判断口诀值得钉在脑子里。逐单元不同的数据走 in(每顶点的位置、每片元的插值结果);一次绘制内恒定的走 uniform(矩阵、光源、时间);要跨阶段传递的顶点着色器 out 接片元着色器 in,名字就是接口协议。名字失配的报错藏在链接日志里(「varying 未被消费」之类),链接通过但画面黑时第一件事是读日志、第二件事是对名单——这个诊断顺序能省掉你几个小时。

内置变量与标准库

GLSL 预置了一批「法定变量」:gl_Position(顶点着色器输出裁剪坐标,2.1 节)、gl_FragCoord(片元的屏幕坐标,含深度,做屏幕空间效果时用它)、gl_VertexID(顶点序号,无属性也能生成几何的钥匙)。标准库按领域打包:几何函数(dot/cross/normalize/reflect)、矩阵函数(inverse/transpose)、纹理函数(texture 及采样偏移变体)、插值函数(mix/smoothstep——smoothstep 的三次平滑过渡是无数渐入渐出效果的三行实现)。用内置替代手写,不止省代码:驱动会对内置调用做针对性优化,手写等价逻辑反而可能错过。

💡 关键直觉:写 GLSL 的地道感来自「向量化思维」。一段对 x、y、z 分别算的代码,合并成一次 vec3 运算后,GPU 的向量单元一次完成三路——代码更短且更快。反过来,标量思维写的 GLSL 能跑但浪费了这门语言的硬件红利。

一个完整的语言解剖样本

把本节语法收进一段可运行的最小着色器对(顶点 + 片元),标注每个语法点:

// —— 顶点着色器 —— #version 330 core // 版本声明必须第一行 layout (location = 0) in vec3 aPos; // 属性门牌0:位置 layout (location = 1) in vec2 aUV; // 属性门牌1:纹理坐标 uniform mat4 mvp; // 绘制级常量:合并的变换矩阵 out vec2 vUV; // 交棒变量:坐标传给片元 void main() { gl_Position = mvp * vec4(aPos, 1.0); // 升四维 · 一次乘完变换链(4.3 节) vUV = aUV; } // —— 片元着色器 —— #version 330 core in vec2 vUV; // 名字必须与 out 完全一致 uniform sampler2D tex; // 采样器:纹理工位的引用 uniform float mixRatio; // 标量常量:掺色比例 out vec4 FragColor; // 法定输出 void main() { vec3 base = texture(tex, vUV).rgb; vec3 tint = vec3(0.9, 0.95, 1.0); // 冷色调偏色 FragColor = vec4(mix(base, tint, mixRatio), 1.0); // smoothstep 同族的插值 }

本节要点回顾

  • 执行模型决定语言形态:隐式并行、无堆指针、函数隔离,C 的差异都有出处
  • 向量化思维是地道感的来源:原生向量类型与分量访问是硬件红利,标量写法能跑但浪费
  • 三车道口诀:逐单元 in、绘制级 uniform、跨阶段 out(名字即协议)
  • 接口病先读链接日志再对名单:黑屏加日志警告 = 名字失配的高概率指纹
  • 精度楚河汉界:字面量写 1.0,整数浮点不混算,浮点不用等号比较
  • 内置函数优先于手写:驱动优化认内置,三行 smoothstep 胜过十行手写过渡

语法地基打好了。下一节解剖程序诞生的流水线——编译、链接、日志诊断,以及运行期即时编译这个设计的历史包袱与现代解法。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U