Flipnote 着色器
一款 Unity 后处理效果,让 3D 场景看起来像是在 Flipnote Studio(任天堂 DSi 与 3DS 上的动画软件)里画的:画面通过抖动压缩到 Flipnote 的六种颜色,并带有草稿感的描边。我独自用 16 天完成了它,期间每天更新技术美术日志;着色器用 HLSL 编写,颜色表则用 Python 生成。
- 颜色:3D 查找纹理把任意 RGB 颜色映射到 231 种抖动色阶中最接近的一种,再用 8×8 拜耳图案绘制到屏幕上。
- 描边:对场景的深度与法线做 Sobel 滤波,再加入噪声,让线条看起来像手绘。
- 可调参数:饱和度、调色板与描边细节。
- 使用方法:一个 .unitypackage,作为后处理效果拖到任意 URP 相机上即可。
UnityHLSLURPC#Python
颜色
3DS 版 Flipnote 用黑、白、红、蓝、黄、绿六种颜色作画,DSi 版则只有四种。我用 Python 脚本把其中每两种颜色以 8×8 拜耳矩阵按 17 个级别混合,得到 231 种色阶,并存为抖动调色板。
接下来,着色器需要为屏幕上的任意颜色找到最接近的色阶。逐像素搜索调色板太慢,所以第二个脚本会针对整个 RGB 立方体预先算好结果,存成 3D 查找纹理(LUT),再由一个 C# 编辑器脚本转换为 Unity 的 Texture3D。它的红、绿通道存放着对应色阶在抖动调色板中的位置。着色器读取这个位置,再用像素的屏幕坐标(对 8 取模)选出图案中正确的纹素。
“最接近”取决于怎么度量。我比较了 RGB、HSV 和 CIELAB 三种距离。HSV 的效果有点怪,给朋友们看后,他们更喜欢 RGB 的色图,所以着色器最终用的是 RGB。
在六色调色板下,色彩偏淡的场景会变得一片灰,所以在调色板处理之前,我用一个小着色器先提高场景的饱和度。

描边
只在彩色图像上做边缘检测,会漏掉两个同色物体之间的边界,还会给阴影描边。所以 Sobel 滤波改在深度法线纹理上进行,也就是场景的法线乘以深度。
滤波器分别测量每个颜色通道的变化并取最大值。这比只用亮度多一点开销,但能找出亮度相近的颜色之间的边缘。
因为 Flipnote 的线条是草稿感的,而不是干净利落的,所以我再用伪随机偏移轻微挪动每个描边采样。最后把描边合成到抖动后的颜色之上。
想改进的地方
制作演示视频时,我注意到了掉帧。每个像素先采样查找纹理,再采样抖动调色板;把两者合并可以省掉一次采样调用,噪声函数也可以更轻量。
致谢
测试场景来自 JustCreate、karboosx 和 Polygonal Mind 在 Unity Asset Store 上的资源包。Flipnote 青蛙精灵图由 thatmusicweeb 绘制。Acerola 和 Freya Holmér 的视频,以及 Ronja 的教程,是我入门着色器的起点。