1. 程式人生 > >一定要線上性空間(Linear Space)中做光照計算

一定要線上性空間(Linear Space)中做光照計算

啥是Gamma Correction?什麼是線上性空間(Linear Space)中做光照計算? 線性空間這玩意已經說爛了,不過再爛也不是生而知之,該學還得學,這裡總結一下。 先介紹下啥是Gamma(別急,先看下去,這是解釋線性空間的前置知識)。這詞兒N多人聽說過,而且也被各種濫用。這裡只解釋遊戲即時渲染相關的概念。首先,就是老式CRT顯示器的一個問題,給顯示器輸入的電壓和輸出的亮度不成線性關係,啥意思呢,就是你告訴顯示器我這個畫素顏色是0.5,顯示器給個亮度,然後你說下個畫素顏色是1.0,然後顯示器給的亮度不是上一個亮度的兩倍,而是成下面這個函式的關係: Y = pow(X, Gamma) 也就是輸出是輸入的Gamma次方。而一般來說這個Gamma值是2.2(具體值根據各種標準不太一樣,這裡統一認為是2.2)
紅色的曲線就是Y = pow(X, 2.2)的曲線,可以看出,顯示器輸出的圖片是會比想象中的暗的。那麼咋辦呢,各種圖片,視訊等其實在進行資料儲存的時候,都會做Gamma Encode(也會被稱為Gamma Correction,這個詞可能會被用到各種地方,包括Gamma Decode也會被稱為Gamma Correction,為了防止混亂,這裡就不這麼叫了乾脆)。舉個例子,照相的時候,你相機照下照片後是會做一系列後處理,然後再存到SD卡里的,其中有一項後處理就是Gamma Encode,也就是將整體畫面“調亮”,然後顯示器再顯示的時候,再做一次Gamma Decode,這樣顯示的畫面就Gamma抵消了,亮度剛好OK。(你應該注意到高階相機都會可能儲存raw這個格式的圖片,超級大,這種圖片是沒有做Gamma Encode的,就是純記錄下來的原始資料。那麼顯示為啥還正確呢,是因為大部分能開啟raw檔案的軟體,都知道你沒做Gamma Encode,所以軟體會在顯示圖片前給Encode一下)。雖然現代的LCD顯示器已經沒有自帶的Gamma Decode“功能”了,但是為了相容以前的顯示器,還是會調一個Gamme Decode出來。 除此以外,各種圖片視訊會做Gamma Encode的原因還有一個,就是人眼識別光線亮暗的敏感度是不一樣的,人眼在看暗處的時候會比看亮的更加敏感。而由於圖片一般使用8bit每color channel,也就是所謂的24位真彩色,這個儲存資料如果平均分配亮暗程度的話,那麼給暗處的資料量會不夠,人眼會看出明顯“色帶”問題,而如果做了Gamma Encode的話,剛剛好(這可真是湊巧)是把暗處資訊儲存更多,而且儲存的比例也剛好差不多符合人眼對於亮暗敏感程度的比例,這種儲存的影象空間就是耳熟能詳的sRGB影象空間。 下圖就是如果用Linear Encode的話,顯示是什麼樣子的,為了更加顯眼,用的是32階的5bit儲存。注意看暗部細節,明顯不夠用,而Gamma Encode則是亮暗部很均衡。

Linear Encode in 5 bits
Gamma Encode in 5 bits 說了半天都是在講Gamma相關的東西,現在終於說到和遊戲相關的了,其實可能已經猜到了,傳統渲染流程中根本就沒有處理Gamma這個東西,是下面這個流程:
這個流程是錯的,因為直接使用了Encoded Gamma圖去做光照計算,也就是說,使用了非線性的輸入來進行線性計算(shader中計算光照可都是線性的),出來的結果自然是錯誤的。正確做法是先把圖片轉到線性空間(Gamma Decode),然後進行光照計算,然後再把計算結果做Gamma Encode,再輸出給螢幕,也就是下面這個流程圖:
現在對比下兩者的區別,上面的是錯誤的,下面是正確的。可以看出,由於計算錯誤,高光部分已經奇怪的從白色偏移到黃綠色了。我知道很多人覺得上面那個陰影過度很好看,但是如果你觀察現實的話,你會發現現實中的過度邊界就是下面那麼“硬”,如下圖:

如果你喜歡錯誤的那種陰影過度樣式,也應該去調整shader專門做相關渲染,而不是用一個錯誤結果的副產品將就了。 而哪些貼圖需要提前做Gamma Decode呢,原則上來說是Diffuse貼圖。而Normal, Metal, Roughness等只是拿貼圖儲存資料,而不是顏色的貼圖則不要做Gamma Decode。 參考: