聊聊像素游戏的调色板

曾经,我的第一台电脑找不到显卡驱动,常年只有16色。

1. 调色板是干什么的

旧社会的时候以前机能不好的时候,同屏颜色数受限,经常只有16色或更少。每个像素显示哪种颜色也不是用RGB来记录的,而是记录一个颜色索引值,到绘制这个像素时用这个索引去寻找真正的颜色(经常仍然是一个索引值,笑)。这个颜色索引表格,就是像素游戏时代的调色板。

基本上,历史上应用调色板的思路我能想到的有四种:

  1. 为像素角色更换皮肤

这个是最容易想到的。比如角色身上的衣服用同一种颜色绘制,改变这个索引的颜色就可以改变衣服的颜色。

除了换衣服之外,策略游戏的团队色也可以用调色板来做。

  1. 突破颜色数限制

《三国志IV》是我非常喜欢的一款游戏。他的很多细节非常值得推敲。比如,这是一款同屏16色的游戏,而武将的头像就用掉了8色。(顺带一提,这8种颜色因为不可能变动,所以也被用在UI上。)留给环境绘制的颜色不多了。然而《三国志IV》还是可以表现从南到北、一年四季的景色变化,它是怎么做的呢?

三国志IV的地图,左侧为从西凉到南蛮的颜色变化,右侧为西凉不同季节的颜色变化

答案就是改变调色板。当我们从北到南卷动地图时,可以发现地图的颜色不是从北到南渐变的,而是随着视角的移动,整体改变颜色。在北方时整体饱和度低,到南方整体饱和度高。

假设地图上没有任何细节,那么只需要消耗一个颜色索引就能实现从北到南的颜色变化。只要平滑地改变索引颜色,就可以使最终的效果非常平滑。当然游戏中实际添加了大量的草、山、水等细节,这些细节的颜色也随着视角移动而改变。这样就看起来更自然了。

那么,同屏还是不超过16种索引颜色,但是在游戏全程能够呈现的颜色远远不止这么点。

老游戏经常用这种方法获得不同风格的场景颜色,或者在转场时做FadeOut。

  1. 调色板动画

把『改变索引颜色』这件事做到极致,变成『时刻不停地改变颜色』,就得到了调色板动画。下面这张图可以很明显地感受到瀑布是由几个固定索引绘制的。

这似乎是SFC的耀西岛

通过改变索引颜色实现动画的最大意义在于——节省资源。只需要一张图就能做动画了。随着存储越来越不值钱,调色板动画逐渐被帧动画或者sprite动画替代了。

  1. 受攻击或者buff特效

例如受到攻击发亮的效果、boss血量低下时闪红的效果、无敌时亮闪闪的效果等等,请回忆一下童年。

2. 调色板在今天

今天,调色板仍然存在。大家在绘制像素画时仍然有意识地控制自己使用的颜色。(毕竟不限制颜色的像素画就不怎么『像素』了。)不过随着显示设备和存储设备的进步,即便是在像素游戏里,在游戏技术层面大家往往也不太关注调色板了。

