游戏是 Shader 的主场:你在任何一款游戏里看到的水面、光影、爆炸、受击闪白,背后都是着色器。这一章不教你做游戏,而是回答一个更实际的问题:你这本书学的东西,在游戏引擎里对应什么、还能直接用吗?答案会让你惊喜——几乎全部通用,连代码都长得差不多。
1游戏里 Shader 都在干什么
游戏引擎里的 Shader 按岗位分四类,前三类你都已经见过它们的「App 版」:
| 岗位 | 干什么 | 本书对应 |
|---|---|---|
| 材质(Surface) | 决定物体表面长什么样:颜色、光照、金属感、卡通描边 | Phase 1 的图形数学 + 本章光照 |
| 后处理(Post) | 整个画面渲染完后再加工:泛光、色调映射、景深、暗角 | Phase 2 全部(输入纹理=游戏画面) |
| 特效(VFX) | 溶解、受击闪白、护盾、传送门,常配合粒子系统 | 造型函数 + 噪声 + 本章溶解 |
| 顶点动画 | 草地摆动、旗帜飘扬、水面起伏——动的是几何体本身 | 唯一的新东西,第 4 节 |
换句话说:游戏后处理 = 你的 Phase 2,输入从照片换成了每帧的游戏画面。CRT、色差、玻璃、马赛克,在游戏里全都有对应的名场面(恐怖游戏的信号故障、水下的折射、像素风滤镜)。
2光照一分钟 + 卡通渲染
2.2 章乐高凸点里偷偷用过的 dot(N, L),是整个实时光照的地基,正式介绍一下:表面朝向光源的程度 = 法线 N 与光方向 L 的点积。正对着光是 1(最亮),侧面是 0,背面为负(黑)。这叫兰伯特(Lambert)漫反射,一行代码撑起了三十年游戏画面。
而「卡通渲染(Toon/Cel Shading)」的秘密更简单:把连续的 N·L 量子化成两三档(1.1 章的 floor / step!),明暗交界立刻变成动画片般的硬边。下面在 2D 里伪造一个 3D 球演示——球的法线可以从圆内坐标直接推出来,这是 Shadertoy 社区的经典白嫖:
就这四个零件——兰伯特、量子化、边缘光、描边——《塞尔达:旷野之息》《原神》风格化渲染的地基就齐了(它们当然还叠了几十层细节,但骨架是这个)。真 3D 里的差别只是:法线不用白嫖,顶点数据自带。
3溶解:游戏特效第一课
怪物死亡化灰、装备升级重构、传送门吞噬——游戏里一切「物体逐渐消失」都是同一个配方,而且你已经有全部原料:噪声(1.5)+ 阈值(step)+ 边缘发光(1.2):
同门师兄弟一并点名,全是几行的变体:受击闪白 = mix(color, vec3(1.0), flash),flash 由受击时刻起指数衰减;护盾 = 半透明球 + rim 发光 + 噪声流动;幽灵化 = 溶解不加火边、alive 换成半透明。游戏特效的门,推开就是这么薄。
4顶点着色器的岗位
本书的 Vertex Shader 一直在摸鱼(原样输出四个顶点)。游戏里它有真正的岗位:移动顶点本身。草会弯、旗会飘、水面有浪,不是美术做了动画,而是 Vertex Shader 每帧根据时间和位置推挤顶点:
// Vertex Shader 里(伪代码,引擎里变量名各异):
vec3 pos = aPosition;
// 离旗杆越远摆得越大;时间 + 位置 = 波浪(全是 1.4 章的思路)
float sway = sin(pos.x * 3.0 + uTime * 4.0) * 0.15 * pos.x;
pos.z += sway;
gl_Position = uProjection * uView * uModel * vec4(pos, 1.0);
看出来了吗?顶点动画就是「造型函数作用在顶点上」——sin 波、噪声、缓动,全套 Phase 1 知识换个作用对象。草地是每根草按世界坐标错开相位(1.4 章的 stagger),水面是 fbm 顶点位移 + 法线重算。规模大了还有 GPU 粒子、蒙皮骨骼,但那是引擎的事,思想不变。
5引擎对照:你的知识怎么迁移
| 引擎 | Shader 语言 | 与本书 GLSL 的关系 | 上手路径 |
|---|---|---|---|
| Unity | HLSL(ShaderLab 包装) | 类型改名:vec3→float3、fract→frac、mix→lerp,其余九成同 | 先玩 Shader Graph(节点版),再写 HLSL |
| Godot | gdshader | 几乎就是 GLSL,入口换成 fragment() + COLOR | 直接写,官方文档一晚上读完 |
| Unreal | 材质节点图(底层 HLSL) | 节点名 = 本书函数名(Lerp、Fresnel、Noise) | 连节点;Custom 节点里可手写 HLSL |
| Web(Three.js) | GLSL | 原生同款,onBeforeCompile / ShaderMaterial 直接贴 | 零迁移 |
看一眼 Godot 版的溶解,感受「知识原样迁移」不是空话:
shader_type canvas_item;
uniform float progress : hint_range(0.0, 1.0);
uniform sampler2D noise_tex; // Godot 内置噪声纹理,连 fbm 都不用写
void fragment() {
float n = texture(noise_tex, UV).r;
float alive = step(progress * 1.2, n);
float edge = smoothstep(progress * 1.2, progress * 1.2 - 0.08, n) * alive;
vec3 fire = mix(vec3(1.0, 0.3, 0.05), vec3(1.0, 0.9, 0.3), edge);
vec4 tex = texture(TEXTURE, UV);
COLOR = vec4(tex.rgb * alive + fire * edge * 1.6, tex.a * max(alive, edge));
}
Freya Holmér 的「Shaders for Game Devs」系列(YouTube,免费,业界公认第一课)→ Catlike Coding 的 Unity 教程(文字,极扎实)→ The Book of Shaders 补数学美感。如果你只是想在 Android 游戏里加特效,Godot + gdshader 是离你现有知识最近的落点。
本章小结
- 游戏 Shader 四岗位:材质、后处理、特效、顶点动画;前三个你已经会了 App 版。
- 光照地基 =
dot(N, L);卡通渲染 = 光照量子化 + 边缘光 + 描边,四个零件全是旧知识。 - 溶解 = 噪声耐烧度 + 进度阈值 + 燃烧边缘;受击闪白、护盾都是它的几行变体。
- 顶点动画 = 造型函数作用在顶点上:旗帜是 sin,草地是相位错开,水面是 fbm。
- 迁移成本:Godot≈0、Unity=改函数名、Unreal=连节点;思维模型全程不变。
动手练习
- 给卡通球加「第二光源」:一个固定的冷色补光(蓝紫),强度是主光的 30%(提示:两个 N·L 各自量子化再相加)。
- 做「受击闪白」:溶解 Demo 里加
uniform float uHit,让画面在 uHit=1 时整体泛白、随后 0.3 秒指数衰减(用编辑器手改数值模拟)。 - 下载 Godot(免费,200MB),建一个 Sprite2D,把本节 gdshader 溶解贴上去跑通——亲手验证一次「知识零成本迁移」。