这两年个人工做都转到了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/文档