對多重採樣(MSAA)原理的一些疑問?

本人已有一定3D圖形學基礎,早年學習過dx,最近為深化知識點學習opengl。最近在看了紅藍寶書,和在網上搜索過很多資料後,仍然對msaa的具體原理一知半解。

主要是因為所有能找到的資料,都沒有一個是能清楚明白地說明問題的,總是把話說一半不說一半,真想知道知識的原出處在哪?是硬體廠商嗎?還是opengl的紅寶書就是出處?後面的人能寫出各種技術文章,總有個參考的出處啊!比如是創造這msaa演算法的人,總會把原理說明白吧?

以上是我想問的第一個問題。

然後,開始問msaa一些我不清楚的問題:

一,多重採樣執行的時間點。書中說的是 光柵化&>片段著色器&>裁剪測試&>msaa 這個順序,我就奇怪了,msaa不就是為了在片段著色器取樣的時候多取樣幾個點再合成一個顏色輸出嗎?放在片段著色器完了裁剪也完了之後才執行是幾個意思?還有什麼用?

二,開啟了msaa,到底在片段著色器階段是逐個sample執行還是一個片段只執行一次?比如4x msaa,是一個片段代碼跑4次還是1次?不同的資料來源說法不一。

三,msaa如4x是將每個sample所在位置採樣得到的顏色加起來除以4嗎?(默認演算法)

四,每個sample是分別在所在的位置採樣不同的顏色,還是都一致等於中心點(片段本身)的顏色?有些資料說比如4x msaa是四個不同的顏色混合,有些資料又說都是等於同一個顏色(片段的顏色)

五,深度和模板值同上,是四個不同的值還是同一個值?(有些地方說是同一個值,只是當沒有一個sample通過的時候才拋棄這個片段,心想這還有什麼用?)

六,深度和模板值,到底有什麼用?怎麼影響到msaa是只對邊緣處理?(具體原理)

七,以上跟alpha to coverage有什麼關係?是不是開啟了a2c,以上問題的結果會不一樣?

八,a2c為什麼可以實現順序無關透明?(詳細點具體原理解釋)(只要關鍵點即可,我知道覆蓋率什麼的,不明白為什麼可以順序無關)


Multi-sample Anti-alias 不是 super sample。所以你說的「在片段著色器取樣的時候多取樣幾個點再合成一個顏色輸出」並不成立。實際的做法是在一個 pixel 中做 n 次 sample,來確定這個 pixel 會覆蓋哪些 triangle。然後對於每個 triangle 調用一次 shader(至於這次調用用的是什麼坐標 —— 比如是 pixel center 還是某個 sample 坐標,就因硬體而異了)產生一個 fragment color。然後每個 fragment color 根據 sample hit 的次數(稱為 fragment coverage)進行 blend。

所以不是有幾次 sample 就調用幾次 shader(否則就是 super sample了)。如果 8 次 sample hit 了兩個 triangles,就調用兩次。hit 了三個,就調用三次。hit 了一個,就調用一次(這個 triangle 的 coverage 就是 100%)。


1. MSAA 發生的時間

MSAA發生在光柵化階段。以4xMSAA為例,對於每一個pixel而言存在一個長度為4bit的coverage mask,其中每一位都代表對應採樣點的覆蓋情況。這裡所謂覆蓋是指某個採樣點被一個或多個primitive(一般多為三角形)覆蓋,並且位於scissor box內。

2. MSAA與PS調用次數的關係

一般GPU都支持兩種shader iteration mode, per pixel and per sample. 從名字上也可以猜出他們的意義。Per pixel模式下,每個可見pixel只調用一次PS,所使用的position, normal coord, or color取自像素的中心採樣點,這種情況相當於沒有進行MSAA。Per sample是對每一個可見sample點調用一次PS,所採用的參數分別來自各採樣點的內插值。一般使能MSAA的情況下,driver都會使用per sample 模式。

3. MSAA中Depth/Stencil值如何處理

每一個sample都有自己獨立的Depth/Stencil值

4. MSAA最後的結果如何合成

前文提到coverage mask的概念,最後在由GPU cache寫入顯存時,會進行一次降採樣,其過程基本上就是將每個sample的值根據這個coverage mask做平均.

