Gamma和Linear修正的問題相信網上已經有很多文章了。簡單來說顯示器的顏色輸出不是線性的,根據硬件參數和輸出顏色
信息擬合曲線是x^2.2,因此會使用一個x^0.45曲線將其擬合回線性。
因為0.5^0.45 = 0.73,0.5^2.2 = 0.217,我們可以通過一張0.5rgb的灰度圖放Unity中觀察變亮還是變暗
來判斷Unity在線性空間下做了哪些操作,以及參數配置是否正確。
這里進入URP渲染管線,並設置為Linear線性顏色空間進行測試:
測試1:一張圖片在ps里制作成(127,127,127)的純色,導入Unity。不勾選sRGB,輸出顏色較亮,勾選sRGB,輸出顏色正常
結論:最終顏色輸出時可能有gamma0.45處理。
測試2:frag里輸出顏色為0.5,該shader最終屏幕輸出時,輸出顏色較亮
結論:進一步確定輸出時有gamma0.45(pow(x,0.45))的處理。
測試3:增加一個UV偏移的shader參數值,在Gamma顏色空間和Linear顏色空間下切換查看,無區別
結論:顏色空間的修正處理類似濾鏡,而正常傳入Shader的字段數值不會做編解碼,不參與顯示相關的數值也不用做
糾正。
測試4:增加一個float類型的shader字段,直接輸出在frag里,並將參數值設為0.5。輸出顏色較亮,
而給參數加上[Gamma]前綴,輸出時顏色正常。
結論:因為最終輸出時屏幕會有一步gamma0.45處理,所以給常規參數追加一個gamma2.2處理,
這樣輸出就是原始顏色了(加上[Gamma]相當於pow(x,2.2))。
測試5(重要):Unity Linear顏色空間,一張Alpha0.5的純黑圖片,疊在白色背景上,但混合結果顏色並不是0.5的灰色。
嘗試加pow(2.2)手動修復,然后換成Alpha0.5的白色圖片,疊純黑背景上。發現僅是對Alpha值修改在混合處理上沒有用。
結論:首先明確一點Alpha通道不會進行sRGB矯正。但是Alpha值雖然沒問題,Unity的混合處理卻有問題。gamma0.45的修正是處理在
混合完成的結果上的,你並不知道混合結果的顏色是來自黑色疊白色,還是白色疊黑色,或是其他顏色相疊。
內建管線下可以創建Gamma RenderTexture,也就是sRGB為true的RT。但URP下似乎有些失效,所以這個問題博主自己目前還沒解決。
雖然單改Alpha值沒用,但我還是研究出了一個基於經驗的公式,通過讀取當前顏色和修改Alpha;做到了一部分還原:
float whiteAlpha = pow(color.a, 2.2); float blackAlpha = 1.0 - pow((1-color.a), 2.2); float lum = max(color.b, max(color.r, color.g)); color.a = lerp(blackAlpha, whiteAlpha, lum); color.rgb *= color.a;
若要求不高又不想擴展管線,也可以試試。
最后總結下:
不用於圖像輸出的圖片,如噪波,就不需要勾選sRGB(不勾選sRGB也叫做保持Linear)。不用於圖像輸出的shader字段,如UV偏移值
就不用處理。用於圖像輸出的shader字段,需要加[Gamma]前綴或者手動調用Gamma22ToLinear。
同樣,用於顯示輸出的圖像如果中途要進行邏輯計算,需要調用LinearToGamma22轉回原始數值。
例如一些手游項目會合並多張貼圖到一張,這就導致了Linear和sRGB混在一張圖片內,這時候就需要代碼里手動去轉了。