The Book of Shaders 术语表详解:GLSL struct 自定义结构体的声明、自动构造器与成员访问
The Book of Shaders 术语表详解:GLSL struct 自定义结构体的声明、自动构造器与成员访问
本文基于 The Book of Shaders(thebookofshaders)仓库术语表中的 struct 词条,系统讲解 GLSL 中 struct 关键字的声明语法、自动生成的构造器机制,以及变量声明的可选语义。读完本文后,你将能够独立定义自己的着色器数据结构(如光照材料、调色板等),正确使用其构造器完成初始化,并理解它在 GLSL 与 JavaScript 对象之间的对应关系与边界。
1. struct 在术语表中的位置
The Book of Shaders 是一本循序渐进讲解 Fragment Shader 的开源教程,其根目录下的 术语表总索引 按主题(TYPES、QUALIFIERS、BUILT-IN VARIABLES 等)和字母序组织了全部 GLSL 词条,struct 被归类在 TYPES(类型) 主题下。
术语表每个词条都是一个独立目录,其中存放多语言版本说明,struct 词条目录包含三个版本:
词条页面由 glossary/index.php 渲染:脚本根据 URL 参数 ?search=struct 定位到对应子目录下的 README[-语言].md 文件,再通过 Parsedown 将其转换为 HTML 输出。从源码结构看,每个词条因此都可以作为独立页面被检索和引用,这也是本文以该词条为骨架的原因。
2. struct 声明什么:基于标准类型的用户自定义结构体
struct 关键字用于声明一个基于标准类型组合而成的用户自定义数据结构。术语表词条给出的示例是一个经典的着色器"材料"(material)参数打包,把传统 Phong 光照模型中的环境色、漫反射色、镜面反射色与镜面指数收纳进同一个类型:
struct matStruct {
vec4 ambientColor;
vec4 diffuseColor;
vec4 specularColor;
float specularExponent;
} newMaterialOne, newMaterialTwo; // 同时声明变量 newMaterialOne 与 newMaterialTwo(类型均为 matStruct)
// 变量 newMaterialOne 的声明处已写过类型,此处赋值时不再需要重复写 matStruct
newMaterialOne = matStruct(
vec4(0.1, 0.1, 0.1, 1.0),
vec4(1.0, 0.0, 0.0, 1.0),
vec4(0.7, 0.7, 0.7, 1.0),
50.0
);
// matStruct 此后作为类型名用于声明同类型的变量
matStruct anotherNewMaterial = matStruct(...);
从这段示例可以读出三层语法事实:
- 字段类型必须是已有的标准类型。示例中的字段全部由
vec4与float构成——struct本身不创造新数据格式,只是把既有类型按固定顺序组合命名。 - 构造器调用
matStruct(...)的实参顺序与字段声明顺序一一对应:前三个vec4分别填入ambientColor、diffuseColor、specularColor,最后的float填入specularExponent。从示例的写法看,实参类型也需与字段类型匹配。 - 类型名之后可以紧跟一个或多个变量声明。这与 C 语言的结构体声明风格一致:
matStruct从此同时具有"类型"身份,anotherNewMaterial = matStruct(...)这类写法就靠它成立。
3. 自动构造器与"变量声明可选"语义
术语表对 struct 的核心描述是两条规则(英文原文见 glossary/struct/README.md):
structdeclares a custom data structure based on standard types. A constructor for the structure with the same name is created automatically. The declaration of a variable (in this case "newMaterial") is optional.
- 同名构造器自动生成:一旦
struct matStruct { ... }被声明,语言自动提供名为matStruct的构造器,无需手写初始化函数。这与附录中讲解向量构造器的思路一致——appendix/04/README.md 在解释vec4构造器重载时就说明了"函数按不同签名被自动/显式提供多个版本"这一 GLSL 特性,struct的构造器是同一机制在自定义类型上的体现。 - 变量声明是可选的:
struct Foo { ... };后面直接跟分号也完全合法,此时只引入类型本身;也可以像词条示例那样在声明处就顺带创建变量。
英文原版示例声明了单个变量 newMaterial,而乌克兰文版的示例更进一步,演示了一次声明多个同名结构体变量(newMaterialOne, newMaterialTwo),并逐行注释了"之后对该变量赋值时不必再写类型名"的用法,比原版多覆盖了多变量声明这一分支。
4. 与 JavaScript 对象的对应关系:"没有方法的对象"
术语表词条偏重语法,而 appendix/04/README.md("给 JavaScript 开发者的 GLSL 入门"一节,乌克兰文版见 appendix/04/README-ua.md)从语言对比角度给出了更完整的语义描述:
If you want a more expressive way of declaring a variable, there is also a
structtype. These are like objects without methods; they allow to store and access multiple variables inside the same object.
即 struct 相当于没有方法(methods)的对象:只能存储和访问数据,不能挂接行为。该节同时给出了另一个示例——ColorStruct 打包三个 vec3 颜色:
struct ColorStruct {
vec3 color0;
vec3 color1;
vec3 color2;
}
// 初始化:调用与结构同名的构造器,实参按字段顺序传入
ColorStruct sandy = ColorStruct( vec3(0.92,0.83,0.60),
vec3(1.,0.94,0.69),
vec3(0.95,0.86,0.69) );
// 通过 "变量名.字段名" 访问成员
sandy.color0 // vec3(0.92,0.83,0.60)
这里还隐含了 GLSL 与 JS 的重要差异背景:GLSL 是编译型、强类型语言,结构体的字段类型在编译期即确定(见 appendix/04/README.md 对 strong typing 的论述),因此 struct 的字段集合在着色器编译后是封闭的,不能像 JS 对象那样在运行时增删属性。对 JS 背景的作者来说,把 sandy.color0 读作"取对象属性"是成立的,只是它背后是编译期就布局好的内存字段,而非动态键值表。
5. 进阶用法:结构体数组与 uniform 数组
在 06/notes.md 中,作者记录并验证了 GLSL 中 struct 的一个实战形态——以结构体类型声明 uniform 数组,这展示了结构体在"从 CPU 向 GPU 批量传参"场景下的价值:
precision mediump float;
struct my_struct {
vec4 color;
};
uniform my_struct u_colors[2];
void main(void) {
gl_FragColor = u_colors[0].color;
}
这个片段(作者在笔记中标注其源自 Khronos 的 WebGL 一致性测试)证明了两点:
- 结构体类型可以作为数组元素的类型(
uniform my_struct u_colors[2]),适合从 CPU 端传递一组同构数据,例如多套配色方案; - 成员访问语法为下标 + 点号链式:
u_colors[0].color先取下标再取字段,这一链式访问与sandy.color0属同一套成员访问机制。
这与附录中关于 GLSL 数组的说明相互印证:GLSL 数组大小固定、不能动态增删、需要逐元素赋值(见 appendix/04/README.md),而"结构体数组 + uniform 修饰符"正是在这种限制下组织复杂参数的一种典型做法。
6. 使用要点小结
结合术语表词条与仓库内的示例代码,使用 struct 时需注意:
- 字段只能来自已有标准类型(
float、vec2~vec4、int、矩阵等),组合顺序即构造器实参顺序; - 构造器同名自动生成,初始化一律写成
类型名( 实参1, 实参2, ... ),实参类型与顺序须与字段匹配; - 结构体声明处可附带变量声明,也可以省略;已声明的变量后续赋值时直接写变量名即可,无需重复类型;
- 成员访问统一使用点号,支持
数组下标.字段的链式形式; - 它是"无方法的对象",只做数据打包,不含行为;对 JS 背景的作者可将其理解为编译期定型的轻量数据容器。
延伸阅读路径(均在本仓库内):术语表总索引 可查看全部 GLSL 词条;glossary/struct/README-ua.md 与 glossary/struct/README.md 为 struct 词条本体;appendix/04/README.md 提供 JS 视角的完整类型系统导读;06/notes.md 收录了结构体 uniform 数组的可运行片段。