Unite 2018 | 浅谈伽玛和线性颜色空间
什么是伽馬顏色空間
通常物體呈現(xiàn)出來的顏色和我們用眼睛看到屏幕上的最終顏色是不一樣的。
下圖01中有三張圖片。左邊偏亮的才是真實的顏色。這里的真實顏色指的是物體的顏色波長是多長,在光譜中對應的信息是什么樣子的,眼睛對這個顏色的反饋是什么樣子的。實際上我們在屏幕上看到的都是右邊的顏色,顯示器在顯示的時候做了伽馬變換,我們把這個指數(shù)叫做伽馬。
?
中間的圖,當伽馬值是1的時候,和原來的顏色是一樣的,伽馬從1到2.2的時候,這個顏色是逐漸加深的,但是純黑(0,0,0)和純白(1,1,1)是不會變的,只是讓中間顏色的亮度有變化。因為我們從來沒有見過真實的顏色是什么樣的,都是通過屏幕上看到的,所以我們理所應當?shù)卣J為右邊是真實的顏色。
再看一個更形象的情況,圖02中有二張圖片。左邊是我們眼睛看到的或者是照相機看到的,右邊是我們實際在顯示器上看到它是什么樣子的。右邊的圖片其實是原始圖,左邊圖片經(jīng)過了伽馬校正,我們在顯示器里看到的東西比它本身的亮度暗一點,顯示器內(nèi)部有伽瑪變換。
?
那么我們看現(xiàn)在這個圖變暗了,如果想讓它正常一點,為了解決這個問題,我們會怎么辦?我們可以反向做一個伽馬校正,把暗的部分校正到變亮的部分,它們的值是倒數(shù)的關系,完全相同的還原回這個亮度本身的值。
這時又出現(xiàn)了另一個問題。例如:當我們輸出到屏幕的時候已經(jīng)變暗了,這時再做校正,是找不到任何一種方法能在這中間插一個步驟來校正它。我們最后想了一個辦法,如果在輸出的時候沒法把它校正,能不能把之前的顏色提前校正回來?
?
如圖03所示,這是一個對比圖,首先它是完全沒有校正過的,最左側(cè)是它本身的顏色,中間這個是可以拿照相機或者拿攝像機,或者你自己眼睛看到真實的亮度,照相機把真實亮度存下來,不做任何的處理,這是中間圖的樣子,但是經(jīng)過屏幕顯示的時候變暗了。
?
我們應該怎么做才能讓它不變暗呢? 把最左邊原始的亮度存儲的時候,我們提前做一些反向的伽馬校正,把伽馬校正這個階段提前到圖片存儲的時候,這個時候顯示器又要讓這個畫面變暗,一亮一暗抵消了之后就是原來的亮度了,看到所有的圖片也就正常了,不會普遍的偏暗,我們看著比較舒服的樣子。
?
這樣就解決了伽瑪變換產(chǎn)生的問題。示例中伽馬值是2.2,每個顯示器的伽馬值都是可能不一樣的,圖片到底應該用哪個伽馬值做校正?數(shù)據(jù)預先校正完之后保存在圖片中了,無法對所有的顯示器分別做校正。所以人們發(fā)明了Color Profile,可以稱為顏色空間或者色彩空間。
?
顏色空間的概念是很大的,在這里我們狹義地理解它為:它解釋了數(shù)值到波長的對應關系。當以RGB保存圖片的時候,圖06中的的顏色空間說明了RGB數(shù)值如何變換的顏色波長;反過來,顯示器也有Color Profile,它說明了一個特定波長的顏色要怎么樣用它屏幕的RGB亮度表現(xiàn)出來。當顯示一張圖片時,根據(jù)圖片的Color Profile把它的顏色變換到波長,顯示器顯示該波長時,也知道應該如何設置它的RGB亮度來表示這個波長的顏色,這樣一張圖片可以在不同的顯示器上看起來都相同。
Color Profile一般也會定義伽馬值,來定義一個伽馬變換,這個伽馬值是多少,也是包含在這個Color Profile里邊的,通過伽馬值就知道它的變換曲線是什么樣子的。這是從顯示器那一端可以拿到當前的Color Profile,甚至可以指定用哪一種Color Profile,你可以看到選不同配置的時候,桌面或者圖片顯示出來的顏色是不一樣的。
通過這種配置,圖片中的數(shù)據(jù)對應到顏色亮度的變換是可以不一樣的。系統(tǒng)設置里一般會把伽馬值翻譯成響應曲線,你可以看到RGB分別有響應曲線,下面是對應的曲線,這個值不同的時候,這個曲線有變化,和剛才看到的現(xiàn)象是一樣的。
?
sRGB是一種最常用的Color Profile,是由微軟和惠普聯(lián)合制定的一個規(guī)范,它的使用如此廣泛以至于我們經(jīng)常把sRGB等同于了伽馬顏色空間。如果顯示一個顏色,所有的顯示設備都用了sRGB的顏色空間,存儲一個圖片或者保存一個視頻也好或者其它圖片工具也使用sRGB的編碼,因為都在一個顏色空間下,所以輸入和顯示是可以免去轉(zhuǎn)換的。
在不同設備上顯示顏色要把圖片顏色轉(zhuǎn)成波長,再把波長放在顯示器上,根據(jù)顯示器的Color Profile可以得出顯示器應該怎么顯示這個值,才能體現(xiàn)出它的原本顏色,這中間有一個計算過程,如果我們都使用同一種Colo Profile,例如:sRGB,就沒有這個過程了,圖片存的時候就是sRGB的顏色空間,顯示器接收的時候已經(jīng)是同樣的顏色空間了,RGB數(shù)值可以直接輸出,省略了轉(zhuǎn)換的過程。
sRGB也有伽瑪變換,它的伽馬值近似等于2.02。圖07中右邊圖顏色部分解釋了它的伽馬值,伽馬值其實不是一個固定值,而是一條曲線。
下面我們看一下Unity里邊用伽馬顏色空間時到底做什么?如果不做任何的伽馬校正,一般的流程是什么樣子的?
?
如圖08所示,最左邊是貼圖,大家看著可能覺得有點暗,這是正常的,因為顯示端做了伽瑪變換。這張圖在Unity里參與光照計算,計算結(jié)果保存到Frame Buffer,這也是正常的結(jié)果。但是顯示器輸出Frame Buffer的時候,結(jié)果看起來變暗了,我們看到后覺得這不是我們想要的結(jié)果。
如圖09所示,這就是我們提到的保存圖片時的伽瑪校正,可以看到,原始的這張圖經(jīng)過伽馬校正提亮,傳到Unity里面做光照計算。通過Frame Buffer可以看到它的計算結(jié)果是特別亮的,經(jīng)過顯示器的伽馬變換之后,這個亮度被壓了下來,你就會覺得這個效果是正常的。但是這個亮度是讓人覺得正常的,代表它的結(jié)果是正確的嗎?其實不是這樣的,后面我們再來詳談。
?
什么是線性顏色空間
什么是線性顏色空間呢?這里要提到二個底層圖形API:sRGB Frame Buffer和sRGB Sampler。
我們拿剛才的渲染流程對比一下,上面分支的流程里輸出是偏暗的,正常的亮度被顯示器壓低了,我們沒有辦法在顯示器輸出前進行校正。但是sRGB Frame Buffer可以,如果使用sRGB Frame Buffer,它能夠在結(jié)果輸出到顯示器這個階段做sRGB伽瑪校正,如圖10所示,最后的顯示是正常的。
?
sRGB Frame Buffer是由硬件支持的,就像剛才所做的變換用Shader也可以實現(xiàn),sRGB Frame Buffer的轉(zhuǎn)換速度要比它快。安卓手機只有OpenGL ES3.0才可以支持它,所以你會看到開啟線性空間必須指定Graphics API為OpenGL ES3.0。需要要注意的是Alpha值不做任何變換的,它只支持每通道8位的格式。為什么只支持8位格式?我們后面會說到。
?
你目前能用到的大部分作圖軟件都是在sRGB的空間下制作的,軟件內(nèi)的預覽和圖片的保存都經(jīng)過了伽瑪校正。這個在伽瑪顏色空間下沒問題,但是在線性顏色空間里,貼圖的顏色應該是線性的。所以當使用線性空間時,經(jīng)過sRGB校正的貼圖應該被還原。有二種方法,可以把圖退回給美術重做,或者你可以使用sRGB Sampler。
?
sRGB Sampler也是硬件支持的一個特性,如果勾選sRGB選項,買二手手游賬號硬件會認為圖是sRGB編碼的,在采樣顏色的時候會先做一次sRGB反向轉(zhuǎn)換,再把結(jié)果返回,所以Shader讀到的顏色是校正之前的顏色。
如果貼圖是線性存儲的,則不需要勾選sRGB選項,采樣得到的顏色值就是直接從貼圖中取得。
?
為什么要用伽馬顏色空間?
那么為什么我們之前要用伽馬顏色空間?
有一個歷史原因,以前的顯示器是模擬信號,電子打到顯示屏上,就能看到像素發(fā)光。它的電壓和亮度并不是線性的增長關系,而是近似于調(diào)伽馬曲線,所以以前的顯示器自帶伽馬變換的。
另一個重要的原因和精度有關,就像我們剛才說,RGB 8位的貼圖才有sRGB格式,sRGB格式的精度會更高。
?
那么同樣是8位的表示方式,為什么精度會有區(qū)別呢?這和人眼有關系,并不是說絕對精度會變高,而是說人眼對不同亮度的區(qū)域的反應是不一樣的。
例如:圖15中的漸變圖是經(jīng)過伽馬變換的,下邊那一個是沒有經(jīng)過任何校正的,也就是說下面這個圖從左到右亮度是均勻變化的。你會發(fā)現(xiàn)下面這張圖右邊基本上是純白,但是它其實是有均勻變化的,這說明我們?nèi)搜蹖α敛康淖R別特別差,對暗部的識別高一些。
假如說對右邊這部分區(qū)域也用8位做編碼的話,亮度值的漸變對人眼來說是看不到任何變化的,人眼識別不了那么清晰的漸變過程,位數(shù)少一些沒有關系。對暗度反而有比較高的敏感度,需要更多的位數(shù)。這就是為什么說sRGB精度高,是因為我們可以從圖像中看到更多信息。
精度如何變高的呢?如圖16所示,這是經(jīng)過伽馬校正以后的曲線,縱軸是sRGB格式的數(shù)據(jù),橫軸是原始數(shù)據(jù)。原始值和編碼后的值基本是一樣的,原始數(shù)據(jù)較小的區(qū)間,編碼后的閾值變得比較大。
?
這是我們期望的,同樣跨度的編碼值可以表示更細微的過渡。這就是為什么要用伽馬顏色空間非常重要的原因,我們制作貼圖時對精度就是有要求的。
那么線性空間的好處在哪里?是因為光照計算會出錯,在伽馬顏色空間下做光照計算分兩步。舉個簡單乘法的例子,第一步:Shader計算結(jié)果,計算方式為貼圖顏色×光照系數(shù),貼圖顏色是經(jīng)過伽瑪校正的,所以它要帶著冪運算。第二步:輸出計算結(jié)果到屏幕,屏幕顏色等于是輸出的顏色。
?
我們想要的正確的結(jié)果是什么樣子?我們并不是要在貼圖顏色上做校正,貼圖顏色就是直接乘以光照系數(shù)得到輸出的顏色,只有在輸出到屏幕的時候,把輸出顏色做伽馬校正。
?
這二種方式比較相似,做一個對比,你就能看到,左邊是做貼圖上的伽馬校正,并且在伽馬顏色空間下的計算結(jié)果,右邊是正確的計算結(jié)果,二者是不一樣的。線性顏色空間的計算是和正確的計算方法一致的。
?
圖20中的這幾張圖片更能說出問題。貼圖一般都不是灰度圖,RGB通道間的差異是不一樣的,比如任的皮膚貼圖,紅通道的值會高于其它二個通道。經(jīng)過伽馬校正之后,紅通道和其他通道的差異就會被放大,用它做光照計算,會發(fā)現(xiàn)紅通道會提升得異常的高。
?
二張圖中左邊是線性正確值,右邊的曝光結(jié)果不對,因為這種色溫比較溫和的顏色,會迅速的曝光,存的這個值在經(jīng)過sRGB變換了之后已經(jīng)比原來的值大了。注意右邊上眼皮輪廓應用的地方,你會發(fā)現(xiàn)有一些黑、有一些藍色,也是一樣的問題,當你做光照計算的時候,冷色調(diào)和暖色調(diào)的差別已經(jīng)拉開了,把光照系數(shù)加上去,差異變得更大,這就是你發(fā)現(xiàn)結(jié)果不對的原因。
選擇顏色空間
你剛才看到只有在光照計算的時候,伽馬空間的弊端會暴露的特別明顯,什么時候分別采用合適的顏色空間呢?
在光照效果來說,只有要求真實光照的話,才要考慮線性空間,真實光照并不是說看起來好,而是說數(shù)值上就是正確的。要求數(shù)值正確是指要求在不同的光照環(huán)境下光照結(jié)果都正常。
?
如果要求的是物理寫實的效果,才只能考慮使用線性空間。其實在其他的情況下,二種方式都是可以的,用伽馬空間也是可以的。
?
2D游戲推薦使用伽馬空間,這不是Unity的考慮,而是美術工作流的考慮,需要花很長時間培訓美術,轉(zhuǎn)到線性空間做圖方式做圖,這是成本比較高的,所以推薦伽馬空間。3D游戲推薦使用線性空間,尤其使用PBR時線性空間是前提,伽馬空間是做不了的。
?
常見問題
下面是我們收集反饋整理的一些常見問題。
Substance Painter的效果和Unity中不一樣
首先這是肯定的,Substance Painter中的光源信息是從環(huán)境貼圖中得到的,Unity沒辦法這么做,因為環(huán)境貼圖中的光源在Unity中不夠強。一些精度的處理SP也做得更好,因為它對性能的要求不苛刻。我們只能做到盡量讓二者效果接近,而且現(xiàn)在的效果也確實很接近了。
在Unity中一定要用線性空間,Substance Painter中導出的貼圖都是經(jīng)過sRGB編碼的,所以每張圖都要勾選sRGB選項。Unity中的環(huán)境貼圖也非常重要,可以把SP的環(huán)境貼圖導入到Unity中使用,Substance從環(huán)境貼圖里面拿到的主光源是特別強的,在Unity里面沒有那么多,所以Unity中的主光源是用來模擬環(huán)境貼圖上面的那個光源方向,你需要調(diào)整到光源方向一致。然后調(diào)整環(huán)境光的強度到合適的值。
?
圖25是Substance Painter和Unity中制作流程的說明,我們可以看一下,當你在Substance設置一個顏色,比如說(0.5、0.5、0.5),如果仔細觀察調(diào)色板,我們會發(fā)現(xiàn)它并不在調(diào)色板正中間,而是偏上的位置,是因為你看到這個預覽顏色的時候,它已經(jīng)經(jīng)過sRGB編碼了。當Substance的貼圖導出時,線性的顏色值經(jīng)過伽馬變換,顏色被提亮了,所以你需要在Unity中勾選sRGB選項,讓它在采樣時能還原回線性值。
?
PhotoShop中導出的圖片應該如何設置
PhotoShop導出的圖片應該怎么設置?PhotoShop對顏色管理特別精確。在Photoshop看到的顏色,比Unity里面看到的顏色更亮,因為Unity里看到的顏色要經(jīng)過顯示器的伽瑪變換,而PhotoShop不會。PhotoShop會讀取顯示器的Color Profile,反向補償回去,也就是說,在Photoshop里邊如果輸?shù)?28,看到的就是128。
然而實際上經(jīng)常不是這樣的,因為PhotoShop有第二個Color Profile,叫做Document Color Profile。通常它的默認值就是sRGB Color Profile,和顯示器的Coor Profile一致,顏色是被這個Color Profile壓暗了,所以PhotoShop中看到的結(jié)果才和Unity中一樣。
?
如果使用線性空間,一般來說Photoshop可以什么都不改,導出的貼圖只要勾上sRGB就可以了。你也可以調(diào)整PhotoShop的伽瑪值為1,導出的貼圖在Unity中也不需要勾選sRGB了。二種流程都可以,因為我們要求它的預覽效果和實際效果是一樣的,所以只要保證Photoshop Unity兩邊的變換結(jié)果是一致的就可以。
?
線性空間中的半透明圖片怎么制作
線性空間中的半透明圖片應該制作呢?我們需要知道Photoshop的圖層和圖層之間做混合的時候,每個上層圖層都經(jīng)過了伽馬變換,然后才做了混合,這個方式是設置中的默認值。
?
你需要在設置中更改它,選擇“用灰度系數(shù)混合RGB顏色”,參數(shù)設置為1,這樣圖層直接就是直接混合的結(jié)果。但是如果你修改了這個選項,你可能只有選擇上面的Linear流程制作貼圖了。
?
記得我們之前提到的,sRGB編碼是為了增加8位顏色的精度,如果你是用了32位浮點數(shù)的貼圖格式,PhotoShop自動使用的是線性空間,沒有做任何伽瑪變換的。
?
伽瑪空間下Lightmap和實時光照效果不一樣
這個是一個已知問題,暫時還沒有明確的修復時間,對于這個問題可能大家只能不斷地烘焙然后調(diào)整結(jié)果了。
線性空間下截圖和游戲畫面有差異
這是因為ReadPixels()這樣的函數(shù)是通過CPU端讀取Frame Buffer的,這種方式返回的顏色是經(jīng)過sRGB編碼的數(shù)據(jù)被直接返回,而沒有還原到線性值。所以這一點要注意,當你得到返回顏色之后,保存貼圖要以線性方式保存,但是目前默認的保存方式是又經(jīng)過一次伽瑪校正的。
?
HDR和線性空間同時開啟的結(jié)果
因為像我們之前提到的,32位浮點格式的Frame Buffer是沒有sRGB擴展的。這一點Unity已經(jīng)為你做好了處理,浮點格式的Frame Buffer在輸出前會先繪制在8位的sRGB Frame Buffer上,然后輸出到屏幕,伽瑪校正在這個階段自動完成。
?
小結(jié)
關于伽瑪和線性顏色空間就為大家介紹到這里,更多Unite Beijing 2018技術演講內(nèi)容的分享盡在Unity官方中文技術論壇(UnityChina.cn) !
總結(jié)
以上是生活随笔為你收集整理的Unite 2018 | 浅谈伽玛和线性颜色空间的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity VS Unreal,游戏开发
- 下一篇: Unity Shader着色器优化