sRGB標准與伽馬校正


sRGB標准

人眼對亮度的感知不是線性的,其對較暗區域的變化更加敏感   參見:Computer Color is Broken

基於人眼該特點,sRGB標准要求圖像(各通道為8bits,最多存儲256個亮度值)使用編碼伽馬,把更多地空間用來存儲更多暗部區域,來最大化地利用表示亮度的數據位或帶寬

 

伽馬校正Gamma correction

在早期,陰極射線管(CRT)顯示器是唯一的電子顯示設備,但它的輸入電壓和顯示出來的亮度關系不是線性的,而是一個類似冪律(pow-law)曲線的關系,使得信號被壓暗

巧合地是,sRGB標准的編碼伽馬是一個將圖像變亮的冪率曲線,正好與其形成互補,使得不需要再做調整就可以讓sRGB圖像在CRT上顯示出與現實場景一致的亮度

后來出現的LCD和等離子顯示器,為了保證兼容,在硬件上也都選擇了和當年CRT一樣的非線性特性

 

類似於sRGB標准的編碼伽馬(encoding Gamma),由於能校正CRT的顯示伽馬(display Gamma,標准值 γ = 2.2),因此又被稱為伽馬校正(Gamma correction)

 

 

 

 

對渲染的意義

渲染中用到的光照都是在線性空間的。因為在設計光照的時候都是認為1的亮度是0.5的2倍

光照如此,texture又如何呢?渲染中用到的 texture一般有兩個來源,一個是照片,一個是artist手工畫的

前文提到了,照片是gamma = 1/2.2的。一般圖象處理軟件也都是在gamma空間工作的,所以artist畫的圖一般也可以認為是gamma = 1/2.2的

所以,我們在pixel shader常可以見到這樣的代碼:

float4 diff = tex2D(diffuse_texture, uv);
return diff * max(0, dot(light_dir, normal));

這樣的代碼對嗎?不對也對。

說其不對,是因為這里沒顯式地做gamma校正。做校正的話應該是這樣的:

float4 diff = pow(tex2D(diffuse_texture, uv), 2.2f); // 對輸入的紋理進行display gamma,轉到線性空間
return pow(diff * max(0, dot(light_dir, normal)), 1 / 2.2f); // 對輸出進行encoding gamma,再次轉回gamma = 1/2.2空間

也就是說,gamma校正的過程就是把輸入的texture都轉換到線性空間,並把輸出的調整到gamma = 1/2.2的空間

說其對,是因為如果diffuse texture如果是sRGB格式的,那么再讀取的時候硬件會把它自動轉到線性空間

glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);

 

如果render target的texture也是sRGB格式的,在輸出的時候硬件也會把它自動轉到gamma = 1/2.2空間

glEnable(GL_FRAMEBUFFER_SRGB);

 

所以,如果輸入和輸出紋理都是sRGB,那么原先那段shader就是正確的。對於不支持sRGB的老硬件,就必須自己做pow了

 

除了渲染,另一個需要注意gamma的地方就是mipmap。如果原texture是gamma = 1/2.2空間的,那么在建立mipmap chain的時候,需要將原texture先轉到線性空間,來計算各級mipmap;完成計算后,再將各級mipmap轉到gamma = 1/2.2空間

 

另外,gamma變換只作用於RGB通道,Alpha透明度則不受影響

對於normal texture、mask texture等存放的不是顏色信息,這類texture不應勾選為sRGB

 

總之,計算都要發生在線性空間,所以輸入的紋理要先進行display gamma。如果輸出的render target是sRGB格式,輸出時要進行encoding gamma

輸出時encoding gamma,會導致寫入color buffer的顏色是非線性的,這樣混合就發生在非線性空間中。解決方法是,在中間計算時不要對輸出進行encoding gamma,在最后進行一個屏幕后處理操作對最后的輸出進行encoding gamma

最佳選擇是采用sRGB格式,這樣pow是硬件內自動實現,速度更快,代碼也簡單。鑒於目前很多texture的數據是gamma = 1/2.2的,而紋理格式卻被錯誤地標記成沒有sRGB的,所以需要修改它們的格式標記,並重新建立mipmap

 

如果在gamma空間中進行着色計算,會造成了渲染出來的游戲總是暗沉沉的(如下右圖所示),和真實世界不像

 

參考

klayge:gamma的傳說

candycat1992:【圖形學】我理解的伽馬校正(Gamma Correction)

Unity:LinearRendering-LinearOrGammaWorkflow 

Gamma Correction and Why It Matters 

learnopengl:Gamma Correction

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM