着色器里的颜色:向量也能当颜料
Color in Shaders
颜色就是一个向量
上一课用 vec2 st 表示位置,用 vec4 表示颜色。它们其实是同一种东西:GLSL 里的向量。所以同一个分量会有好几个名字,用哪个只看你想让代码读起来像什么:
| 下标 | 当坐标用 | 当颜色用 | 当纹理坐标用 |
|---|---|---|---|
[0] | .x | .r | .s |
[1] | .y | .g | .t |
[2] | .z | .b | .p |
[3] | .w | .a | .q |
red.x 和 red.r 读到的是同一个数。这是着色语言故意的设计:让你习惯把颜色和空间坐标换着想。
swizzle:随手重排分量
分量名可以按任意顺序拼在一起,这叫 swizzle。比如 yellow.rbg 就是把黄色的三个分量按「红、蓝、绿」的顺序重新取一遍:绿和蓝对调,黄色变成了品红。
也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。
mix:在两种颜色之间走
mix(a, b, t) 按比例 t 在 a 和 b 之间插值:t 为 0 时是 a,为 1 时是 b。t 的范围正好是 0 到 1,上一课练的塑形函数在这里直接就能用上。
下面用 abs(sin(u_time)) 让 t 在 0 和 1 之间来回,再过一道 smoothstep,让颜色在两头多停一会儿:
也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。
让三个通道各走各的
mix 的第三个参数也可以是 vec3。这样三个通道就能用不同的比例混合:红色可以早早地过去,蓝色可以拖到最后。给每个通道配一条不同的塑形曲线,普通的两色渐变就会变得有层次。
也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。
HSB:换一种选颜色的方式
RGB 不是唯一的组织颜色的办法。HSB(色相 Hue、饱和度 Saturation、亮度 Brightness,也叫 HSV)更接近人挑颜色的习惯:先选「什么颜色」,再选「多鲜艳」「多亮」。
把横坐标当色相、纵坐标当亮度,整块画布就铺满了可见光谱。拖动滑块改饱和度:
也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。
把色相绕成一圈:极坐标
HSB 原本就是按极坐标设计的:用角度表示色相,用到圆心的距离表示饱和度。要画一个色轮,先求出每个像素相对画布中心的两个量:
- 半径:
length(p),到中心的距离; - 角度:
atan(p.y, p.x),返回 −π 到 π 之间的弧度。
角度还得换算成 0~1 才能当色相用,换算分两步:
整个游戏就是这样:把各种范围想办法挪到 0~1 里来。再给色相加上一点随时间增长的偏移,色轮就转起来了:
也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。
注意这里把表示颜色的 vec3 和表示位置的 vec2 放在一起算,完全合法。除了 length,GLSL 还有 distance、dot、cross、normalize、faceforward、reflect、refract 这些几何函数,以及 lessThan、equal、notEqual 这一族逐分量比较的函数。
在光照里:颜色是相乘出来的
前面都是「直接给颜色」。到了有光照的场景,物体的颜色是怎么来的?
现实里,我们看到的物体颜色,是它没有吸收、反射出来的那部分光。LearnOpenGL 用一个很直接的式子近似这件事:
这里的乘法是逐个通道相乘的。白光照在珊瑚色的物体上,看到的还是珊瑚色。换成绿光,物体只能反射绿色通道,就变成了暗绿色。
也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。
后面讲 Phong 和 PBR 时,「材质的反射率 × 入射光」用的都是这同一种乘法。
在游戏里怎么用
取舍与常见的坑
小结与练习
- 颜色和坐标在 GLSL 里是同一种向量,
.rgba、.xyzw、.stpq只是不同的名字;swizzle 能随手重排分量。 mix(a, b, t)在两种颜色之间插值;t换成vec3,三个通道就能各走各的曲线。- HSB 更适合挑颜色。用
atan和length换到极坐标,就能画出色轮。 - 有光照时,看到的颜色是「光 × 物体」逐通道相乘的结果。
练习:把色轮和第一课的鼠标结合起来:让鼠标的横坐标控制整个色轮的亮度,纵坐标控制旋转速度。
在 GLSL 里颜色与空间坐标共用 vec2/vec3/vec4:可用 .rgba / .xyzw / .stpq 或下标访问,并 swizzle 任意重排。mix 在 上混合;HSB(HSV)比 RGB 更直观,还可映到极坐标色轮。
向量即颜色:访问名与 swizzle
面向对象里像结构体字段那样访问向量:vec3 red; red.x=...。同一分量有多套名字(仅为可读性):
| 下标 | 空间 | 颜色 | 纹理 |
|---|---|---|---|
[0] | .x | .r | .s |
[1] | .y | .g | .t |
[2] | .z | .b | .p |
[3] | .w | .a | .q |
.s/.t/.p 常用于纹理坐标(见纹理章)。属性可按任意顺序组合,称为 swizzle:例如 magenta = yellow.rbg 交换绿蓝;green.rgb = yellow.bgb 用黄的蓝通道填红与蓝。这种「颜色与坐标可互换思考」是着色语言的刻意设计。
mix:按比例混合
mix(a, b, t) 用 在 、 间插值——正好接上塑形函数的篱笆课。可用 |sin(u_time)| 在两种情绪色之间过渡;结合 easing 类塑形函数做「出现/消隐」。
t 也可以是与前两参同型的 vec3:于是 每个 RGB 通道用不同比例 混合,配合三条不同的塑形曲线就能做出 Turner 式晚霞、日出日落动画、彩虹或 step 彩旗。
HSB / HSV
除 RGB 外,HSB(Hue, Saturation, Brightness/Value)更易选色。书中给出 rgb2hsv / hsv2rgb;把 →Hue、→Brightness 可铺满可见光谱。
极坐标 HSB
HSB 本按极坐标(角+半径)设计。对像素相对画布中心:
- 半径:
length()(中心最大约 0.5,常 ×2 归一到 1); - 角:
atan(y,x)(GLSL 的 atan2 形式),返回 ,除以TWO_PI得到 再加 0.5 映到 。
此时把表示颜色的 vec3 当作几何向量完全合法。相关几何函数还有 distance、dot、cross、normalize、faceforward、reflect、refract;关系函数有 lessThan 族与 equal/notEqual。
练习方向:旋转色轮;用塑形函数拉长某一色相、压缩其余;修正与 RYB 拾色器的对立色差异(RGB 色轮上红的对面是青而非绿)。Josef Albers《色彩构成》可作练习语境。
函数参数限定符
示例函数参数前的 in 表示只读;还有 out(只写)与 inout(可读可写,类似引用传递)。
与本库色彩页的分工
- 测量与色度、CIEXYZ、色域:色度学与色彩空间
- 显示 gamma / sRGB 工作流:显示编码与 sRGB
- 本页:BoS 路径下的 GLSL 向量习惯、
mix渐变与 HSB 程序化选色(创造性编码视角,非完整色度学)。
工具箱:LYGIA 的 color 分区(空间转换、混合、渐变等)。
LearnOpenGL 的视角:光照下的物体色 = 分量相乘(Lighting / Colors)
来源:Colors;摘录 Lighting — Colors。程序化选色、HSB、
mix仍见上文 BoS 内容,此处不重复。
数字颜色用 RGB∈[0,1]。物理直觉:物体「颜色」是反射出去、未被吸收的光;白光照射珊瑚物体时,结果近似 lightColor * objectColor(逐分量乘)。换绿光则只剩绿色通道可反射,珊瑚会变成暗绿——这为后续 Phong/PBR 里「材质反射率 × 入射光」打下同一代数。
场景搭法(着色相关部分):容器与灯各用一套着色器;灯的片元着色器输出常数亮色,避免被场景光照公式牵连。物体片元着色器早期版本即 FragColor = vec4(lightColor * objectColor, 1.0)。
更完整的色度与显示编码见 色度学与色彩空间、显示编码与 sRGB;线性工作流在 Gamma/PBR 章展开。