PHASE 2 · 效果工坊

2.2马赛克工坊

像素化、圆点、六边形与乐高积木

这一章做四种马赛克,从五行代码的基础像素化,一路做到带立体光影的乐高积木墙。它们共享同一个核心机关,又各自演示一种「格子内再创作」的手法——学完你不但会做这四种,还能自己发明第五种。

1万法归一:量子化

所有马赛克的本质是同一件事:让一片区域的像素共享同一个颜色来源。翻译成 Shader 语言:把连续的 UV 坐标「四舍五入」到格子中心,再拿这个统一坐标去采样——1.3 章的 floor/fract 兄弟又来了:

GLSL · 马赛克核心机关(就这三行)
float n = 40.0;                              // 横向格子数
vec2 cellUV = (floor(uv * n) + 0.5) / n;     // 本格中心的 UV
vec3 color  = texture(uTex, cellUV).rgb;     // 全格共用中心色 → 马赛克!

拆解:floor(uv * n) 是格子编号(0, 1, 2…),加 0.5 挪到格子正中,再除以 n 变回 UV 空间。整个格子里的像素算出的 cellUV 完全相同,于是取到同一个颜色。连续坐标被「量子化」成了离散台阶——这个机关贯穿全章。

2基础像素化

加上宽高比修正(保证格子是正方形),就是完整的像素化效果。拖动滑块感受颗粒度:

Android 视角 真实用途

这就是聊天 App 里「拍照打码」的实现(隐私遮挡区域内做量子化采样);也是视频 App 弹幕蒙版、直播打码的底层。用 AGSL 写的话,连 uProgress 滑块都能直接接 Compose 的 Slider(3.4 章)。顺带一提:马赛克并不安全,低颗粒度的打码存在被神经网络还原的风险,真要遮敏感信息请用纯色。

3圆点马赛克

手法升级:格子颜色不再涂满,而是画成一个圆点,而且圆点大小跟随亮度变化——亮处大点、暗处小点,远看是照片、近看是波点艺术(印刷网点、LED 大屏都是这个思路):

注意这里的新思想:格子编号管「取什么颜色」,格内坐标管「画什么形状」,两条流水线在最后的 mix 汇合。想换成方块、菱形、五角星?只改「画什么形状」那一半。

4六边形马赛克

蜂窝格比方格「高级感」强得多,但六边形没法用一次 floor 切出来。经典技巧:叠两张错位的矩形网格,每个像素归属于离它较近的那张网格的格心——两套候选格心一比,蜂窝就出现了:

不必一次读懂 hexGrid 的每行——先把它当「六边形版的 floor/fract」用起来,它同样返回「格内坐标 + 格子标识」这对老搭档。想深挖推导,搜 The Art of Code 的 Hexagonal Tiling 视频(4.2 章工具箱有链接)。

5压轴:乐高积木

把本章所有手法堆在一起,再加一味新调料——假光照,马赛克就变成了乐高墙。配方:

  1. 量子化取色(第 1 节)→ 每块积木的颜色;
  2. 格内坐标画圆(第 3 节)→ 积木顶面的圆凸点(stud);
  3. 光照三件套:凸点上半亮、下半暗(假装光从左上来),边缘一圈高光,凸点右下投个影;
  4. 格子四边压暗一圈 → 积木之间的接缝。

值得咀嚼的是第④步:dot(normalize(cell), lightDir)。这其实是 3D 渲染的兰伯特光照(N·L)公式,只是「法线」被我们用格内坐标的方向偷懒近似了——顶点朝上的曲面,边缘的法线确实大致指向外侧。4.1 章游戏 Shader 里的光照,就是这一行的严肃版本。一个平面纹理,靠「亮一边、暗一边、加高光、投个影」四步就骗过了人眼,这套「假光照四件套」值得单独记住,做任何拟物效果都用得上。

再进一步:调色板量化

真乐高只有几十种颜色。想更逼真,把 base 颜色「吸附」到最近的乐高官方色:预置一个 vec3 palette[16] 数组,循环比较 distance(base, palette[i]) 取最近者。这个手法叫色彩量化,顺便还能做出 GameBoy 四色屏、8-bit 复古风——和 2.4 章是近亲。

本章小结

  • 马赛克核心机关:(floor(uv*n)+0.5)/n 把 UV 量子化到格心,全格共色;记得宽高比修正。
  • 双流水线:格子编号管取色,格内坐标管造型,最后 mix 汇合——圆点、方块随便换。
  • 六边形 = 两张错位矩形网格取近者,hexGrid 当「六边形版 fract/floor」用。
  • 乐高 = 量子化 + 格内画凸点 + 假光照四件套(受光面提亮、背光面压暗、边缘高光、错位投影)。
  • dot(N, L) 光照第一次登场,4.1 章将正式展开。

动手练习

  1. 做「十字绣」风格:圆点 Demo 里把圆换成旋转 45° 的细十字(两条 sdSegment),底色换成米色布纹(叠一点 hash 噪声)。
  2. 给乐高 Demo 加「随机高度差」:用 hash21(id) 给每块积木一个 0~1 的高度,高的块整体提亮、影子加长——远看会有浮雕感。
  3. 实现 GameBoy 风:像素化 + 亮度量化到 4 档 + 映射到经典的四种墨绿色(提示:floor(luma * 4.0) / 3.0 再查色)。