這兩年個人工做都轉到了D3D11,目前新出硬件幾乎所有支持此標準,加上D3D11接口清晰,概念直觀,等到windows7普及,想必將來都是D3D11的天下。最近時間較空,我陸續開始寫些基礎文章,但願對新學者有所幫助。但文章純屬我本身隨意寫寫,錯誤確定不少,請你們多多包涵。 算法
所謂MSAA,就是讓一個像素能夠同時存儲多個顏色,而最終的顯示結果由多個顏色重建而成。具體存儲顏色的數量由DXGI_SAMPLE_DESC中的Count來決定,其中的Quality則通常用來給硬件設計廠商做爲很是規發揮的餘地,好比NVDIA CSAA開啓方式就是用Quality某些值來實現的。在D3D9中MSAA中即便一個像素被分紅多個子片斷來光柵化,但實際上覆蓋此像素的每一個三角形依然只執行一次pixel shader,子片斷的位置只用來決定各類頂點屬性的插值位置,以及進行覆蓋率評定,這就是MSAA相比SSAA的最大不一樣之處,MSAA只會增長被多個三角形同時非徹底覆蓋時的計算率,並且無論其覆蓋率有多高,每一個三角形都只執行一次Pixel shader,並將Pixel shader返回的值存入相關覆蓋子像素。須要注意的是,這裏存在兩個細節問題,一是Pixel shader輸入的值如何插值而來(插值位置和插值算法);二是子像素到底位於像素中的何處,個數如何決定,是否每一個子像素都對應一個color/z/stencil存儲,如何將全部這些存儲的子合成爲最後的結果。對於第一個問題,就牽涉到MSAA光柵化規則的問題,注意若是沒有任何特殊設置,對應像素Fragment的屬性插值操做都將在像素中心位置上執行,MSAA中進行覆蓋率評定時,頗有可能三角形並未覆蓋到像素中心位置,這就牽扯到一個外部插值(extrapolate)的問題,即插值位置根本就不在三角形內,很顯然這樣插值出來的屬性結果是錯誤的,爲了解決這個問題,D3D引入一個配置「centroid sample」,指定在rasterize時,相關屬性進行插值的位置必須位於三角形與像素相交區域內,這個一般這個位置取在某個被覆蓋的子像素位置,但並不保證永遠不變,可能和具體硬件設計還有關,D3D11 reference rasterizer選擇centroid sample位置的具體算法,可參考D3D文檔。在D3D11中想要打開centroid sample,只需在對應pixel shader input attribute上加上centroid modifier便可;屬性的插值算法,就是如何用三個頂點attribute值,以及中間點A的位置,使用某種算法插值出attribute在中心點A上的值,D3D中最經常使用的就是帶透視矯正的線性插值(linear),全部attribute默認都使用此算法插值(linear modifier),固然D3D11還提供其餘幾種插值方式:nointerpolation(就是不插值,使用三角形中第一個頂點的屬性值做爲Fragment屬性值),noperspective(不帶透視矯正的線性插值,只使用屏幕2D座標位置進行插值計算),sample(在每一個子像素位置進行插值)。這些modifier能夠加在PS input attribute前面,不過使用起來仍是有些限制和規則,好比centroid、sample明顯只能在MSAA模式下才能起做用,由於普通模式下不存在非中心覆蓋和子像素位置問題;而centroid很顯然也不能同nointerpolation一塊兒使用,更多信息還請參考DX文檔,畢竟知道這些背後原理後,更好記憶和理解這些限制。如今討論第二個問題,子像素分佈在像素區域中的位置是因硬件設計而變的,D3D標準並無規定具體分佈的位置,而個數按道理上來說就是DXGI_SAMPLE_DESC種的count所變量指定。是否每一個子像素都會在RT surface上有相應的存儲位置(color/z/stencil),這個就有點懸了,畢竟這個是要增長硬件成本的事,並且D3D標準也沒強制,硬件廠商說:OK,我能夠給你指定的覆蓋點數,我也能夠把這些點的位置進行精心設計分佈,但我不必定會給每一個點都分配實際的存儲位置。好比CSAA就將子像素數和實際存儲數分開來了,以此來節省存儲和帶寬,CSAA和16x實際上只有4個存儲位置(但它確實有16個子像素),16個子像素(覆蓋率判斷)如何分享4個存儲位置呢?答案是硬件設計有關。最後一個問題,每一個像素中存儲的多個值如何重建爲最終結果?答案仍是硬件設計有關,但咱們能夠本身resolve(http://mynameismjp.wordpress.com/2012/10/28/msaa-resolve-filters/)。windows
在D3D11中是能夠指定pixel shader進行per-sample excution的,這個和D3D9徹底不一樣,在pixel shader input中指定SV_SampleIndex屬性或爲屬性指定sample modifier都會打開pixel shader逐子像素執行(這個在CSAA中就有點問題了,由於CSAA並不爲每一個子像素分配獨立的存儲)。MSAA並不禁Pipeline中的一個stage完成,而牽涉到rasterization、pixel shader、output merger三個stage,D3D11對MSAA的操做進行了空前的加強,能夠獲取sample index, coverage mask, sub pixel value, 以及pixel shader新支持的UAV,綜合這些咱們能夠完成一些很特別的算法。須要注意的是用centroid sample或per sample execution後會帶來一個問題,就是GPU的某些地方的導數計算可能有誤,好比ddx ddy以及texture lod計算,由於三角形邊緣像素的採樣位置會被偏置到某個sample的位置,而再也不是像素中心,這樣2x2像素中,變量相差以後的值就再也不是基於單位的屏幕空間座標了,這樣在三角形邊緣的像素上計算變量的導數就會出現跳躍起伏,這樣會使ddx ddy的結果產生異常,因此要麼你能容忍或解決這個問題,要麼就不要在centroid sample的屬性上進行導數計算。wordpress
pixel shader輸出Z會給MSAA帶來一些麻煩。若是pixel shader沒有開啓per sample exctution,但卻輸出了SV_Depth,這就產生一個問題,原本每一個子像素在depth stencil buffer中都會輸出各自獨立的Z值,此Z值爲光柵化時插值產生,所以每一個子像素都有一個正確的Z值,但若是pixel shader人工輸出了Z,而這個pixel shader只執行一次,這樣被此三角形覆蓋的全部子像素的Z值都將是這個單一值,此值爲像素中心的Z值(沒有開啓centroid sample的狀況下),這就會致使一個問題,全部先繪製了更近三角形的邊緣像素均可能失去或產生錯誤的抗鋸齒效果!(特別是在三角形連續交界處)請看下圖,繪製順序爲紅、藍、綠,這些幾何體的pixel shader都輸出了SV_Depth。請注意某些邊緣已經失去了抗鋸齒效果。spa
另外D3D10引入一個新的概念ALPHA-TO-COVERAGE,以及一個SV_Coverage的pixel shader輸出變量。註定要把MSAA玩出花來了!以8x的MSAA爲例,在z/stencil/color buffer上每一個像素均有8個子像素,若是開啓了ALPHA-TO-COVERAGE,pixel shader輸出的ALPHA值會被轉爲一個8階的值,表示此Fragment在像素上的mask,這個主要是用來解決Alpha Test邊緣鋸齒問題,其原理就是將光柵化階段產生的MASK A,AND ALPHA轉化的MASK B,AND SV_Coverage MASK C。看下面的例子,三塊徹底重疊的面片,打開Alpha-To-Coverage,而且都輸出0.5的Alpha值,從近到遠分別爲紅、綠、藍,發現徹底不會有互相半透的效果,緣由很簡單,本例開的是8x msaa,0.5的ALPHA會被GPU轉化爲00001111B的MASK,紅綠藍三個Mesh都輸出相同MASK的話,子像素的值會被最近的Mesh覆蓋掉。設計
咱們修改下輸出的Alpha值,紅色0.25,綠色0.5,藍色0.75,當紅綠藍視距從近到遠排列時,輸出結果以下:blog
很簡單,由於紅色的MASK爲00000011B,綠色爲00001111B,藍色爲00111111B,互相重疊的部分,近的顏色將佔據MASK相對應的子像素,較遠的會被覆蓋掉。若是咱們再反過來看,讓紅綠藍視距變爲從遠到近排列,結果就變成這樣了:接口
緣由你們能夠本身分析。綜上,Alpha-To-Coverage註定是個悲催的OIT技術!ip
更多MSAA資料ci
http://mynameismjp.wordpress.com/2012/10/24/msaa-overview/文檔