5. Alpha2Coverage

每個像素或採樣點的Alpha值被轉化為coverage mask。


關於MSAA可參考官方規格說明文檔
對於OpenGL是Khronos維護的:https://khronos.org/registry/OpenGL/index_gl.php,其實裡面很多內容在紅藍寶書都有。
DX由微軟維護,但只通過DX的SDK文檔或Rasterization Rules公開了較上層的那部分

先貼出OpenGL官方描述:

First, each fragment includes a coverage value with the value of SAMPLES bits
Second, each fragment includes SAMPLES depth values and sets of associated data, instead of the single depth value and set of associated data that is maintained in single-sample rendering mode. An implementation may choose to assign the same associated data to more than one sample. The location for evaluating such associated data can be anywhere within the pixel including the fragment center or any of the sample locations. The different associated data values need not all be evaluated at the same location. Each pixel fragment thus consists of integer x and y grid coordinates, SAMPLES depth values and sets of associated data, and a coverage value with a maximum of SAMPLES bits.

整個回答以MSAA4x為例,官方描述是說,每個像素有4bit的coverage mask,以及4個深度值(每個sample各一個),另外還有其他attribute(最簡單的就是color),並且是可以拿一份color copy給所有sample(準確地說是只copy給那些mask非0的sample),而這份color可以來自像素中心或者某個sample。

一,多重採樣執行的時間點。書中說的是 光柵化&>片段著色器&>裁剪測試&>msaa 這個順序,我就奇怪了,msaa不就是為了在片段著色器取樣的時候多取樣幾個點再合成一個顏色輸出嗎?放在片段著色器完了裁剪也完了之後才執行是幾個意思?還有什麼用?

1)PS(Pixel Shader,片段著色器)只負責算出每個像素/sample的color,它並不知道每個像素各自有多少個sample被覆蓋、是有效的,其實PS自身都可能把sample給discard。

2)裁剪(scissor reject)完再執行是必須的,要得到coverage mask,不僅僅看4個sample中是否被三角形覆蓋到(光柵化),還要看是否在scissor內部。比如某個像素4個sample都被triangle覆蓋,但可能只有2個sample在scissor里,所以「合成」這個操作是不可能在PS做的,PS不具備需要的信息。

3)至少不可能在第一個pass的PS完成resolve,一般底層也很難一個pass到位,因為4x本質上是中間數據的4x,一個pass得到4x的深度、color,但最終展現的是1x的Image,到了Output Merger這個階段一般不會為了DownSample去加一些資源。所以通常把4x的color寫到buffer上,再一個pass(對於上層而言是不可見的)的PS來做Down Sampling,得到每個像素的,即仍然是1x的color。

二,開啟了msaa,到底在片段著色器階段是逐個sample執行還是一個片段只執行一次?比如4x msaa,是一個片段代碼跑4次還是1次?不同的資料來源說法不一。
四,每個sample是分別在所在的位置採樣不同的顏色,還是都一致等於中心點(片段本身)的顏色?有些資料說比如4x msaa是四個不同的顏色混合,有些資料又說都是等於同一個顏色(片段的顏色)

默認每個pixel只執行1次,即Pixel Frequency,也就是說PS只執行算出每個像素中心的color,然後copy給4個sample。當然前面說了,這份copy不一定來自像素中心,也可以是像素的其他位置,例如DX中如果把color的插值方式聲明為Centroid,那麼當像素中心不在三角形內部(但有Sample在三角形內),則會選擇三角形內的Sample,避免「Outerpolate」。

Pixel Frequency的好處是PS仍然是1x的,降低消耗,但有時候視覺效果不太好,所以也可以開啟Sample Frequency,要求一個pixel的4個sample分別由4個PS負責執行,每個sample計算各自的color。可以參考ARB_sample_shading的說明:https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_sample_shading.txt

三,msaa如4x是將每個sample所在位置採樣得到的顏色加起來除以4嗎?(默認演算法)

  默認是。

五,深度和模板值同上,是四個不同的值還是同一個值?(有些地方說是同一個值,只是當沒有一個sample通過的時候才拋棄這個片段,心想這還有什麼用?)
六,深度和模板值,到底有什麼用?怎麼影響到msaa是只對邊緣處理?(具體原理)

