第 2 课:GLSL 基础
用少数函数控制大量像素
常见界面底纹、进度条、遮罩和渐变,本质上都是“给定一个位置,求出一个颜色”。本课用覆盖画布的平面,把位置表示为 vUv,从固定颜色逐步做出渐变、边界、光晕和重复条纹。练习入口在 http://localhost:1000/02-glsl-basics/;先从 doc/shader-tutorial/exercises/ 启动 http-server -p 1000。
vec 与 GLSL 类型
float 是标量;vec2、vec3、vec4 分别包含 2、3、4 个浮点分量。片元颜色通常先用 vec3 color 保存 RGB,再用 vec4(color, 1.0) 加上 alpha。vec3(0.2, 0.5, 0.8) 是暗红、适中绿、较强蓝的颜色,适合固定颜色练习。
color.r、color.g 与 color.b 读取颜色分量;color.x、color.y、color.z 是同一三个分量的坐标别名。swizzle 可按任意顺序选取并重组,例如 red.grb 把 vec3(1,0,0) 变为 vec3(0,1,0)。重排只改变取值,不会修改原变量。实际项目里它常用于从 RGBA 纹理中抽通道、把 XY 向量倒序或快速构造新的颜色。
GLSL 对类型比 JS 更严格:浮点值写 1.0,整数写 1,向量和标量可参与某些逐分量运算,但不能假设任意类型都能自动转换。编译报错时先检查函数参数类型、分号和变量声明。
颜色插值与边界函数
mix(a,b,t) 计算 (1-t)*a + t*b。当 t=vUv.x 时,画布左端 t≈0 取 a,右端 t≈1 取 b,于是得到黑白水平渐变。mix 并不限制 t;若它超出 0–1 会外推,可能产生负颜色或超过 1 的颜色。在需要稳定范围的 UI 遮罩中,应先用 clamp(t,0.0,1.0)。
红→黄→绿可拆成两段:前半段把 vUv.x*2.0 映射到 0–1,后半段把 (vUv.x-0.5)*2.0 映射到 0–1。两段各做一次 mix,再按 0.5 的边界选择。边界处两段都恰好是黄色,所以没有跳色。若把边界改成 0.3,两段的重新映射也要相应改;只改 step 的阈值会出现不连续。
step(edge,x) 在 x < edge 时返回 0,否则返回 1,适合二值遮罩;边缘必然是硬的。smoothstep(edge0,edge1,x) 在两个边界之间给平滑的 0–1 值,适合可调柔边。smoothstep 的两个边界必须满足 edge0 < edge1。对于真正跨分辨率稳定的抗锯齿,第 3 课先理解坐标尺度,后续形状课再引入基于 fwidth 的像素宽度。
clamp(x,lo,hi) 把值限制在区间内。光晕练习用 1.0 - abs(vUv.x-0.5)*2.0 得到中心 1、边缘 0,再用 clamp 保证合法颜色。它是水平亮带;若想让亮度向所有方向衰减,就要用到第 3 课的 length。
Fract、周期与动画
fract(x) 只保留小数部分,输入每跨过一个整数就回到 0。fract(vUv.x*5.0) 让画布产生五个周期;每个周期再用 step(0.5,phase) 分成一黑一白两段,于是总共十条等宽竖纹。公式中的 5 是周期数,不是条纹总数。
滚动条纹在相位中减去 uTime*0.35。若相位要保持固定,随着时间增加,vUv.x 必须增加,因此条纹视觉上向右移动。改成加号就向左。fract 的接缝处通常从 1 跳回 0;红白交替条纹本来就有硬边,因此接缝属于预期边界。如果要连续的平滑纹理,就不能直接把跳变相位当颜色。
数据流与取舍
这些练习里 JS 创建全屏平面。每顶点的 uv attribute 经顶点着色器成为 vUv,GPU 对三角形内部做插值;片元着色器据此计算颜色。只有最后一题还由 JS 每帧更新 uTime。所有颜色计算均在片元阶段,因此会随着像素数增长而增加 GPU 工作量。简单 CSS 渐变和纯色 UI 往往更省开发成本;当图案需要与 3D 几何、材质参数或后续 Shader 效果组合时,再使用 GLSL。
练习修改后若画面全黑,检查 vec3/float 的类型和颜色是否被算为 0;若条纹数量不对,把 fract 的周期数乘以每周期的颜色段数;若动画速度异常,确认 uTime 以秒而不是毫秒传入。页面资源由公共运行时释放,题目代码不在渲染循环中创建新材质。
练习
| 难度 | 题目 | 可见验收 |
|---|---|---|
| ⭐ | 固定 vec3 蓝色 | 固定蓝色 |
| ⭐ | Swizzle 把红色改为绿色 | 绿色 |
| ⭐⭐ | Mix 黑白水平渐变 | 左黑右白 |
| ⭐⭐ | 两次 Mix:红黄绿 | 左红中黄右绿 |
| ⭐⭐ | Step 硬边分界 | 中央锐利黑白分界 |
| ⭐⭐⭐ | Smoothstep 柔和分界 | 中间灰色过渡带 |
| ⭐⭐⭐ | Clamp 中央光晕 | 中央亮、两侧暗 |
| ⭐⭐⭐⭐ | Step + Fract 十条竖纹 | 五白五黑等宽竖纹 |
| ⭐⭐⭐⭐ | 红白相间滚动条纹 | 向右无缝滚动 |