Toolkitly

如何把图片转成 Game Boy 的四色调色板

四档绿色是还有人认真去做的最小的调色板,也正因为小,所有偷工减料的做法都会露馅。 这一档做对了,色数更多的调色板只会更容易。

1. 那四种颜色

初代 Game Boy 的屏幕只有四档明暗,通常被引用的色值是:

#9BBC0F 最亮
#8BAC0F
#306230
#0F380F 最暗

它们不是加了点绿的中性灰。上面两档偏黄绿,下面两档接近黑色, 所以这套调色板在中间留了一个很大的空档,中高亮度那一段几乎什么都没有。 往它上面转图片时所有难受的地方,都源自这个空档。

硬件本身从来没有存过这几个数字——它驱动的是一块单色 LCD, 那个绿是屏幕的颜色,不是数据的颜色。任何一套十六进制值都是某个人对实物面板的测量, 所以你会找到好几套彼此差几个点的版本。挑一套用到底: 一套素材里混用两套,你得到的是八种颜色,不是四种。

2. 先看清楚你是从多少种颜色开始的

动任何设置之前,先看这张图实际含有多少颜色。在 Aseprite 里画的精灵可能是十一种; 一张截图是几千种;而一张精灵图的 JPEG 也是几千种——虽然看起来像只有十一种, 因为剩下那些是压缩自己造出来的。

这个数字决定了这次转换的工作量有多大。从十一种刻意选定的颜色压到四种, 是一个能预判、能逐个核对的过程。从四万种压到四种则是彻底重画一遍, 下面几个设置就是让这件事还能收场的关键。

如果源文件是 JPEG,每一条硬边附近都会出问题:那些光晕是真实存在的像素、 带着真实的颜色,它们会和别的像素一起被分配到某一档去。有 PNG 就从 PNG 开始。

3. 决定画面还读不读得出来的那个设置

任何转换工具都要对每个像素回答同一个问题:它离四档里的哪一档最近? 最直觉的答案——把红绿蓝当成三条坐标轴、量直线距离——是多数工具的做法, 而在四色调色板上它错得肉眼可见。

sRGB 的通道数值和亮度不成正比。绿色承担了人眼感知亮度的绝大部分,蓝色几乎不承担。 于是像 #005500 这样的中等绿色,数值都不大,就被判成「深色」, 送去了 #0F380F——那个接近黑的档位。可它在任何人眼里都不接近黑。 而一个数值量级相同的中蓝看起来要暗得多,按原始距离算,这两者却被同等对待。

在十六色的调色板上这个问题多半会被稀释掉,因为往哪边偏都有个邻近的颜色接着。 在中间开着大口子的四档上,每一次判断错误都会把像素挪出很远, 本该分开的区域于是糊成一坨。人们说一次转换「变糊了」,指的就是这件事。

解决办法是在感知均匀的颜色空间里量距离——OKLab 是目前合理的默认选择——在那里等量的数值差看起来也是等量的差。 在转换工具里这个开关叫按看起来像; 在同一张图上把它切到按 RGB 数值,差别一点都不含蓄。

4. 抖动:照片要开,精灵图不要

抖动是用两种调色板颜色按花纹交替,伪造出中间色, 于是平滑的渐变变成一片纹理,而不是三条硬邦邦的色带。 在一张日落图上,这就是「看得出是天空」和「看得出是出了错」的区别。

在像素画上它通常是错的。精灵图里每一个像素都是特意放在那里的, 在一片纯色区域里撒上棋盘格,等于用噪点替换掉作者的决定; 而且在精灵图的尺度上,花纹相对于画面的比例比在照片里大得多。

真要抖动,用有序矩阵(4×4 或 8×8),别用 Floyd–Steinberg 那类误差扩散。误差扩散会把每个像素的误差带给邻居, 所以花纹取决于你从哪里开始扫描:抖完之后再把某一帧从图集里裁出来, 它内部的花纹就变了。有序抖动只根据像素自己的坐标决定, 所以一帧无论是单独存在还是躺在图集里,抖出来都一样。

5. 让它一直保持四色

一次确实只产出了四种颜色的转换,进到引擎里可能变成几千种。有三个地方会干这事:

双线性过滤。只要不是 1:1 显示,开着过滤绘制贴图就会混合相邻像素, 而两档颜色混出来的就是第五种颜色。把过滤设成 Nearest——Unity 里是 Point (no filter),Godot 的导入面板里是 Nearest, Phaser 里是 pixelArt: true

块压缩。DXT 和它的亲戚的原理是用一对插值端点去近似 4×4 的色块, 那恰好是固定调色板的反面。像素画把压缩设成 none; 反正四色图片存成 PNG 本来就很小。

导出后再缩放。转换完再用平滑算法放大,中间色立刻就回来了。 要按整数倍、用邻近采样放大——像素画缩放工具就是干这个的——或者干脆交给引擎在绘制时缩放,并把过滤关掉。

常见问题

整张图塌成了两档颜色

图里的中间调全都落到了调色板中间空档的同一侧。先把颜色匹配切到感知模式; 如果还是很平,转换之前先把原图的对比度提上去。

天空变成了三条硬色带

四种颜色对付渐变就是这个结果。把有序抖动打开——它存在就是为了这种情况。

精灵图上全是麻点

抖动开着,而它不该开。有大片纯色区域的画面把抖动关掉。

导出的 PNG 里不止四种颜色

转换之后有东西缩放或过滤过这张图。检查导出尺寸和原图是否一致, 以及导出路径上有没有加平滑。

两份素材的绿色有细微差别

它们是按两套不同的 DMG 调色板转的。把调色板导出成一个 .gpl 或 .hex 文件, 之后所有东西都对着这一个文件转。

现在就转一张图