DirectX入门知识点(4)
关于凹凸映射的原理请参阅凹凸映射(Bump Map)实现原理。
凹凸纹理映射是一种纹理混合方法,它可以创建三维物体复杂的纹理外观表面。普通的纹理映射只能模拟比较平滑的三维物体表面,难以显示表面高低起伏、凹凸不平的效果。凹凸纹理映射能够通过一张表示物体表面凹凸程度的高度图(称为凹凸纹理),对另一张表示物体表面环境映射的纹理图的纹理坐标进行相应的干扰,经过干扰的纹理坐标将应用于环境映射,从而产生凹凸不平的显示效果。凹凸纹理映射通常由三张纹理映射图组成,第一张纹理图表示物体表面原始纹理颜色,第二张凹凸纹理图表示物体表面凹凸的高度起伏值,用来对下一张环境纹理图坐标进行干扰,第三张纹理图表示周围镜面反射或漫反射光照的环境光照映射图。
Direct3D的凹凸纹理被用来表示物体表面相邻像素的高度差,它的每个纹理元素由表示水平相邻像素高度差的Du、表示垂直相邻像素高度差的Dv以及表示该点亮度的L组成(某些凹凸纹理像素格式可以不包含L)
凹凸纹理映射通常使用3层纹理:物体原始纹理、由原始纹理高度图生成的凹凸纹理、环境纹理,对应于多层纹理混合的0、1、2层。指定当前纹理层状态为D3DTOP_BUMPENVMAP或D3DTOP_BUMPENVMAPLUMINANCE可设置当前纹理层为凹凸纹理,例如:
pd3dDevice->SetTexture(1, g_bump_map_texture);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAP);
或
pd3dDevice->SetTexture(1, g_bump_map_texture);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAPLUMINANCE);
纹理状态D3DTOP_BUMPENVMAP和D3DTOP_BUMPENVMAPLUMINANCE表示两种不同的凹凸纹理映射方法。纹理状态D3DTOP_BUMPENVMAPLUMINANCE表示在凹凸纹理中包含凹凸纹理亮度值L,L将与下一纹理层的纹理颜色相乘作为最后输出的纹理颜色。纹理状态D3DTOP_BUMPENVMAP默认亮度值L为1,即不改变下一纹理层的纹理颜色。
立体纹理(volume texture)是一组应用到二维图元(如一个三角形或一条直线)的三维纹理元素的集合,可以使用立体纹理实现一些特殊效果,如迷雾、爆炸等。当对一个图元使用立体纹理时,它的每个顶点都需要一组三元纹理坐标。当绘制该图元时,它中间的每个像素都将用立体纹理中的一些纹理元素的颜色值进行填充,这与二维纹理映射的情况相似。
我们需要在灵活顶点格式中指定每个顶点都需要三个纹理坐标,如下所示:
struct sCustomVertex
{
float x, y, z;
float u, v, w;
};
#define D3DFVF_CUSTOM_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0))
在Direct3D中,三维物体的显示是通过网格模型来实现的,显示三维物体的关键在于生成该网格模型。三维文本也不例外,显示三维文本同样需要该文本所对应的网格模型。 Direct3D为此提供了功能库函数D3DXCreateText(),通过它可以方便地创建一个包含具体文本的网格模型
HDC hdc = CreateCompatibleDC(NULL);
if(hdc == NULL)
return false;
HFONT hfont = CreateFont(0, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial");
SelectObject(hdc, hfont);
D3DXCreateText(g_device, hdc, L"三维字体", 0.001f, 0.4f, &g_text_mesh, NULL, NULL);
DeleteObject(hfont);
DeleteDC(hdc);
创建好文本的网格模型之后,就可以使用ID3DXMesh的接口函数DrawSubset()将其绘制出来,在绘制之前,需要注意设置合适的世界矩阵,这时虽然是绘制三维文本,但实质上就是绘制一个三维物体,所以为三维文本设置世界矩阵是必不可少的。
使用D3DXCreateText()函数为文本创建网格模型时,网格模型的原点在左下方,所以需要对文本网格模型进行平移,使其基本上显示在窗口的中央。
绘制复杂的三维场景时,不可避免地会出现物体间的相互遮挡,在这种情况下,为了正确地绘制场景需要使用深度测试。半透明物体的绘制不同于不透明物体,Direct3D通过alpha混合实现半透明物体的绘制。深度测试可以简化复杂场景的绘制,alpha混合可以使绘制的三维场景更完整、更逼真。
在复杂的场景中,通常有多个物体需要绘制,这些物体之间通常会存在遮挡关系,离观察点较远的物体会因为近处物体的者的遮挡而不可见或只有部分可见,Direct3D图形系统提供了深度测试功能来实现这种效果。
要理解深度测试,首先需要理解深度缓冲区。深度缓冲区是Direct3D用来存储绘制到屏幕上的每个像素点的深度信息的一块内存缓冲区。当Direct3D将一个场景渲染到目标表面上时,它使用深度缓冲区来决定光栅化后各个多边形的像素的前后遮挡关系,最终决定哪个颜色值被绘制出来。也就是说,Direct3D通过比较当前绘制的像素点的深度和对应深度缓冲区的点的深度值来决定是否绘制当前像素。如果深度测试结果为TRUE,则绘制当前像素,并用当前像素点的深度来更新深度缓冲区,反之则不予绘制。通常情况下,深度缓冲区对应于屏幕大小的一块二维区域。
对一个启用了深度缓冲区的场景进行光栅化操作时,渲染表面上的每个点都要进行深度测试。在深度测试开始时,深度缓冲区的深度值被设置为该场景可能出现的最大值(在IDevice3DDevice9::Clear中的深度模板缓冲中设置),渲染表面上的颜色值被设置为背景颜色值。然后测试场景内即将绘制的每个多边形,看它是否小于存储在深度缓冲区中的深度值,如果该多边形的深度值更小,则该深度值被更新到深度缓冲区中,并将渲染表面上当前点的颜色值替换为该多边形的颜色。如果多边形在这一点的深度值更大,将继续测试列表中的下一个多边形。
若要在Direct3D图形程序中应用深度测试,首先必须在创建Direct3D渲染设备时创建深度缓冲区,示例代码如下:
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE; // 表示由Direct3D创建并管理一个深度缓冲区
d3dpp.AutoDepthStencilFormat = D3DFMT_D16; // 表示深度缓冲区中每一个像素的深度值由16位的二进制数表示
if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_device)))
{
return false;
}
深度缓冲区随Direct3D渲染设备创建好后,调用Direct3D渲染状态设置函数IDirect3DDevice9::SetRenderState(),将第一个参数设为D3DRS_ZENABLE,第二个参数设为TRUE,激活深度测试:
g_device->SetRenderState(D3DRS_ZENABLE, TRUE);
通常情况下,深度测试函数设置为D3DCMP_LESS,表示当测试点深度值小于深度缓冲区中相应值时,通过深度测试并绘制相关像素,这样没有被遮挡的物体才显示,而被遮挡的物体就不显示。示例代码如下:
g_device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS);
// 左手系?深度数值越小,越靠向观察者?
设置了深度测试函数后,还需要设置深度测试成功时对深度缓冲区如何操作,是保持原来的深度值,还是用当前像素的深度值更新对应的数值。
g_device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
表示如果通过测试,则用当前像素的深度值更新深度缓冲区中对应的数值,这是最常用的设置,也是默认设置。
Alpha混合原理
通过定义一个表示物体半透明度的alpha值和一个半透明计算公式,可以将要绘制的物体颜色与颜色缓冲区中存在的颜色相混合,从而绘制出具有半透明效果的物体。Direct3D计算alpha颜色混合的方法如下:
color = (RGBsrc * Ksrc) OP (RGBdst * Kdst)
其中color表示alpha混合后的颜色值,RGBsrc表示源颜色值,即将要绘制的图元的颜色值;Ksrc表示源混合系数,通常赋值为表示半透明程度的alpha值,也可以是属于枚举类型D3DBLEND的任意值,用来和RGBsrc相乘。RGBdst表示目标颜色值,即当前颜色缓冲区中的颜色值,Kdst表示目标混合系数,可以是属于枚举D3DBLEND的任意值,用来和RGBdst相乘。OP表示源计算结果与颜色缓冲区计算结果的混合方法,默认状态下OP为D3DBLEND_ADD,即源计算结果与颜色缓冲区计算结果相加。
图形显示中,对alpha混合最普遍的用法是:把Ksrc赋值为D3DBLEND_SRCALPHA,即当前绘制像素的alpha值;把Kdst赋值为D3DBLEND_INVSRCALPHA,即1减去当前绘制像素的alpha值;把OP赋值为D3DBLEND_ADD,使源计算结果和颜色缓冲区计算结果相加,这样一来,alpha混合颜色的公式变为:
color = (RGBsrc * Ksrc) + (RGBdst * Kdst)
上面的设置可以较好地模拟大多数半透明物体的效果。
启用Alpha混合
想要绘制半透明物体,首先需要激活Direct3D的Alpha混合运算,调用Direct3D渲染状态设置函数IDirect3DDevice9:::SetRenderState(),将第一个参数设置为D3DRS_ALPHABLENDENABLE,第二个参数设置为TRUE,可以激活Alpha混合,代码如下:
g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
由于Alpha混合是当前绘制的像素颜色与颜色缓冲区中存在的颜色的混合运算,因此,在绘制半透明物体前,必须保证位于半透明物体后的物体先于半透明物体绘制,也就是说,先绘制不透明物体,再绘制半透明物体。
(Alpha对绘制顺序要求很严!)
Alpha源混合系数通常设置为D3DBLEND_SRCALPHA,即当前绘制像素的alpha值。目标混合系数设置为D3DBLEND_INVSRCALPHA,即1减去当前绘制像素的alpha值。那么当前绘制像素的alpha值又是如何得到的呢?如果没有使用材质和纹理,当前绘制像素的alpha值来自每个顶点颜色设置的alpha值;如果使用光照和材质,则当前像素的alpha值来自物体表面材质;如果为物体表面使用了纹理,则alpha值还与纹理有关。
如果在程序中直接指定每个顶点的颜色,则可以直接给出每个顶点颜色的 alpha值,可以在定义顶点时直接声明该顶点的alpha值,也可以在程序运行时动态地修改顶点的alpha值。有了顶点的alpha值,渲染对象中每个像素的alpha值由该对象的alpha值和着色模式决定。当着色模式为FLAT着色模式时,构成对象的各个多边形中所有像素的alpha都等于该多边形的第一个顶点的alpha值。当着色模式为GOURAUD着色模式时,每个多边形面上的像素的alpha值由它的各个顶点的alpha值进行线性插值得到的。
材质Alpha
顶点alpha是没有使用光照和材质的情况,如果对场景内的物体添加光照和材质而没有添加纹理时,顶点alpha值取决于材质属性中漫反射颜色的alpha系数和灯光颜色中的alpha系数,顶点alpha值是根据光照计算得到的。顶点光照计算是分别针对红、绿、蓝和alpha进行的,其中alpha光照计算的结果就是顶点的alpha值。有了顶点的alpha值就可根据着色模式计算出每个像素的alpha值,
纹理Alpha
当对物体表面使用了纹理之后,像素的alpha值就是纹理alpha混合之后的值,所以这又取决于纹理的alpha混合方法,纹理alpha混合方法决定了纹理alpha混合之后的alpha值是取自材质,还是取自纹理,或者取自二者的某种运算。像素alpha值的具体计算过程是这样的,首先得到顶点alpha值,顶点alpha值可能是直接指定的,也可能是光照计算得到,然后根据着色模式对顶点alpha值进行插值,得到的结果再根据纹理alpha混合方法和纹理采样得到的alpha值进行指定的运算,得到最终每个像素的alpha值。
透过那些透明度非常高的物体看其他物体,例如透过几乎完全透明的玻璃看其他物体,会感到玻璃好像不存在,在三维图形程序中渲染时就可以不渲染这些透明度非常高的物体,从而可以提高渲染速度,这可以通过alpha测试来实现。
alpha测试根据当前像素是否满足alpha测试条件(即是否达到一定的透明度)来控制是否绘制该像素,图形程序应用alpha测试可以有效地屏蔽某些像素颜色。与alpha混合相比,alpha测试不将当前像素的颜色与颜色缓冲区中像素的颜色混合,像素要么完全不透明,要么完全透明。由于无需进行颜色缓冲区的读操作和颜色混合,因此alpha测试在速度上要优于alpha混合。
alpha测试通过激活渲染状态D3DRS_ALPHATESTENABLE来设置,示例代码如下:
g_device->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
渲染状态D3DRS_ALPHAREF用来设置alpha测试的参考值,alpha测试函数比较当前绘制的像素的alpha值和参考值,如果返回TRUE,则通过测试并绘制像素,反之不予绘制。参考值的取值范围是0x00000000到0x000000ff。
渲染状态D3DRS_ALPHAFUNC用来设置alpha测试函数,alpha测试函数属于D3DCMPFUNC枚举类型,默认状态为D3DCMP_ALWAYS。下列代码设置alpha测试函数为D3DCMP_GREATER,表示测试点像素的alpha值大于设置的alpha参考值时则返回TRUE:
g_device->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
g_device->SetRenderState(D3DRS_ALPHAREF, 0x00000081);
g_device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
已知蓝色玻璃的alpha值为浮点值0.5f,等价于两位16进制数0x80,小于程序中设置的alpha测试参考值0x81,并且alpha测试函数被设为D3DCMP_GREATER,所以蓝色玻璃的颜色不会被绘制出来。
调用D3DXCreateMesh()函数创建了网格模型对象后,还需要为其载入模型数据,而载入模型数据是比较复杂的。所以在大多数情况下,不直接调用该函数,它被封装在Direct3D扩展实用库函数中,由Direct3D在内部完成网格模型对象的创建和模型数据的载入操作。(例如D3DXLoadMeshFromX函数)复杂的三维模型实际上是由许许多多的多边形构成的,所以首先需要得到这些构成模型的多边形。使用Direct3D功能扩展库函数D3DXLoadMeshFromX(),可以从.X文件中提取多边形信息(包括顶点坐标、颜色、法向量和纹理信息等),生成网格模型。这个函数的返回值LPD3DXBUFFER因数据操作的方便性而诞生,它的好处是可以存储顶点位置坐标、材质、纹理等多种类型的Direct3D数据,而不必对每种数据声明一种函数接口类型。可使用接口函数ID3DXBuffer::GetBufferPointer()获取缓冲区中的数据,使用ID3DXBuffer::GetBufferSize()获取缓冲区数据大小。
一个三维网格模型通常是由几个子模型组成的,在制作模型时通常为每个子模型分别设置材质和纹理,所以这些子模型就可能使用不同的材质和纹理,因此在Direct3D程序就需要为所有的子模型分别保存材质和纹理。此外,因为每个子模型可能具有不同的材质和纹理,所以在渲染三维模型时也需要逐个子模型分别进行渲染。
渲染网格模型
网格模型接口ID3DXMesh实际上是三维物体的顶点缓冲区的集合,它将创建顶点缓冲区、定义灵活顶点格式和绘制顶点缓冲区等功能封装在一个COM对象里,极大地方便了三维物体的绘制。对于以ID3DXMesh表示的三维物体,可以遍历它所有的顶点缓冲区,按照相应的顶点格式将它们分别渲染,也可以直接调用它的接口函数ID3DXMesh::DrawSubset()绘制图形。
当Direct3D渲染一个图元时,必须将它通过坐标变换映射到二维屏幕上。如果图元有纹理,Direct3D就需要用纹理来产生图元的二维渲染图像上每个像素的颜色。对于图元在二维屏幕上图像的每个像素来说,都必须从纹理中获得一个颜色,从纹理中为每个像素获取颜色的过程称为纹理过滤(texture filtering)。
光照计算模型
在真实世界中,光线在到达眼睛之前经过了物体表面的多次反射,每次反射时,物体表面都会吸收一些光,有些被随机反射扩散出去,其余的到达下一个物体的表面或眼睛。真实世界中光线反射的效果就是光线跟踪算法需要模拟实现的。尽管光线跟踪算法能够创建非常逼真的与自然界中观察到极为相似的景象,但是还没有实时程序能够完成这些运算。考虑到实时渲染的需要,Direct3D使用更简单的方法进行光照计算。Direct3D光照计算模型包括4种:环境光、漫反射光、镜面反射光和自发光。它们的结合能灵活高效地解决三维图形程序中的光照问题。
用自发光属性创建的材质并不发射出能被场景内其他对象反射的光,也就是说,它发出的光不参与光运算。为了实现反射光,必须在场景内添加额外的灯光。
环境光、自发光、漫反射光的计算结果作为顶点的漫反射颜色输出,镜面反射光的计算结果作为顶点的镜面反射光颜色输出。
光源发出光的三种颜色,与当前材质的相应部分相作用,生成最终用于渲染的颜色。光源漫反射颜色与当前材质的漫反射属性作用,光源镜面反射颜色与当前材质的镜面反射属性作用等。
因为镜面反射的计算量很大,所以Direct3D在默认状态下不进行镜面反射运算。如果想得到镜面反射效果,可以先设置好结构体D3DLIGHT9的Specular成员和物体表面材质结构体D3DMATERIAL9的Specular和Power成员,然后通过下面的代码激活镜面反射运算。
g_device->SetRenderState(D3DRS_SPECULARENABLE, TRUE);
如果需要进行漫反射或镜面反射运算,那么在顶点缓冲区中必须包含顶点的法向量信息,这是因为Direct3D在进行光照运算时,要用到顶点法向量。
对于光照计算,光源和材质两者缺一不可,物体表面材质属性决定了它能反射什么颜色的光线以及能反射多少,在Direct3D中,物体表面材质属性由结构体D3DMATERIAL9定义
场景中的环境光有两个来源:一是通过渲染状态设置的全局环境光,二是通过每个光源中的环境光属性设置的环境光。建议通过渲染状态设置一个整体上的环境光,对于场景中的各个光源不设置其环境光属性,因为在同一个场景中,对于每个物体其接受到的环境光应当相同,所以通过渲染状态设置一个整体上的环境光比较方便,也符合实际情况。
在Direct3D中,光源和材质是互不分离、相互作用的两部分,光源是相对于整个场景的,而材质是相对于每个物体的。两者相互作用,共同决定最终的渲染结果,这样虽然灵活但不易控制,所以光源和物体表面材质的设置应尽量符合现实情况。例如,可将光源设为白光,将各个物体材质颜色设为真实颜色,当然为了得到特殊的效果,可以在某些方面进行夸张。
如果在创建资源时使用D3DPOOL_DEFAULT内存池,则Direct3D通常会将资源保存到显存或AGP内存中,以得到更高的性能,但是,Direct3D设备丢失后,在调用IDirect3DDevice9::Reset()恢复设备前,必须释放用D3DPOOL_DEFAULT创建的资源,并且在恢复设备后必须重新创建这些资源。
使用D3DPOOL_MANAGED标志创建的资源称为托管资源。对于托管资源,Direct3D会自动在系统内存中进行备份,当设备丢失时,Direct3D将自动释放这类资源;恢复设备时也不需要重新创建,Direct3D会自动从系统内存恢复这些资源。
对于那些设备不经常访问的资源,通常使用D3DPOOL_SYSTEMMEM内存池标志创建。这些资源将常驻系统内存,所以在设备丢失后,这类资源不会丢失,恢复设备时也不需要重新创建。
D3DPOOL_DEFAULT和D3DPOOL_MANAGED资源是最常使用的两种资源,要牢记这两种资源在设备丢失和恢复时的区别。不能为同一个对象使用多种内存池,当为一个资源选定一个内存池后,该内存池就不能再改变了。
场景提交概述
场景提交即将在后台缓冲区绘制好的场景提交到前台缓冲区,从而在屏幕上显示出来。提交接口函数是一组控制特定的渲染设备状态的方法,这些设备影响显示器的显示。
(1)前台缓冲区:这是一块由显卡转换来的矩形存储区,这块矩形存储区的内容显示在显示器或其他输出设备上。
(2)后台缓冲区:后台缓冲区是一个表面,其内容可以提交到前台缓冲区。
(3)交换链:一组后台缓冲区集合,它们被顺序地提交到前台缓冲区。一般情况下,一个全屏交换链通过翻转设备驱动接口(DDI)来提交随后的显示内容,窗口交换链通过位块传送DDI提交显示内容。
前台缓冲区不能直接在Direct3D API中使用。所以,应用程序不能对前台缓冲区进行锁定或渲染。DirectX 9.0应用程序中没有主表面的概念,不能创建一个带有主表面的对象。
窗口模式下的多视口(视区)
Direct3D设备对象拥有并控制自己的交换链,此外,应用程序可以使用函数IDirect3DDevice9::CreateAdditionalSwapChain()创建附加交换链,用来在同一个设备中提交多个视口。一般地说,应用程序为每个视口创建一个交换链,每个交换链对应一个特定的视口。应用程序在每个视口的后台缓冲区内渲染图形,然后用函数 IDirect3DDevice9::Present()将它们分别提交。注意:对于任何Direct3D设备对象,一次只能有一个交换链用于全屏显示。
多显示器操作
当一个设备被成功设置为全屏操作时,创建该设备的Direct3D对象被标识为拥有系统的所有显卡。这种状态称为独占模式(exclusive mode),也就是说,Direct3D对象为独占模式。独占模式是指,这时其他所有的Direct3D对象创建的设备都不能进行全屏操作,也不能申请资源空间。此外,当一个对象是独占模式时,所有未在全屏模式下的设备都将被设为丢失状态。当Direct3D对象的最后一个全屏设备被设置为窗口模式或被销毁时,独占模式被取消。
当一个Direct3D设备是独占模式时,设备将被分为两大类,第一类设备有下列属性:
(1)它们都是由同一个创建全屏设备的Direct3D对象创建的。
(2)因为设备是全屏的,它们具有同一个焦点窗口。
(3)它们代表不同于任何全屏设备的显卡。
对于这种类型的设备,不用关心它们能否被重新设置或创建,因为它们不处于丢失状态。甚至,这种类型的设备都可以被设置为全屏状态。
不属于第一类的设备即由其他Direct3D对象创建的设备,或和当前全屏设备不具有相同的焦点窗口,或和当前全屏设备使用不同的显卡。这一类Direct3D设备不能被重新设置,将一直处于丢失状态,直至当前全屏设备的独占模式取消。这样一来,一个多显示器应用程序可在全屏模式下拥有多个设备,但是,这些设备必须由相同的Direct3D对象创建,对应于不同的物理显卡并且共享同一个焦点窗口。
操作深度缓冲区
深度缓冲区与设备相关。当应用程序设置渲染目标时,需要访问深度缓冲区。可以使用函数IDirect3DDevice9::GetDepthStencilSurface()和IDirect3DDevice9::SetDepthStencilSurface()来操作深度缓冲区。
访问前台缓冲区
可以通过函数IDirect3DDevice9::GetFrontBufferData()访问前台缓冲区,这是得到一个反锯齿场景屏幕快照的唯一方法。
图形反锯齿(antialiasing)
图形像素在颜色缓冲区或屏幕中以一个二维坐标(x, y)表示当前位置。如果实际计算的像素值是浮点数,则将被转换为整数坐标显示,这种光栅化的处理方法可能使图形出现锯齿形外观。图形学中称这种由于采样频率不足而造成的失真为锯齿(alisasing),Direct3D采用图形反锯齿(通过多重采样)来改善图形的锯齿效果,增加图形边缘的平滑度。
全屏幕显示
游戏程序通常都是运行在全屏幕模式下,进行全屏显示的关键是使用全屏显示的渲染设备。创建全屏显示模式渲染设备同窗口模式渲染设备基本相同,区别是将d3dpp.Windowed设置为FALSE,告诉Direct3D系统,将要创建的是全屏模式渲染设备。此外,还需要明确指定后台缓冲区的大小和格式,这和创建窗口模式渲染设备是不相同的,在创建窗口模式渲染设备时可将后台缓冲区格式设置为D3DFMT_UNKNOWN,后台缓冲区大小也可取默认值,而在创建全屏模式渲染设备时这些都需要明确指定。
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
D3DDISPLAYMODE display_mode;
g_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &display_mode);
d3dpp.Windowed = FALSE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferWidth = display_mode.Width;
d3dpp.BackBufferHeight = display_mode.Height;
d3dpp.BackBufferFormat = display_mode.Format;
if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_device)))
{
return false;
}
凹凸纹理映射是一种纹理混合方法,它可以创建三维物体复杂的纹理外观表面。普通的纹理映射只能模拟比较平滑的三维物体表面,难以显示表面高低起伏、凹凸不平的效果。凹凸纹理映射能够通过一张表示物体表面凹凸程度的高度图(称为凹凸纹理),对另一张表示物体表面环境映射的纹理图的纹理坐标进行相应的干扰,经过干扰的纹理坐标将应用于环境映射,从而产生凹凸不平的显示效果。凹凸纹理映射通常由三张纹理映射图组成,第一张纹理图表示物体表面原始纹理颜色,第二张凹凸纹理图表示物体表面凹凸的高度起伏值,用来对下一张环境纹理图坐标进行干扰,第三张纹理图表示周围镜面反射或漫反射光照的环境光照映射图。
Direct3D的凹凸纹理被用来表示物体表面相邻像素的高度差,它的每个纹理元素由表示水平相邻像素高度差的Du、表示垂直相邻像素高度差的Dv以及表示该点亮度的L组成(某些凹凸纹理像素格式可以不包含L)
凹凸纹理映射通常使用3层纹理:物体原始纹理、由原始纹理高度图生成的凹凸纹理、环境纹理,对应于多层纹理混合的0、1、2层。指定当前纹理层状态为D3DTOP_BUMPENVMAP或D3DTOP_BUMPENVMAPLUMINANCE可设置当前纹理层为凹凸纹理,例如:
pd3dDevice->SetTexture(1, g_bump_map_texture);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAP);
或
pd3dDevice->SetTexture(1, g_bump_map_texture);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BUMPENVMAPLUMINANCE);
纹理状态D3DTOP_BUMPENVMAP和D3DTOP_BUMPENVMAPLUMINANCE表示两种不同的凹凸纹理映射方法。纹理状态D3DTOP_BUMPENVMAPLUMINANCE表示在凹凸纹理中包含凹凸纹理亮度值L,L将与下一纹理层的纹理颜色相乘作为最后输出的纹理颜色。纹理状态D3DTOP_BUMPENVMAP默认亮度值L为1,即不改变下一纹理层的纹理颜色。
立体纹理(volume texture)是一组应用到二维图元(如一个三角形或一条直线)的三维纹理元素的集合,可以使用立体纹理实现一些特殊效果,如迷雾、爆炸等。当对一个图元使用立体纹理时,它的每个顶点都需要一组三元纹理坐标。当绘制该图元时,它中间的每个像素都将用立体纹理中的一些纹理元素的颜色值进行填充,这与二维纹理映射的情况相似。
我们需要在灵活顶点格式中指定每个顶点都需要三个纹理坐标,如下所示:
struct sCustomVertex
{
float x, y, z;
float u, v, w;
};
#define D3DFVF_CUSTOM_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0))
在Direct3D中,三维物体的显示是通过网格模型来实现的,显示三维物体的关键在于生成该网格模型。三维文本也不例外,显示三维文本同样需要该文本所对应的网格模型。 Direct3D为此提供了功能库函数D3DXCreateText(),通过它可以方便地创建一个包含具体文本的网格模型
HDC hdc = CreateCompatibleDC(NULL);
if(hdc == NULL)
return false;
HFONT hfont = CreateFont(0, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial");
SelectObject(hdc, hfont);
D3DXCreateText(g_device, hdc, L"三维字体", 0.001f, 0.4f, &g_text_mesh, NULL, NULL);
DeleteObject(hfont);
DeleteDC(hdc);
创建好文本的网格模型之后,就可以使用ID3DXMesh的接口函数DrawSubset()将其绘制出来,在绘制之前,需要注意设置合适的世界矩阵,这时虽然是绘制三维文本,但实质上就是绘制一个三维物体,所以为三维文本设置世界矩阵是必不可少的。
使用D3DXCreateText()函数为文本创建网格模型时,网格模型的原点在左下方,所以需要对文本网格模型进行平移,使其基本上显示在窗口的中央。
绘制复杂的三维场景时,不可避免地会出现物体间的相互遮挡,在这种情况下,为了正确地绘制场景需要使用深度测试。半透明物体的绘制不同于不透明物体,Direct3D通过alpha混合实现半透明物体的绘制。深度测试可以简化复杂场景的绘制,alpha混合可以使绘制的三维场景更完整、更逼真。
在复杂的场景中,通常有多个物体需要绘制,这些物体之间通常会存在遮挡关系,离观察点较远的物体会因为近处物体的者的遮挡而不可见或只有部分可见,Direct3D图形系统提供了深度测试功能来实现这种效果。
要理解深度测试,首先需要理解深度缓冲区。深度缓冲区是Direct3D用来存储绘制到屏幕上的每个像素点的深度信息的一块内存缓冲区。当Direct3D将一个场景渲染到目标表面上时,它使用深度缓冲区来决定光栅化后各个多边形的像素的前后遮挡关系,最终决定哪个颜色值被绘制出来。也就是说,Direct3D通过比较当前绘制的像素点的深度和对应深度缓冲区的点的深度值来决定是否绘制当前像素。如果深度测试结果为TRUE,则绘制当前像素,并用当前像素点的深度来更新深度缓冲区,反之则不予绘制。通常情况下,深度缓冲区对应于屏幕大小的一块二维区域。
对一个启用了深度缓冲区的场景进行光栅化操作时,渲染表面上的每个点都要进行深度测试。在深度测试开始时,深度缓冲区的深度值被设置为该场景可能出现的最大值(在IDevice3DDevice9::Clear中的深度模板缓冲中设置),渲染表面上的颜色值被设置为背景颜色值。然后测试场景内即将绘制的每个多边形,看它是否小于存储在深度缓冲区中的深度值,如果该多边形的深度值更小,则该深度值被更新到深度缓冲区中,并将渲染表面上当前点的颜色值替换为该多边形的颜色。如果多边形在这一点的深度值更大,将继续测试列表中的下一个多边形。
若要在Direct3D图形程序中应用深度测试,首先必须在创建Direct3D渲染设备时创建深度缓冲区,示例代码如下:
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE; // 表示由Direct3D创建并管理一个深度缓冲区
d3dpp.AutoDepthStencilFormat = D3DFMT_D16; // 表示深度缓冲区中每一个像素的深度值由16位的二进制数表示
if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_device)))
{
return false;
}
深度缓冲区随Direct3D渲染设备创建好后,调用Direct3D渲染状态设置函数IDirect3DDevice9::SetRenderState(),将第一个参数设为D3DRS_ZENABLE,第二个参数设为TRUE,激活深度测试:
g_device->SetRenderState(D3DRS_ZENABLE, TRUE);
通常情况下,深度测试函数设置为D3DCMP_LESS,表示当测试点深度值小于深度缓冲区中相应值时,通过深度测试并绘制相关像素,这样没有被遮挡的物体才显示,而被遮挡的物体就不显示。示例代码如下:
g_device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS);
// 左手系?深度数值越小,越靠向观察者?
设置了深度测试函数后,还需要设置深度测试成功时对深度缓冲区如何操作,是保持原来的深度值,还是用当前像素的深度值更新对应的数值。
g_device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
表示如果通过测试,则用当前像素的深度值更新深度缓冲区中对应的数值,这是最常用的设置,也是默认设置。
Alpha混合原理
通过定义一个表示物体半透明度的alpha值和一个半透明计算公式,可以将要绘制的物体颜色与颜色缓冲区中存在的颜色相混合,从而绘制出具有半透明效果的物体。Direct3D计算alpha颜色混合的方法如下:
color = (RGBsrc * Ksrc) OP (RGBdst * Kdst)
其中color表示alpha混合后的颜色值,RGBsrc表示源颜色值,即将要绘制的图元的颜色值;Ksrc表示源混合系数,通常赋值为表示半透明程度的alpha值,也可以是属于枚举类型D3DBLEND的任意值,用来和RGBsrc相乘。RGBdst表示目标颜色值,即当前颜色缓冲区中的颜色值,Kdst表示目标混合系数,可以是属于枚举D3DBLEND的任意值,用来和RGBdst相乘。OP表示源计算结果与颜色缓冲区计算结果的混合方法,默认状态下OP为D3DBLEND_ADD,即源计算结果与颜色缓冲区计算结果相加。
图形显示中,对alpha混合最普遍的用法是:把Ksrc赋值为D3DBLEND_SRCALPHA,即当前绘制像素的alpha值;把Kdst赋值为D3DBLEND_INVSRCALPHA,即1减去当前绘制像素的alpha值;把OP赋值为D3DBLEND_ADD,使源计算结果和颜色缓冲区计算结果相加,这样一来,alpha混合颜色的公式变为:
color = (RGBsrc * Ksrc) + (RGBdst * Kdst)
上面的设置可以较好地模拟大多数半透明物体的效果。
启用Alpha混合
想要绘制半透明物体,首先需要激活Direct3D的Alpha混合运算,调用Direct3D渲染状态设置函数IDirect3DDevice9:::SetRenderState(),将第一个参数设置为D3DRS_ALPHABLENDENABLE,第二个参数设置为TRUE,可以激活Alpha混合,代码如下:
g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
由于Alpha混合是当前绘制的像素颜色与颜色缓冲区中存在的颜色的混合运算,因此,在绘制半透明物体前,必须保证位于半透明物体后的物体先于半透明物体绘制,也就是说,先绘制不透明物体,再绘制半透明物体。
(Alpha对绘制顺序要求很严!)
Alpha源混合系数通常设置为D3DBLEND_SRCALPHA,即当前绘制像素的alpha值。目标混合系数设置为D3DBLEND_INVSRCALPHA,即1减去当前绘制像素的alpha值。那么当前绘制像素的alpha值又是如何得到的呢?如果没有使用材质和纹理,当前绘制像素的alpha值来自每个顶点颜色设置的alpha值;如果使用光照和材质,则当前像素的alpha值来自物体表面材质;如果为物体表面使用了纹理,则alpha值还与纹理有关。
如果在程序中直接指定每个顶点的颜色,则可以直接给出每个顶点颜色的 alpha值,可以在定义顶点时直接声明该顶点的alpha值,也可以在程序运行时动态地修改顶点的alpha值。有了顶点的alpha值,渲染对象中每个像素的alpha值由该对象的alpha值和着色模式决定。当着色模式为FLAT着色模式时,构成对象的各个多边形中所有像素的alpha都等于该多边形的第一个顶点的alpha值。当着色模式为GOURAUD着色模式时,每个多边形面上的像素的alpha值由它的各个顶点的alpha值进行线性插值得到的。
材质Alpha
顶点alpha是没有使用光照和材质的情况,如果对场景内的物体添加光照和材质而没有添加纹理时,顶点alpha值取决于材质属性中漫反射颜色的alpha系数和灯光颜色中的alpha系数,顶点alpha值是根据光照计算得到的。顶点光照计算是分别针对红、绿、蓝和alpha进行的,其中alpha光照计算的结果就是顶点的alpha值。有了顶点的alpha值就可根据着色模式计算出每个像素的alpha值,
纹理Alpha
当对物体表面使用了纹理之后,像素的alpha值就是纹理alpha混合之后的值,所以这又取决于纹理的alpha混合方法,纹理alpha混合方法决定了纹理alpha混合之后的alpha值是取自材质,还是取自纹理,或者取自二者的某种运算。像素alpha值的具体计算过程是这样的,首先得到顶点alpha值,顶点alpha值可能是直接指定的,也可能是光照计算得到,然后根据着色模式对顶点alpha值进行插值,得到的结果再根据纹理alpha混合方法和纹理采样得到的alpha值进行指定的运算,得到最终每个像素的alpha值。
透过那些透明度非常高的物体看其他物体,例如透过几乎完全透明的玻璃看其他物体,会感到玻璃好像不存在,在三维图形程序中渲染时就可以不渲染这些透明度非常高的物体,从而可以提高渲染速度,这可以通过alpha测试来实现。
alpha测试根据当前像素是否满足alpha测试条件(即是否达到一定的透明度)来控制是否绘制该像素,图形程序应用alpha测试可以有效地屏蔽某些像素颜色。与alpha混合相比,alpha测试不将当前像素的颜色与颜色缓冲区中像素的颜色混合,像素要么完全不透明,要么完全透明。由于无需进行颜色缓冲区的读操作和颜色混合,因此alpha测试在速度上要优于alpha混合。
alpha测试通过激活渲染状态D3DRS_ALPHATESTENABLE来设置,示例代码如下:
g_device->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
渲染状态D3DRS_ALPHAREF用来设置alpha测试的参考值,alpha测试函数比较当前绘制的像素的alpha值和参考值,如果返回TRUE,则通过测试并绘制像素,反之不予绘制。参考值的取值范围是0x00000000到0x000000ff。
渲染状态D3DRS_ALPHAFUNC用来设置alpha测试函数,alpha测试函数属于D3DCMPFUNC枚举类型,默认状态为D3DCMP_ALWAYS。下列代码设置alpha测试函数为D3DCMP_GREATER,表示测试点像素的alpha值大于设置的alpha参考值时则返回TRUE:
g_device->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
g_device->SetRenderState(D3DRS_ALPHAREF, 0x00000081);
g_device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
已知蓝色玻璃的alpha值为浮点值0.5f,等价于两位16进制数0x80,小于程序中设置的alpha测试参考值0x81,并且alpha测试函数被设为D3DCMP_GREATER,所以蓝色玻璃的颜色不会被绘制出来。
调用D3DXCreateMesh()函数创建了网格模型对象后,还需要为其载入模型数据,而载入模型数据是比较复杂的。所以在大多数情况下,不直接调用该函数,它被封装在Direct3D扩展实用库函数中,由Direct3D在内部完成网格模型对象的创建和模型数据的载入操作。(例如D3DXLoadMeshFromX函数)复杂的三维模型实际上是由许许多多的多边形构成的,所以首先需要得到这些构成模型的多边形。使用Direct3D功能扩展库函数D3DXLoadMeshFromX(),可以从.X文件中提取多边形信息(包括顶点坐标、颜色、法向量和纹理信息等),生成网格模型。这个函数的返回值LPD3DXBUFFER因数据操作的方便性而诞生,它的好处是可以存储顶点位置坐标、材质、纹理等多种类型的Direct3D数据,而不必对每种数据声明一种函数接口类型。可使用接口函数ID3DXBuffer::GetBufferPointer()获取缓冲区中的数据,使用ID3DXBuffer::GetBufferSize()获取缓冲区数据大小。
一个三维网格模型通常是由几个子模型组成的,在制作模型时通常为每个子模型分别设置材质和纹理,所以这些子模型就可能使用不同的材质和纹理,因此在Direct3D程序就需要为所有的子模型分别保存材质和纹理。此外,因为每个子模型可能具有不同的材质和纹理,所以在渲染三维模型时也需要逐个子模型分别进行渲染。
渲染网格模型
网格模型接口ID3DXMesh实际上是三维物体的顶点缓冲区的集合,它将创建顶点缓冲区、定义灵活顶点格式和绘制顶点缓冲区等功能封装在一个COM对象里,极大地方便了三维物体的绘制。对于以ID3DXMesh表示的三维物体,可以遍历它所有的顶点缓冲区,按照相应的顶点格式将它们分别渲染,也可以直接调用它的接口函数ID3DXMesh::DrawSubset()绘制图形。
当Direct3D渲染一个图元时,必须将它通过坐标变换映射到二维屏幕上。如果图元有纹理,Direct3D就需要用纹理来产生图元的二维渲染图像上每个像素的颜色。对于图元在二维屏幕上图像的每个像素来说,都必须从纹理中获得一个颜色,从纹理中为每个像素获取颜色的过程称为纹理过滤(texture filtering)。
光照计算模型
在真实世界中,光线在到达眼睛之前经过了物体表面的多次反射,每次反射时,物体表面都会吸收一些光,有些被随机反射扩散出去,其余的到达下一个物体的表面或眼睛。真实世界中光线反射的效果就是光线跟踪算法需要模拟实现的。尽管光线跟踪算法能够创建非常逼真的与自然界中观察到极为相似的景象,但是还没有实时程序能够完成这些运算。考虑到实时渲染的需要,Direct3D使用更简单的方法进行光照计算。Direct3D光照计算模型包括4种:环境光、漫反射光、镜面反射光和自发光。它们的结合能灵活高效地解决三维图形程序中的光照问题。
用自发光属性创建的材质并不发射出能被场景内其他对象反射的光,也就是说,它发出的光不参与光运算。为了实现反射光,必须在场景内添加额外的灯光。
环境光、自发光、漫反射光的计算结果作为顶点的漫反射颜色输出,镜面反射光的计算结果作为顶点的镜面反射光颜色输出。
光源发出光的三种颜色,与当前材质的相应部分相作用,生成最终用于渲染的颜色。光源漫反射颜色与当前材质的漫反射属性作用,光源镜面反射颜色与当前材质的镜面反射属性作用等。
因为镜面反射的计算量很大,所以Direct3D在默认状态下不进行镜面反射运算。如果想得到镜面反射效果,可以先设置好结构体D3DLIGHT9的Specular成员和物体表面材质结构体D3DMATERIAL9的Specular和Power成员,然后通过下面的代码激活镜面反射运算。
g_device->SetRenderState(D3DRS_SPECULARENABLE, TRUE);
如果需要进行漫反射或镜面反射运算,那么在顶点缓冲区中必须包含顶点的法向量信息,这是因为Direct3D在进行光照运算时,要用到顶点法向量。
对于光照计算,光源和材质两者缺一不可,物体表面材质属性决定了它能反射什么颜色的光线以及能反射多少,在Direct3D中,物体表面材质属性由结构体D3DMATERIAL9定义
场景中的环境光有两个来源:一是通过渲染状态设置的全局环境光,二是通过每个光源中的环境光属性设置的环境光。建议通过渲染状态设置一个整体上的环境光,对于场景中的各个光源不设置其环境光属性,因为在同一个场景中,对于每个物体其接受到的环境光应当相同,所以通过渲染状态设置一个整体上的环境光比较方便,也符合实际情况。
在Direct3D中,光源和材质是互不分离、相互作用的两部分,光源是相对于整个场景的,而材质是相对于每个物体的。两者相互作用,共同决定最终的渲染结果,这样虽然灵活但不易控制,所以光源和物体表面材质的设置应尽量符合现实情况。例如,可将光源设为白光,将各个物体材质颜色设为真实颜色,当然为了得到特殊的效果,可以在某些方面进行夸张。
如果在创建资源时使用D3DPOOL_DEFAULT内存池,则Direct3D通常会将资源保存到显存或AGP内存中,以得到更高的性能,但是,Direct3D设备丢失后,在调用IDirect3DDevice9::Reset()恢复设备前,必须释放用D3DPOOL_DEFAULT创建的资源,并且在恢复设备后必须重新创建这些资源。
使用D3DPOOL_MANAGED标志创建的资源称为托管资源。对于托管资源,Direct3D会自动在系统内存中进行备份,当设备丢失时,Direct3D将自动释放这类资源;恢复设备时也不需要重新创建,Direct3D会自动从系统内存恢复这些资源。
对于那些设备不经常访问的资源,通常使用D3DPOOL_SYSTEMMEM内存池标志创建。这些资源将常驻系统内存,所以在设备丢失后,这类资源不会丢失,恢复设备时也不需要重新创建。
D3DPOOL_DEFAULT和D3DPOOL_MANAGED资源是最常使用的两种资源,要牢记这两种资源在设备丢失和恢复时的区别。不能为同一个对象使用多种内存池,当为一个资源选定一个内存池后,该内存池就不能再改变了。
场景提交概述
场景提交即将在后台缓冲区绘制好的场景提交到前台缓冲区,从而在屏幕上显示出来。提交接口函数是一组控制特定的渲染设备状态的方法,这些设备影响显示器的显示。
(1)前台缓冲区:这是一块由显卡转换来的矩形存储区,这块矩形存储区的内容显示在显示器或其他输出设备上。
(2)后台缓冲区:后台缓冲区是一个表面,其内容可以提交到前台缓冲区。
(3)交换链:一组后台缓冲区集合,它们被顺序地提交到前台缓冲区。一般情况下,一个全屏交换链通过翻转设备驱动接口(DDI)来提交随后的显示内容,窗口交换链通过位块传送DDI提交显示内容。
前台缓冲区不能直接在Direct3D API中使用。所以,应用程序不能对前台缓冲区进行锁定或渲染。DirectX 9.0应用程序中没有主表面的概念,不能创建一个带有主表面的对象。
窗口模式下的多视口(视区)
Direct3D设备对象拥有并控制自己的交换链,此外,应用程序可以使用函数IDirect3DDevice9::CreateAdditionalSwapChain()创建附加交换链,用来在同一个设备中提交多个视口。一般地说,应用程序为每个视口创建一个交换链,每个交换链对应一个特定的视口。应用程序在每个视口的后台缓冲区内渲染图形,然后用函数 IDirect3DDevice9::Present()将它们分别提交。注意:对于任何Direct3D设备对象,一次只能有一个交换链用于全屏显示。
多显示器操作
当一个设备被成功设置为全屏操作时,创建该设备的Direct3D对象被标识为拥有系统的所有显卡。这种状态称为独占模式(exclusive mode),也就是说,Direct3D对象为独占模式。独占模式是指,这时其他所有的Direct3D对象创建的设备都不能进行全屏操作,也不能申请资源空间。此外,当一个对象是独占模式时,所有未在全屏模式下的设备都将被设为丢失状态。当Direct3D对象的最后一个全屏设备被设置为窗口模式或被销毁时,独占模式被取消。
当一个Direct3D设备是独占模式时,设备将被分为两大类,第一类设备有下列属性:
(1)它们都是由同一个创建全屏设备的Direct3D对象创建的。
(2)因为设备是全屏的,它们具有同一个焦点窗口。
(3)它们代表不同于任何全屏设备的显卡。
对于这种类型的设备,不用关心它们能否被重新设置或创建,因为它们不处于丢失状态。甚至,这种类型的设备都可以被设置为全屏状态。
不属于第一类的设备即由其他Direct3D对象创建的设备,或和当前全屏设备不具有相同的焦点窗口,或和当前全屏设备使用不同的显卡。这一类Direct3D设备不能被重新设置,将一直处于丢失状态,直至当前全屏设备的独占模式取消。这样一来,一个多显示器应用程序可在全屏模式下拥有多个设备,但是,这些设备必须由相同的Direct3D对象创建,对应于不同的物理显卡并且共享同一个焦点窗口。
操作深度缓冲区
深度缓冲区与设备相关。当应用程序设置渲染目标时,需要访问深度缓冲区。可以使用函数IDirect3DDevice9::GetDepthStencilSurface()和IDirect3DDevice9::SetDepthStencilSurface()来操作深度缓冲区。
访问前台缓冲区
可以通过函数IDirect3DDevice9::GetFrontBufferData()访问前台缓冲区,这是得到一个反锯齿场景屏幕快照的唯一方法。
图形反锯齿(antialiasing)
图形像素在颜色缓冲区或屏幕中以一个二维坐标(x, y)表示当前位置。如果实际计算的像素值是浮点数,则将被转换为整数坐标显示,这种光栅化的处理方法可能使图形出现锯齿形外观。图形学中称这种由于采样频率不足而造成的失真为锯齿(alisasing),Direct3D采用图形反锯齿(通过多重采样)来改善图形的锯齿效果,增加图形边缘的平滑度。
全屏幕显示
游戏程序通常都是运行在全屏幕模式下,进行全屏显示的关键是使用全屏显示的渲染设备。创建全屏显示模式渲染设备同窗口模式渲染设备基本相同,区别是将d3dpp.Windowed设置为FALSE,告诉Direct3D系统,将要创建的是全屏模式渲染设备。此外,还需要明确指定后台缓冲区的大小和格式,这和创建窗口模式渲染设备是不相同的,在创建窗口模式渲染设备时可将后台缓冲区格式设置为D3DFMT_UNKNOWN,后台缓冲区大小也可取默认值,而在创建全屏模式渲染设备时这些都需要明确指定。
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
D3DDISPLAYMODE display_mode;
g_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &display_mode);
d3dpp.Windowed = FALSE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferWidth = display_mode.Width;
d3dpp.BackBufferHeight = display_mode.Height;
d3dpp.BackBufferFormat = display_mode.Format;
if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_device)))
{
return false;
}