無論Pixel Frequency還是Sample Frequncy,深度始終是4x,即每個sample有自己的深度。還是回到「有效」sample的概念上,深度測試fail的sample是無效的,PS輸出的color不會copy給它。如果4個sample的深度測試都fail,那麼pixel被discard。

七,以上跟alpha to coverage有什麼關係?是不是開啟了a2c,以上問題的結果會不一樣?
八,a2c為什麼可以實現順序無關透明?(詳細點具體原理解釋)(只要關鍵點即可,我知道覆蓋率什麼的,不明白為什麼可以順序無關)

Alpha to Coverage是在MSAA Enable是才能起作用的,因為它是通過改變coverage mask來改變color的(通過與操作),A2C是要利用MSAA,不會對影響以上問題的結果。  

將AlphaTest,Alpha to Coverage和Alpha Blend一起比較:

Alpha Test順序無關,但邊沿效果差。

Alpha Blend邊沿效果較好,但要求嚴格順序。

在一定程度上,Alpha to Coverage可以理解為基於sample級別的Alpha Test,把Alpha轉成mask來實現平滑過渡。理解了Alpha Test順序無關,就理解了Alpha to Coverge順序無關。

-------------------------------------一些補充----------------------------------------------------------------------

1)關於supersample和MSAA

MSAA默認是pixel frequency的,開啟了sample frequncy時我覺得該算作supersampling了,Stack Overflow有個類似的問題也提到了:Multisampling in pipeline。supersampling和MSAA的區別是PS對每個像素是per-pixel執行還是per-sample執行,MSAA也是對所有位置處理的,並不只是邊沿,只是因為三角形內部像素coverage mask為1111,所以最終顏色仍等於像素中心顏色。

2)MSAA最終圖像的由來

MSAA開啟時,解析度x4,即100*100的屏幕,要準備200*200的數據buffer,每個像素的4個sample有各自的color,深度,一口氣把整一幀所有的triangle畫完,按照sample級別去做Z test(當前像素的Z比buffer里的Z小則測試通過),最終像素的顏色是4個sample的均值,通過Bilinear resolve得到100*100。

每個sample的顏色可能來自不同三角形,如下圖左邊(最終顏色應該是(紅+綠)/2)。有多個三角形覆蓋同一個sample,當然選擇最終Z test pass即離眼睛最近的,如下圖右邊(最終顏色應該是(紅+背景色)/2)。總之就是4個sample color的均值,沒被任何三角形覆蓋的sample取的自然就是背景色。


msaa的理解難點在於和ssaa區別開。

msaa:一個三角形覆蓋片段內的多個採樣點,只進行一次片段計算,如果你的renderTarget是一個多採樣紋理,那麼多個採樣點的輸出是一模一樣的(深度除外)。而ssaa每個採樣點都需要一次片段計算,輸出是不一樣的。


題主我對MSAA的理解跟你有些不同:

提到MSAA,我們應該先談談SSAA:據渲染到一個高解析度的buff上,然後再downsample到屏幕解析度上.這麼做的問題是什麼?除去更占內存,你的pixel shader計算次數也顯著增加.後來大家發現,其實鋸齒的明顯部分集中在幾何邊緣,比如牆壁的垂直或者水平邊緣,傾斜下就出現"小樓梯".這個時候MSAA誕生了:SSAA跟MSAA大多數技術細節通用,不同的是MSAA的pixel shader只在他的sub pixel有效覆蓋才進行計算.

所以,

問題1:放ps後計算,那就跟SSAA沒啥區別了.性能考慮嘛~

問題2:看前面已經解釋了.

問題3.當然不能除4,但是一般來說是有效顏色均除的boxfilter.

問題4:

問題5:不通值,不然覆蓋和遮擋沒法算.

問題6:參看前面第一段描述.

問題7:沒關係

問題8:他真的可以?


推薦閱讀:

如何用OpenGL封裝一個2D引擎?
如果做一個GIS渲染引擎,圖形API應當選擇OpenGL還是DirectX?
有哪些優秀的 C++ OpenGL 開源遊戲引擎?
為什麼 NVIDIA 的示例總是一條龍?

TAG:演算法 | OpenGL | 計算機圖形學 | DirectX |