琉煌·庵

特效设计师 · 技术美术

返回作品集

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。

在六色调色板下,色彩偏淡的场景会变得一片灰,所以在调色板处理之前,我用一个小着色器先提高场景的饱和度。

四张抖动色图:3DS 与 DSi 调色板,分别按 CIELAB 距离和 RGB 距离匹配

描边

只在彩色图像上做边缘检测,会漏掉两个同色物体之间的边界,还会给阴影描边。所以 Sobel 滤波改在深度法线纹理上进行,也就是场景的法线乘以深度。

滤波器分别测量每个颜色通道的变化并取最大值。这比只用亮度多一点开销,但能找出亮度相近的颜色之间的边缘。

因为 Flipnote 的线条是草稿感的,而不是干净利落的,所以我再用伪随机偏移轻微挪动每个描边采样。最后把描边合成到抖动后的颜色之上。

想改进的地方

制作演示视频时,我注意到了掉帧。每个像素先采样查找纹理,再采样抖动调色板;把两者合并可以省掉一次采样调用,噪声函数也可以更轻量。

致谢

测试场景来自 JustCreate、karboosx 和 Polygonal Mind 在 Unity Asset Store 上的资源包。Flipnote 青蛙精灵图由 thatmusicweeb 绘制。Acerola 和 Freya Holmér 的视频,以及 Ronja 的教程,是我入门着色器的起点。