概述
線性渲染就是渲染場景所有輸入都是線性的。一般來說存在的紋理都是經過Gamma矯正了的,也就是說當紋理被采樣到一個材質上時,顏色值已經不是線性的了。如果這些紋理用通常的計算方式去計算光照和圖片效果,在非線性空間計算,這將導致輕微的偏差。(而這種偏差就是需要Gamma矯正的原因)
線性渲染保證了在shader中輸入與輸出都是在正確的顏色空間得出更正確的結果。
Gamma矯正
所謂伽瑪校正就是對圖像的伽瑪曲線進行編輯,以對圖像進行非線性色調編輯的方法,檢出圖像信號中的深色部分和淺色部分,并使兩者比例增大,從而提高圖像對比度效果。計算機繪圖領域慣以此屏幕輸出電壓與對應亮度的轉換關系曲線,稱為伽瑪曲線(Gamma Curve)。以傳統(tǒng)CRT(Cathode Ray Tube)屏幕的特性而言,它的輸入電壓和顯示出來的亮度關系不是線性的,而是一個類似冪律(pow-law)曲線的關系,而這個關系又恰好跟人眼對光的敏感度是相反的。這個巧合意味著,雖然CRT顯示關系是非線性的,但對人類來說感知上很可能是一致的。
Gamma 校正補償了不同輸出設備存在的顏色顯示差異,從而使圖像在不同的監(jiān)視器上呈現(xiàn)出相同的效果。
保存顏色信息本身矯正稱為encoding gamma,顯示器對顏色的矯正稱為display gamma,所以一個完整的圖形系統(tǒng)中需要兩個Gamma值,兩次矯正剛好在一定程度上抵消(但一般不是完全抵消)。
下圖展示了Gamma矯正對顏色值的影響。
sRGB
個人電腦使用的一個標準叫sRGB,它使用的encoding gamma大約是0.45(1/2.2),這個值為了配合display gamma為2.5的設備工作的,這樣兩次gamma矯正后產生偏差為0.45 * 2.5 = 1.125,從而在視覺上進行了補償。
非線性輸入
大部分圖像文件都進行了提前矯正,就是保存文件時已經使用了encoding gamma對像素值編碼,這意味著它是非線性的,如果在shader中直接使用就是在非線性空間計算,使得結果和真實世界結果不一致。
Gamma管線(Gamma Pipeline)
在Gamma渲染管線中,所有顏色和紋理在Gamma空間被采樣,在shader應用了之后前,不會對shader輸入做任何處理。即使這些值是在Gamma空間中的,所有shader計算對待他們輸入都當做在線性空間,此外,當把shader輸出寫到內存中時,沒有對最終像素應用Gamma矯正。很多時候是可以接受兩次偏差一定程度上相互抵消。但是這是不正確的。
兩種情況:
線性輸入 輸入顏色值在線性空間下,而在shader中按照線性空間下的計算,這些都是正確的,但最終輸出的時候也沒有做任何處理(主要Gamma矯正),所以在屏幕顯示時,屏幕進行了一次display gamma,這樣的轉換后得到非預期的亮度,通常表現(xiàn)為整個場景比較昏暗。
非線性輸入 輸入顏色值在非線性空間下(通常表現(xiàn)為紋理),而在shader中把該值當成是線性空間下計算的(產生了偏差),這是不正確的,在最終輸出的時候也沒有做任何處理,但在屏幕顯示時,進行了display gamma,雖然這樣產生兩次偏差會在一定程度上進行抵消,但這樣的抵消是不正確的。
為了直接顯示時可以正確顯示,大多數(shù)圖像文件都進行了提前的校正,即已經使用了一個encoding gamma對像素值編碼。
Gamma是非線性的,Gamma空間就是所謂的非線性空間。
線性管線(Linear Pipeline)
如果開啟了線性渲染(Linear Rendering),Unity會背地里把輸入紋理設置為sRGB模式,這種模式下硬件在對紋理進行采樣時會自動將其轉換到線性空間中;并且,也會設置一個sRGB格式的buffer,此時GPU會在shader寫入color buffer前自動進行伽馬校正。如果此時開啟了混合(像我們之前的那樣),在每次混合是,之前buffer中存儲的顏色值會先重新轉換回線性空間中,然后再進行混合,完成后再進行伽馬校正,最后把校正后的混合結果寫入color buffer中。這里需要注意,Alpha通道是不會參與伽馬校正的。
sRGB模式是在近代的GPU上才有的東西。如果不支持sRGB,我們就需要自己在shader中進行伽馬校正。
對非線性輸入紋理的校正通常代碼如下:
float3 diffuseCol = pow(tex2D( diffTex, texCoord ), 2.2 );
1
在最后輸出前,對輸出像素值的校正代碼通常長下面這樣:
fragColor.rgb = pow(fragColor.rgb, 1.0/2.2);
return fragColor;
1
2
但是,手工對輸出像素進行伽馬校正在使用混合的時候會出現(xiàn)問題。這是因為,校正后導致寫入color buffer的顏色是非線性的,這樣混合就發(fā)生在非線性空間中。一種解決方法時,在中間計算時不要對輸出進行伽馬校正,在最后進行一個屏幕后處理操作對最后的輸出進行伽馬校正,但很顯然這會造成性能問題。 還有一些細節(jié)問題,例如在進行屏幕后處理的時候,要小心我們目前正在處理的圖像到底是不是已經伽馬校正后的。
總之,一切工作都是為了“保證所有的輸入都轉換到線性空間,并在線性空間下做各種光照計算,最后的輸出(最最最最后的輸出)進行伽馬校正后再顯示”。
Linear Rendering 和 Gamma Rendering的區(qū)別
Linear Rendering就是在shader中所有計算會在線性空間下進行,Gamma Rendering就是在shader中不進行轉換到線性空間下,直接計算。然就是計算方程式不同,也就意味例如光照表面會有不同的響應曲線和圖片效果,表現(xiàn)不相同。
Light Falloff
光照表現(xiàn)一般受光源的距離和法線兩個因素影響(在同等光強下)。首先當我們用Linear Rendering時,執(zhí)行Gamma矯正將會使光照范圍變大。第二種會使邊緣模糊,分不清界限。這更準確的表現(xiàn)了表面光照強度下降。
表面響應強度
隨著光強的增加,非線性方式計算的表面會更亮一些。這導致了光照在表面很多地方曝光過度,而且給場景模型一個褪色(變白色了)的感覺。當你用線性渲染時,表面顏色仍然隨著光照強度線性增加的,這樣就使表面材質和顏色更接近現(xiàn)實
混合
混合是在幀緩沖區(qū)發(fā)生的,當使用Gamma Rendering,這表示顏色之間混合是在非線性空間下計算的。然而這是不正確的。
上圖是在Linear Space中混合結果,顏色之間過度不是很明顯。
上圖是Gamma Space中混合結果,顏色交界處出現(xiàn)了明顯的其它顏色,顏色更亮,出現(xiàn)褪色的現(xiàn)象。
Mipmaps
計算紋理Mipmap是種線性計算,需要對某個方形區(qū)域內像素取平均值,如果紋理存儲在非線性空間,那么計算時也是在非線性空間里計算,這樣就會得到錯誤的結果。正確的做法是先轉換到線性空間在計算mipmap。
Lightmapping
切換linear 和gamma方式,需要重新烘焙相關的Lightmapping。
寫在最后
總結了最近學習的gamma correction,圍繞unity中l(wèi)inear和gamma討論,參考了大神們寫的文章,加上自己學習的理解,就當做自己的學習筆記,可能有些地方翻譯或理解不夠準確,希望能和大家交流討論。