满怀冰玉一杯酒,又是初雪旧时节
关于深度测试
深度缓冲区,也常常称为Z缓存,是Direct3D中用来存储绘制到屏幕上的每个像素点的深度信息的一块内存缓冲区,是一个只含有特定像素深度信息而不含图像数据的表面,深度缓存为最终绘制的图像中的每一个像素都保留了一个深度值,如果我们绘制屏幕的分辨率为800×600像素的话,那么深度缓存的大小也为800×600。
当Direct3D讲一个场景渲染来目标表面上时,它使用深度缓冲区来决定光栅化后各个多边形的像素前后的遮挡关系,最终决定哪个颜色值会被绘制出来。
所以可以这样理解,Direct3D通过比较当前绘制的像素点的深度和对应深度缓冲区的点的深度值来决定是否绘制当前像素。如果深度测试的结果为TRUE,就会绘制当前像素,并用当前像素点的深度值来更新一下深度缓冲区,反之,则不予绘制。而在通常情况下,深度缓冲区对应于屏幕大小的一块二维区域。
比如,我们在三维场景中同时绘制了一把长剑和一个盾牌两个立体模型。如果长剑的深度值为30,盾牌的深度值为60( 如取景变换中摄像机的位置矩阵在Z轴上为负值D3DXVECTOR3 vEye(0.0f, 1.0, -100.0f),长剑模型的世界矩阵为(0.0f,0.0f,30.0f),盾牌的世界矩阵为(0.0f,0.0f,60.0f)),那么就是表示长剑的盾牌的前面。Direct3D会首先绘制长剑和盾牌中的其一,而当绘制剩下的那个3D模型的时候,Direct3D会将当前3D模型位于同一位置的像素与已经绘制的像素(如果两者在该位置都有像素的话)进行测试,若当前像素比原来的像素更近(即深度值更小),那么将用当前像素来更新掉原来的像素(前提是),否则不予更新。
当Direct3D将一个场景渲染到目标表面的时候,它使用深度缓冲区决定光栅化后每个多边形的像素前后的遮挡关系,最终决定哪个颜色值被绘制出来,对于一个启用了深度缓冲区的场景进行光栅化操作时,表面上的每个值都要进行深度测试。
深度测试四部曲
1.创建深度缓冲区
2.开启深度测试
3.设置深度测试函数
4.更新深度缓冲区
1.创建深度缓冲区
Direct3D初始化四步曲知识,四步曲之三,其实从头到尾其实就是在填充一个D3DPRESENT_PARAMETERS结构体,下面我们先贴出这个结构体的原型:
- typedef struct D3DPRESENT_PARAMETERS {
- UINT BackBufferWidth;
- UINT BackBufferHeight;
- D3DFORMAT BackBufferFormat;
- UINT BackBufferCount;
- D3DMULTISAMPLE_TYPE MultiSampleType;
- DWORD MultiSampleQuality;
- D3DSWAPEFFECT SwapEffect;
- HWND hDeviceWindow;
- BOOL Windowed;
- BOOL EnableAutoDepthStencil;
- D3DFORMAT AutoDepthStencilFormat;
- DWORD Flags;
- UINT FullScreen_RefreshRateInHz;
- UINT PresentationInterval;
- } D3DPRESENT_PARAMETERS,*LPD3DPRESENT_PARAMETERS;
其中的第十和第十一个参数是我们今天深度测试相关的。
◆第十个参数,BOOL 类型的EnableAutoDepthStencil,表示Direct3D是否为应用程序自动管理深度缓存,这个成员为TRUE的话,表示需要自动管理深度缓存,这时候就需要对下一个成员AutoDepthStencilFormat进行相关像素格式的设置。
◆第十一个参数,D3DFORMAT类型的AutoDepthStencilFormat,上面刚介绍过,如果我们把EnableAutoDepthStencil成员设为TRUE的话,在这里就需要指定AutoDepthStencilFormat的深度缓冲的像素格式。具体格式可以在结构体D3DFORMAT中进行选取。我们列举一些可以选取的值:
D3DFMT_D16 深度缓存用16位存储每个像素的深度值
D3DFMT_D24X8 深度缓存用24位存储每个像素的深度值
D3DFMT_D32深度缓存用32位存储每个像素的深度值
想要在之后绘制时使用深度测试的话,在这里就要把第十个参数EnableAutoDepthStencil设为true,表示让Direct3D创建并自行管理一个深度缓冲区。而第十一个参数AutoDepthStencilFormat用于设置一下深度缓存的格式,16位,24位,32位等等格式任选。
2.开启深度测试
这一步的主角依旧是我们的老朋友SetRenderState,这个函数的一个参数可以取各式各样的渲染状态和类型。
先看一下我们正在讲解的第二步,开启深度测试。自然就是给SetRenderState两个参数取不同的值就可以了。将第一个参数设为D3DRS_ZENABLE,表示第二个参数将对深度测试的开启或者关闭进行设置,第二个参数设为TRUE或者FALSE,表示开启或者关闭深度测试。
我们这步的目的当然就是要开启深度测试,也就是这样写:
- g_pd3dDevice->SetRenderState(D3DRS_ZENABLE,true); //开启深度测试
当然,要关闭深度测试的话,把第二个参数取false就可以了:
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE,false); //关闭深度测试
3.设置深度测试函数
还是那个神通广大的SetRenderState函数。对应于这一步的任务,我们把它的第一个参数设为D3DRS_ZFUNC,第二个参数设为想要进行使用的深度测试函数,在D3DCMPFUNC枚举类型中取值,这个D3DCMPFUNC枚举类型我们可以在MSDN中查到定义如下
- typedef enum D3DCMPFUNC {
- D3DCMP_NEVER = 1,
- D3DCMP_LESS = 2,
- D3DCMP_EQUAL = 3,
- D3DCMP_LESSEQUAL = 4,
- D3DCMP_GREATER = 5,
- D3DCMP_NOTEQUAL = 6,
- D3DCMP_GREATEREQUAL = 7,
- D3DCMP_ALWAYS = 8,
- D3DCMP_FORCE_DWORD = 0x7fffffff
- } D3DCMPFUNC, *LPD3DCMPFUNC;
下面我们通过一个表格,对这些枚举类型中的成员进行讲解说明:
枚举类型值 |
精析 |
D3DCMP_NEVER |
深度测试函数总是返回FALSE |
D3DCMP_LESS |
测试点深度值小于深度缓冲区中相应值时,返回TRUE,为默认值 |
D3DCMP_QUAL |
测试点深度值等于深度缓冲区中相应值时,返回TRUE |
D3DCMP_LESSEQUAL |
测试点深度值大于等于深度缓冲区中相应值时,返回TRUE |
D3DCMP_GREATER |
测试点深度值大于深度缓冲区中相应值时,返回TRUE |
D3DCMP_NOTEQUAL |
测试点深度值不等于深度缓冲区中相应值时,返回TRUE |
D3DCMP_GREATEREQUAL |
测试点深度值大于等于深度缓冲区中相应值时,返回TRUE |
D3DCMP_ALWAYS |
深度测试函数总是返回TRUE |
D3DCMP_FORCE_DWORD |
这个枚举值一般不用,保证将D3DCMPFUNC枚举类型编译为32位 |
一般我们都将深度测试函数设为D3DCMP_LESS,表示当测试点深度值小于深度缓冲区中相应值时,通过测试并绘制相关像素,这样没有被遮挡的物体才显示,被遮挡住的物体是不显示的。
这步里面也就是一句SetRenderState代码:
- g_pd3dDevice->SetRenderState(D3DRS_ZFUNC,D3DCMP_LESS);
4.更新深度缓冲区
配合第三步设置的深度测试函数,还需要设置深度测试成功时对深度缓冲区如何操作,是保持原来的深度值“按兵不动”呢,还是用当前像素的深度值来更新对应的数值“与时俱进”。
还是那个神通广大的SetRenderState函数。对应于这一步的任务,我们把它的第一个参数设为D3DRS_ZWRITEENABLE,表示在第二个参数里面将对深度缓冲区更改与否做出选择。第二个参数设为TURE的话,表示深度测试成功之后,用当前像素的深度值更新深度缓冲区对应的数值。第二个参数设为TURE是设置更新深度缓冲区时最常用的设置,同时也是默认值。反之,第二个参数设为FALSE,则表示尽管深度测试成功,还是不更新深度缓冲区对应的数值。
代码书写方面,就是这样写:
- g_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE,true); //深度测试成功后,更新深度缓存