今天,完美继承了调色板思想的应用是……美图滤镜。(望天

颜色滤镜可以理解为将每一种颜色映射为另一种颜色。效率最好的实现就是将这种映射提前记录下来,存成一张查找表,从而得到了Color Lookup Table。

某个LUT

很明显,颜色本身的RGB就成了256 * 256 * 256的三维索引,这个LUT的图就是调色板。

游戏中也有用LUT的实例,比如这篇文章提到了在Grave Keeper中使用LUT实现不同时间的光照。

2D游戏的光照和颜色是一个大话题,以后可能会说吧……本文的重点不在这里,我们先从最原始的调色板开始讨论。

3. 实现简单的调色板

How to Use a Shader to Dynamically Swap a Sprite's Colors这篇文章给了一个非常简明的思路:用图像的一个通道作为索引(所以是256色的调色板),到一张一维的纹理上查找颜色。

Shader改动前:

fixed4 frag(v2f IN) : SV_Target
{
    fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
    c.rgb *= c.a;
    return c;
}

改动后:

fixed4 frag(v2f IN) : SV_Target
{
    fixed4 c = SampleSpriteTexture (IN.texcoord);
    fixed4 swapCol = tex2D(_SwapTex, float2(c.r, 0));
    fixed4 final = lerp(c, swapCol, swapCol.a) * IN.color;
    final.a = c.a;
    final.rgb *= c.a;
    return final;
}

注意这个实现其实是基于颜色替换,所以调色板的透明度可以决定颜色替换的比例,不过这不是本文重点。我们就当做刚健朴实的调色板来用。

原图、调整颜色的结果、使用的调色板如下:

下方为调色板图

原图只有四种颜色,所以调色板里只有4条线,就对应着那4个颜色。另外为什么调色板背景是灰色的?只是为了debug。

在Unity中的注意事项:

  • 原图和调色板图的Filter Mode都选择Point
  • 原图和调色板图的Compression都选择None

否则像素颜色和位置都是不准确的,不会得到想要的结果。

这个实现是用r通道实现的256色调色板,如果用r、g通道一起做索引,就得到了16位色调色板。远远超过一般像素游戏的需要了。

当然在实际应用中我们不会用左侧那种原图。实际的做法我们放在最后讨论。先在基础静态调色板的基础上看看能做什么。

4. 动态调色板

How to Use a Shader to Dynamically Swap a Sprite's Colors这篇文章里已经给了动态改变调色板的实现了。我懒得画图了,鸽了鸽了。

5. 调色板动画

调色板动画的思路就是随时间改变调色板。在刚才的静态例子中我们只用到了一维的纹理,那么把第二个维度用于时间映射就好了,是不是很简明。

说起调色板动画最简单的就是做个水流或者瀑布了。首先我做了一个大概这样的瀑布效果图:

瀑布效果图,标上了用到的颜色索引。在这个图里我想用16色的调色板做一个瀑布(实际用到了15色)。

接下来生成索引图,也就是用r通道记录颜色索引:

瀑布的索引图

什么都看不出来就对了。因为这张图只用到了16种颜色,所以r通道最大只有15,g、b都是0。

接下来是调色板图。按照设想,颜色应该是循环映射的,这样水才能循环流动起来。最后做出来的调色板图类似这样:

调色版图。放大了。注意这个图里每一行是一帧内的颜色索引。

把二者结合就做出了这样的动画:

动画效果

调色板动画可以节省大量的存储空间,并且可以通过更换调色板改变效果。比如这张瀑布的图可以很容易地通过改颜色改成流下的粘液或者熔岩。

调色板纹理的第二个维度可以干很多事,例如描述同一对象的不同皮肤,或者同一对象在一天内不同时间的颜色等等。

5. 如何愉快绘制

一个现实的问题是,怎么导出索引颜色的原图,以及怎么导出调色板。

我的做法是这样的:

我用的工具是Aseprite,创建一个使用索引颜色的图:

首先新建文件

首先确定一个场景用多少颜色,以瀑布为例是16种颜色,我们就随便建立一个16色的调色板然后开始画:

添加其他细节

这一步能够区分出所画的颜色、便于绘制就可以,颜色不一定十分正确。

然后我们手动建立一个调色板文件。类似下面这样:

GIMP Palette
#
  0   0   0 Untitled
  1   0   0 Untitled
  2   0   0 Untitled
  3   0   0 Untitled
  4   0   0 Untitled
  5   0   0 Untitled
...
254   0   0 Untitled
255   0   0 Untitled

在刚才那种图里载入这个调色板,就会将颜色索引映射到(0, 0, 0)、(1, 0, 0)、(2, 0, 0)……这样的颜色上。这时候再导出就得到了我们想要的索引图。

对!还是这张!

调色板图的制作方法是把这个过程反过来:

  • 先建立一张256x1的图
  • 每个像素依次填入index=0、index=1……的颜色
  • 载入绘制效果图所用的调色板文件(比如刚才的瀑布所用的16色调色板),这样每个索引所在的位置都被对应的颜色着色了
  • 导出

这样导出的是用于一帧的调色板。如果要做多个调色板就反复进行这个步骤,然后把图拼起来。

实际上具体问题具体分析。在做瀑布的调色板动画时我是手动编辑的,因为大体上每帧之间就是颜色循环。用图像编辑软件处理的时候还能统一改改饱和度亮度之类的,更方便一些。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,612评论 4 59
  • 梁素红,焦点网络九期持续分享第250天(380)舞钢 压力产生急躁,这几天都是一天没有规律的吃饭,而且一天吃一顿饭...
    天高地阔心飞扬阅读 115评论 0 0
  • 这几天接到一个新的项目,博**迁移项目,我刚刚开始拿到项目一点头绪没有,傻傻的上去自己安装要用nginx,tomc...
    螃蟹和骆驼先生Yvan阅读 700评论 0 1
  • 1.4 1.2 外中 九下 M 2U 1 M 1U 1 九上 末A M 12U1 M1U2 M3U1 M3U2 M...
    珍珍Pearl阅读 529评论 0 0
  • 下班后陪孩子们学习,基本是我每晚的必修课了。昨天晚上陪孩子们练习拼音时,读到了“约会(yuehui)”。 儿子主动...
    杉清阅读 340评论 2 4