曾經(jīng),我的第一臺(tái)電腦找不到顯卡驅(qū)動(dòng),常年只有16色。
1. 調(diào)色板是干什么的
舊社會(huì)的時(shí)候以前機(jī)能不好的時(shí)候,同屏顏色數(shù)受限,經(jīng)常只有16色或更少。每個(gè)像素顯示哪種顏色也不是用RGB來記錄的,而是記錄一個(gè)顏色索引值,到繪制這個(gè)像素時(shí)用這個(gè)索引去尋找真正的顏色(經(jīng)常仍然是一個(gè)索引值,笑)。這個(gè)顏色索引表格,就是像素游戲時(shí)代的調(diào)色板。
基本上,歷史上應(yīng)用調(diào)色板的思路我能想到的有四種:
- 為像素角色更換皮膚
這個(gè)是最容易想到的。比如角色身上的衣服用同一種顏色繪制,改變這個(gè)索引的顏色就可以改變衣服的顏色。
除了換衣服之外,策略游戲的團(tuán)隊(duì)色也可以用調(diào)色板來做。
- 突破顏色數(shù)限制
《三國志IV》是我非常喜歡的一款游戲。他的很多細(xì)節(jié)非常值得推敲。比如,這是一款同屏16色的游戲,而武將的頭像就用掉了8色。(順帶一提,這8種顏色因?yàn)椴豢赡茏儎?dòng),所以也被用在UI上。)留給環(huán)境繪制的顏色不多了。然而《三國志IV》還是可以表現(xiàn)從南到北、一年四季的景色變化,它是怎么做的呢?

答案就是改變調(diào)色板。當(dāng)我們從北到南卷動(dòng)地圖時(shí),可以發(fā)現(xiàn)地圖的顏色不是從北到南漸變的,而是隨著視角的移動(dòng),整體改變顏色。在北方時(shí)整體飽和度低,到南方整體飽和度高。
假設(shè)地圖上沒有任何細(xì)節(jié),那么只需要消耗一個(gè)顏色索引就能實(shí)現(xiàn)從北到南的顏色變化。只要平滑地改變索引顏色,就可以使最終的效果非常平滑。當(dāng)然游戲中實(shí)際添加了大量的草、山、水等細(xì)節(jié),這些細(xì)節(jié)的顏色也隨著視角移動(dòng)而改變。這樣就看起來更自然了。
那么,同屏還是不超過16種索引顏色,但是在游戲全程能夠呈現(xiàn)的顏色遠(yuǎn)遠(yuǎn)不止這么點(diǎn)。
老游戲經(jīng)常用這種方法獲得不同風(fēng)格的場景顏色,或者在轉(zhuǎn)場時(shí)做FadeOut。
- 調(diào)色板動(dòng)畫
把『改變索引顏色』這件事做到極致,變成『時(shí)刻不停地改變顏色』,就得到了調(diào)色板動(dòng)畫。下面這張圖可以很明顯地感受到瀑布是由幾個(gè)固定索引繪制的。

通過改變索引顏色實(shí)現(xiàn)動(dòng)畫的最大意義在于——節(jié)省資源。只需要一張圖就能做動(dòng)畫了。隨著存儲(chǔ)越來越不值錢,調(diào)色板動(dòng)畫逐漸被幀動(dòng)畫或者sprite動(dòng)畫替代了。
- 受攻擊或者buff特效
例如受到攻擊發(fā)亮的效果、boss血量低下時(shí)閃紅的效果、無敵時(shí)亮閃閃的效果等等,請回憶一下童年。
2. 調(diào)色板在今天
今天,調(diào)色板仍然存在。大家在繪制像素畫時(shí)仍然有意識(shí)地控制自己使用的顏色。(畢竟不限制顏色的像素畫就不怎么『像素』了。)不過隨著顯示設(shè)備和存儲(chǔ)設(shè)備的進(jìn)步,即便是在像素游戲里,在游戲技術(shù)層面大家往往也不太關(guān)注調(diào)色板了。
今天,完美繼承了調(diào)色板思想的應(yīng)用是……美圖濾鏡。(望天
顏色濾鏡可以理解為將每一種顏色映射為另一種顏色。效率最好的實(shí)現(xiàn)就是將這種映射提前記錄下來,存成一張查找表,從而得到了Color Lookup Table。

很明顯,顏色本身的RGB就成了256 * 256 * 256的三維索引,這個(gè)LUT的圖就是調(diào)色板。
游戲中也有用LUT的實(shí)例,比如這篇文章提到了在Grave Keeper中使用LUT實(shí)現(xiàn)不同時(shí)間的光照。
2D游戲的光照和顏色是一個(gè)大話題,以后可能會(huì)說吧……本文的重點(diǎn)不在這里,我們先從最原始的調(diào)色板開始討論。
3. 實(shí)現(xiàn)簡單的調(diào)色板
How to Use a Shader to Dynamically Swap a Sprite's Colors這篇文章給了一個(gè)非常簡明的思路:用圖像的一個(gè)通道作為索引(所以是256色的調(diào)色板),到一張一維的紋理上查找顏色。
Shader改動(dòng)前:
fixed4 frag(v2f IN) : SV_Target
{
fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
c.rgb *= c.a;
return c;
}
改動(dòng)后:
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;
}
注意這個(gè)實(shí)現(xiàn)其實(shí)是基于顏色替換,所以調(diào)色板的透明度可以決定顏色替換的比例,不過這不是本文重點(diǎn)。我們就當(dāng)做剛健樸實(shí)的調(diào)色板來用。
原圖、調(diào)整顏色的結(jié)果、使用的調(diào)色板如下:

