反走样和OpenGL多重采样
1. 反走樣
在計算機圖形學中,在屏幕上顯示對象時,可能會出現許多的“鋸齒”,這些鋸齒是由頂點數據像素化之后成為片段的方式所引起的,由于將數學意義上的坐標轉換到物理的顯示器硬件上進行顯示,顯示器是有一個個像素點構成的,并不能實現數學意義上的“無限小”的描述。關于產生鋸齒的更詳細的介紹可以參考OpenGL學習腳印: 反走樣初步(Anti-aliasing basic)
為了消除“鋸齒”,圖形工作者提出了許多抗鋸齒的算法(也稱為反走樣[Anti-aliasing]算法),本文主要介紹OpenGL中提到的一種反走樣方法——多重采樣(Multisample antialiasing簡稱 MSAA)。接觸到這一主題時,查閱了很多資料,發現網絡上許多內容都大同小異,很多內容并沒有參考價值。整體的思路說清楚了,但是一旦涉及到某一項技術,最難的往往是技術中的細節部分。本文記錄我個人在理解MSAA時候比較困惑的點,在理解之后記錄下來。
2. Supersampling
在理解MSAA之前,有必要先理解一下什么是SuperSampling (字面翻譯是超采樣,簡稱是SSAA),超采樣就是加大采樣的點,提供比屏幕分辨率更多的采樣點。是渲染的時候按照顯示器分辨率的若干倍來渲染,例如顯示器1024x768,那么SSAA 4X就是4096x3072。
?
上圖是OpenGL決定一個像素顏色采用的算法,如果紅色的采樣點(像素中心位置)落在三角形區域,那么這個像素就被認為屬于三角形,從而會著色成為三角形的顏色。但是邊緣部分的像素并沒有“完全屬于”三角形,那么這些像素的顏色應該是某種介于邊緣多種顏色的“過渡色”比較合理一點。
SSAA的想法比較簡單,就是擴大采樣點,生成一些次像素級別的采樣點(sub-pixel)。有點類似股份公司的感覺,把像素理解成為一家公司,那么這家公司歸屬誰呢?在中心區域,由于完全被三角形區域所包含,那么屬于三角形沒有什么爭議,但是邊緣部分的像素由于并沒有完全被三角形占據,那么它的所有權問題就應該是多個股東決議的結果,根據大家股份占用的大小來中和最后的結果。
在像素中如何添加新的次像素級別的采樣點,方法很多。不同的采樣點設計方式最終對場景渲染的結果也不同。以下是一些采樣點的選擇方式:
得到次級像素的采樣點之后,把這個場景渲染到一個更高分辨率的緩沖區A中,然后再從A緩沖區中采樣出和屏幕分辨率一樣的像素,像素的顏色使用A緩沖區中的像素顏色插值得到。整體的思路如下圖所示:
上圖中使用4個采樣點,如果不使用SSAA技術,那么最終屏幕上這個像素的顏色是中心位置的黃色,使用了SSAA之后,這個像素的顏色是淡淡的棕黃色,這個顏色綜合了像素中其他的顏色值計算而來的。(圖示中使用的是簡單的求平均值的方法,在實際中可能會有其他的算法來計算,但是整體思路還是一樣的)
在SSAA方法中,有一個需要注意的地方是:所有的次級采樣點和未使用SSAA中采樣點的地位是一樣的,假設有片元shader需要在每一個像素上運行,那么SSAA中片元shader也會在每一個次級像素上運行。理解這一點非常的關鍵,它是SSAA和MSAA最重要的一個區別。
SSAA的缺點:通過上面的分析可以知道SSAA需要占用更大的顯存空間,它需要更大緩沖區,采用4x、8x、16x那么使用的空間是未開啟SSAA的4倍,8倍和16倍。另外SSAA需要每一個shader在所有次級片元上都同樣的運行一遍,這也是一筆可觀的計算開銷,會顯著地降低FPS。
3. MSAA
有了SSAA的基礎,那么理解多重采樣(MSAA)就簡單很多了。MSAA中增加采樣的方式和SSAA是一樣的,MSAA同樣也需要一個分辨率更高的緩沖區(假設命名是Anti-Buffer),但是MSAA并不是像SSAA一樣把次級采樣點當成類似未開啟反走樣中采樣點同等地位來對待,它采用另一種方式。
示意圖如下:
圖示中兩個三角形的圖元都包含了像素的采樣點(圓形位置),4個叉代表著該像素的4個子采樣點。在開啟MSAA之后的過程如下:
首先由于屏幕背景清空色是白色,那么Anti-Buffer中的每一個子采樣點的顏色都設置成了白色,
假設藍色的三角形首先繪制,由于它包含像素的采樣點,因此在跑了一次shader之后,左下角的這個2個子采樣點在Anti-Buffer緩沖區的值被藍色填充,然后開始繪制黃色三角形,由于它也包含像素的采樣點,因此也會跑一次shader,假設計算的結果是黃色。由于右邊的采樣點被它包含,因此被設置成黃色,最終這個像素對應在Anti-Buffer緩沖區中4個子采樣點的顏色是白色、黃色、藍色、藍色。最終的顏色由這4個顏色計算平均得到。
也就是說:MSAA每一個像素上shader只運行一次,運行的結果會復制到每一個包含了子采樣點的緩沖區中。而不是像SSAA那樣每一個子采樣點都運行一次shader。大大減少了shader的運行次數。
4. MSAA在OpenGL中的使用
網絡上許多關于MSAA在OpenGL中的使用方式都沒有說的很清楚,這里就介紹一下OpenGL中的MSAA。
OpenGL中有很多狀態變量,默認情況下基本都是關閉的(比如光照計算、混合計算、深度測試等等),但是只有兩個狀態默認是開啟的,MSAA就是其中之一(另一個是Dither),要使用OpenGL提供的MSAA,需要申請一個可以進行超采樣的緩沖區(也就是我們上文說的Anti-Buffer),申請的方式和窗口系統有關,在GLUT中申請的方式很簡單,只需要添加一個枚舉值的變量GLUT_MULTISAMPLE即可:
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH |GLUT_RGBA | GLUT_MULTISAMPLE);添加緩沖區之后,開啟多重采樣即可(默認是開啟的可以不用顯式設置),開啟方式:glEnable(GL_MULTISAMPLE);
使用方式就是這么簡單,另外在OpenGL中還有一些多重采樣控制的參數需要設置,相關的API如下:
開啟多重采樣覆蓋率的設置
在MSAA介紹部分,我們列舉的例子中,說到了最后的顏色由白色、黃色、藍色、藍色。這4個顏色計算平均得到。這里的描述是不完全準確,OpenGL默認情況下是會直接取4個顏色的平均值,但是也可以添加更多的控制。
默認計算算法((color1+color2+color3+...+colorn) / n),需要注意這時候color的alpha值是不參與計算的。
如果進行下面的設置,那么計算算法會改變,
4.1. glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
設置之后alpha值參與計算
由于多重采樣的實現方式很多,OpenGL也沒有規定硬件應該怎么實現多重采樣,都是交給硬件自身處理。因此這里我們描述一種可能的多重采樣的處理過程:
當我們使用4x,8x等MSAA時,OpenGL會為每一個像素分配一個掩碼值,這個掩碼值的位數是由子采樣的點數決定的,也就是說4x是分配4位,8x是分配8位。分配完成之后,這些位記錄了一個子采樣點是否被其他幾何圖元覆蓋到了,以3中的情況為例,由于那4個采樣點有3個被覆蓋到了,因此那個像素中的Coverage掩碼是 0 1 1 1
A B C D ————> 4個采樣掩碼位
0 1 1 1 ————–>覆蓋掩碼值
白色 藍色 藍色 黃色 ——>MSAA采樣緩沖區值
然后再計算平均值的時候考慮到某一個掩碼是0,那么該子采樣點的顏色就不參與最后的運算了。
上面說的是正常的處理過程,一旦我們設置了glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE),那么這個掩碼就要開始運算了:設置之后,OpenGL會根據MSAA緩沖區中顏色值的Alpha成分,生成一個掩碼值,并與這個原來的掩碼進行按位與的運算。(alpha值是一個浮點數的值,怎么進行按位與的運算呢?原來OpenGL可以通過映射,將某一范圍內的值映射成一個整數),生成一個新的掩碼值。具體來說是:
一個fragment的Alpha值在0~1間,它對應著一個值。還是以4XMSAA為例,這個值也是xxxx的形式,Alpha為0對應了0000,alpha為1對應了1111,至于中間的值的對應關系,OpenGL是交由顯卡制造商決定的——其實一般就是類似[0~0.249 -> 0000, 0.25~0.499 -> 0001, 0.5~0.749 -> 0011, 0.75~0.99-> 0111]這樣(在D3D11中,就可以自定義這個值)。這個值與與coverage mask作一次邏輯按位與操作獲得新的coverage mask。
4.2 glEnable(GL_SAMPLE_ALPHA_TO_ONE);
設置多重采樣中每一個子采樣點的顏色alpha成分是1。
4.3 . glSampleCoverage
開啟自定義設置,需要2個函數調用
glEnable(GL_SAMPLE_COVERAGE)
glSampleCoverage
這里面glSampleCoverage的函數原型如下:
第一個參數是 value,表示掩碼值
第二個參數是 invert,表示是否翻轉計算
第一次看到這個接口API時,發現是一個float的value值,OpenGL的spec文檔中一直提到要做按位的與運算,我就一臉懵逼了,按位運算不是整型才有的運算操作嗎?是不是說這個value會被cast成一個整型,既然要cast成一個整型為什么不設置參數類型就是整型呢?
事實上是這樣的:這個浮點數的值會被映射成一個整型數,和之前alpha_to_coverage類似
glSampleCoverage的效果和 glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);有點類似,都是設置掩碼和原來像素的coverage掩碼作按位與的運算,不同之處是后者使用顏色成分的alpha值運算。而這個函數是自己指定一個值,這個值是value映射后的整型值。第二個參數invert是對這個value映射后的整型值作按位取反操作。然后再與像素的coverage掩碼作運算。
以上是所有OpenGL中和MSAA反走樣相關的內容。反走樣是一個十分龐大的主題,讀者也可以通過FBO自己實現反走樣的算法,關于在FBO中作反走樣,可以閱讀參考資料中的內容。
5. 參考資料
1. OpenGL學習腳印: 反走樣初步(Anti-aliasing basic)
2. 抗鋸齒
3. Supersampling
4. Multisample Anti-Aliasing
5. Rasterization Rules
6. 亂彈紀錄II:Alpha To Coverage
————————————————
版權聲明:本文為CSDN博主「csxiaoshui」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/csxiaoshui/article/details/78932603
總結
以上是生活随笔為你收集整理的反走样和OpenGL多重采样的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开源项目Hopsan代码梳理、流程分析
- 下一篇: OpenGL学习脚印:模板测试(sten