原圖只有四種顏色,所以調(diào)色板里只有4條線,就對應(yīng)著那4個(gè)顏色。另外為什么調(diào)色板背景是灰色的?只是為了debug。
在Unity中的注意事項(xiàng):
- 原圖和調(diào)色板圖的Filter Mode都選擇Point
- 原圖和調(diào)色板圖的Compression都選擇None
否則像素顏色和位置都是不準(zhǔn)確的,不會(huì)得到想要的結(jié)果。
這個(gè)實(shí)現(xiàn)是用r通道實(shí)現(xiàn)的256色調(diào)色板,如果用r、g通道一起做索引,就得到了16位色調(diào)色板。遠(yuǎn)遠(yuǎn)超過一般像素游戲的需要了。
當(dāng)然在實(shí)際應(yīng)用中我們不會(huì)用左側(cè)那種原圖。實(shí)際的做法我們放在最后討論。先在基礎(chǔ)靜態(tài)調(diào)色板的基礎(chǔ)上看看能做什么。
4. 動(dòng)態(tài)調(diào)色板
How to Use a Shader to Dynamically Swap a Sprite's Colors這篇文章里已經(jīng)給了動(dòng)態(tài)改變調(diào)色板的實(shí)現(xiàn)了。我懶得畫圖了,鴿了鴿了。
5. 調(diào)色板動(dòng)畫
調(diào)色板動(dòng)畫的思路就是隨時(shí)間改變調(diào)色板。在剛才的靜態(tài)例子中我們只用到了一維的紋理,那么把第二個(gè)維度用于時(shí)間映射就好了,是不是很簡明。
說起調(diào)色板動(dòng)畫最簡單的就是做個(gè)水流或者瀑布了。首先我做了一個(gè)大概這樣的瀑布效果圖:

接下來生成索引圖,也就是用r通道記錄顏色索引:

什么都看不出來就對了。因?yàn)檫@張圖只用到了16種顏色,所以r通道最大只有15,g、b都是0。
接下來是調(diào)色板圖。按照設(shè)想,顏色應(yīng)該是循環(huán)映射的,這樣水才能循環(huán)流動(dòng)起來。最后做出來的調(diào)色板圖類似這樣:

把二者結(jié)合就做出了這樣的動(dòng)畫:

調(diào)色板動(dòng)畫可以節(jié)省大量的存儲(chǔ)空間,并且可以通過更換調(diào)色板改變效果。比如這張瀑布的圖可以很容易地通過改顏色改成流下的粘液或者熔巖。
調(diào)色板紋理的第二個(gè)維度可以干很多事,例如描述同一對象的不同皮膚,或者同一對象在一天內(nèi)不同時(shí)間的顏色等等。
5. 如何愉快繪制
一個(gè)現(xiàn)實(shí)的問題是,怎么導(dǎo)出索引顏色的原圖,以及怎么導(dǎo)出調(diào)色板。
我的做法是這樣的:
我用的工具是Aseprite,創(chuàng)建一個(gè)使用索引顏色的圖:

首先確定一個(gè)場景用多少顏色,以瀑布為例是16種顏色,我們就隨便建立一個(gè)16色的調(diào)色板然后開始畫:

這一步能夠區(qū)分出所畫的顏色、便于繪制就可以,顏色不一定十分正確。
然后我們手動(dòng)建立一個(gè)調(diào)色板文件。類似下面這樣:
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
在剛才那種圖里載入這個(gè)調(diào)色板,就會(huì)將顏色索引映射到(0, 0, 0)、(1, 0, 0)、(2, 0, 0)……這樣的顏色上。這時(shí)候再導(dǎo)出就得到了我們想要的索引圖。

調(diào)色板圖的制作方法是把這個(gè)過程反過來:
- 先建立一張256x1的圖
- 每個(gè)像素依次填入index=0、index=1……的顏色
- 載入繪制效果圖所用的調(diào)色板文件(比如剛才的瀑布所用的16色調(diào)色板),這樣每個(gè)索引所在的位置都被對應(yīng)的顏色著色了
- 導(dǎo)出
這樣導(dǎo)出的是用于一幀的調(diào)色板。如果要做多個(gè)調(diào)色板就反復(fù)進(jìn)行這個(gè)步驟,然后把圖拼起來。
實(shí)際上具體問題具體分析。在做瀑布的調(diào)色板動(dòng)畫時(shí)我是手動(dòng)編輯的,因?yàn)榇篌w上每幀之間就是顏色循環(huán)。用圖像編輯軟件處理的時(shí)候還能統(tǒng)一改改飽和度亮度之類的,更方便一些。