第17章 三维游戏模型的载入
主要是如何从3ds max中导出.X文件,以及如何从X文件加载三维模型到DirextX游戏程序里。因为复杂的3D物体,要用代码去实现,那太反人类了,所以我们需要一些建模软件。
对于3ds max,要到出.X文件,要装个Panda插件。然后就是作者推荐的一个3D模型资源网站:http://www.cgmodel.com/。
网格模型接口ID3DXMesh
这个接口表示网格,继承自ID3DXBaseMesh。ID3DXMesh接口中的D3DXCreateMesh()可用于创建一个Direct3D网格模型对象:
HRESULT D3DXCreateMesh( _In_ DWORD NumFaces, //创建网格模型的多边形数目 _In_ DWORD NumVertices, //创建网格的顶点数目 _In_ DWORD Options, //创建网格时的附加选项 _In_ const LPD3DVERTEXELEMENT9 *pDeclaration, //顶点包含哪些信息 _In_ LPDIRECT3DDEVICE9 pD3DDevice, //Direct3D设备指针 _Out_ LPD3DXMESH *ppMesh //创建好的网格模型对象指针的地址 );
这个函数很少直接取用,通常是通过载入 .X 文件来生成网格模型。下面来介绍如何在程序中导入.X格式的文件。
文件模型载入第一步:通过X文件加载网格模型
使用D3DXLoadMeshFromX:
HRESULT D3DXLoadMeshFromX( _In_ LPCTSTR pFilename, //需要加载的.X文件的磁盘路劲和文件名的字符串 _In_ DWORD Options, //创建网格时的附加选项 _In_ LPDIRECT3DDEVICE9 pD3DDevice, _Out_ LPD3DXBUFFER *ppAdjacency, //用于保存加载网格的邻接信息,也就是包含每个多边形周围的多边形信息的缓冲区的内存地址 _Out_ LPD3DXBUFFER *ppMaterials, //用于保存网格的所有子集的材质 _Out_ LPD3DXBUFFER *ppEffectInstances, //用于存储网格模型的特殊效果 _Out_ DWORD *pNumMaterials, //配合第五个参数,用于存储所有子集材质的数目 _Out_ LPD3DXMESH *ppMesh //指向我们从文件生成的Direct3D网格模型指针地址,以后要访问我们创建好的网格模型,都靠这个参数 );
上面的LPD3DXBUFFER是个泛型数据结构,至于泛型就不多说了。还有就是书上这里还提到了两个函数GetBufferPointer() 和 GetBufferSize(),分别用来获取缓冲区中的数据和缓冲区的数据大小
文件模型载入第二步:载入材质和纹理
X文件中的材质信息是以D3DXMATERIAL结构类型的数组形式储存的。其中,该结构体定义了D3DMATERIAL9结构类型的成员和一个指向以NULL结尾的字符串指针,而该字符串用于指定与网格子集相关的纹理贴图文件名。
typedef struct D3DXMATERIAL { D3DMATERIAL9 MatD3D; LPSTR pTextureFilename; } D3DXMATERIAL, *LPD3DXMATERIAL;
当我们加载X文件后,需要遍历整个D3DXMATERIAL结构类型的数组,用于取出保存在ID3DXBuffer接口对象中的材质信息。由于X文件中并未存储具体的纹理数据,只包含纹理贴图的文件名,因此需要我们自己根据该文件名创建相应的纹理对象。像这样:
//从X文件中加载网格数据 LPD3DXBUFFER pAdjBuffer = NULL; LPD3DXBUFFER pMtrlBuffer = NULL; D3DXLoadMeshFromX(L"miki.X",D3DXMESH_MANAGED,g_pd3dDevice,&pAdjBuffer,&pMtrlBuffer,NULL,&g_dwNumMtrls,&g_pMesh); //读取材质和纹理数据 D3DXMATERIAL *pMtrls = (D3DXMATERIAL*)pMtrlBuffer->GetBufferPointer(); g_pMaterials = new D3DXMATERIAL9[g_dwNumMtrls]; g_pTextures = new LPDIRECT3DTEXTURE9[g_dwNumMtrls]; for(DWORD i=0;i<g_dwNumMtrls;i++) { //获取材质,并设置一下环境光 g_pMaterials[i]=pMtrls[i].MatD3D; g_pMaterials[i].AMbient=g_pMaterials[i].Diffuse; //用于将材质对漫反射光的反应程度赋值给材质的环境光反应程度 //创建纹理对象 g_pTextures[i]=NULL; D3DXCreateTextureFromFileA(g_pd3dDevice,pMtrls[i].pTextureFilename,&g_pTextures[i]); } SAFE_RELEASE(pAdjBuffer); SAFE_RELEASE(pMtrlBuffer);
文件模型载入第三步:绘制网格模型
g_pd3dDevice->BeginScene(); //用一个for循环,进行网格各个部分的绘制 for(DWORD i=0,i<g_dwNumMtrls;i++) { g_pd3dDevice->setMaterial(&g_pMaterials[i]); g_pd3dDevice->setTexture(0,g_pTextures[i]); g_pMesh->DrawSubset(i) } g_pd3dDevice->EndScene();;
将以上3步综合起来的代码就是:
//1. 从X文件中加载网格数据 LPD3DXBUFFER pAdjBuffer = NULL; LPD3DXBUFFER pMtrlBuffer = NULL; D3DXLoadMeshFromX(L"miki.X",D3DXMESH_MANAGED,g_pd3dDevice,&pAdjBuffer,&pMtrlBuffer,NULL,&g_dwNumMtrls,&g_pMesh); //2. 读取材质和纹理数据 D3DXMATERIAL *pMtrls = (D3DXMATERIAL*)pMtrlBuffer->GetBufferPointer(); g_pMaterials = new D3DXMATERIAL9[g_dwNumMtrls]; g_pTextures = new LPDIRECT3DTEXTURE9[g_dwNumMtrls]; for(DWORD i=0;i<g_dwNumMtrls;i++) { //获取材质,并设置一下环境光 g_pMaterials[i]=pMtrls[i].MatD3D; g_pMaterials[i].AMbient=g_pMaterials[i].Diffuse; //创建纹理对象 g_pTextures[i]=NULL; D3DXCreateTextureFromFileA(g_pd3dDevice,pMtrls[i].pTextureFilename,&g_pTextures[i]); } SAFE_RELEASE(pAdjBuffer); SAFE_RELEASE(pMtrlBuffer); void Direct3D_Render(HWND hwnd) { g_pd3dDevice->Clear(0,NULL,D3DCLEAR_TARRGET|D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(100,100,100),1.0f,0); //3. 绘制 g_pd3dDevice->BeginScene(); //用一个for循环,进行网格各个部分的绘制 for(DWORD i=0,i<g_dwNumMtrls;i++) { g_pd3dDevice->setMaterial(&g_pMaterials[i]); g_pd3dDevice->setTexture(0,g_pTextures[i]); g_pMesh->DrawSubset(i) } g_pd3dDevice->EndScene(); g_pd3dDevice->Present(NULL,NULL,NULL,NULL); }
第18章 Alpha混合技术
首先要明白的是什么是Alpha混合技术。Alpha通道是计算机中存储一张图片的透明和半透明的信息的通道。它是一个8位的灰度通道,用256级灰度来记录图像中的透明度信息,定义透明,不透明和半透明区域,其中黑表示全透明,白表示不透明,灰表示半透明。
Direct3D中的融合因子
Direct3D中用Alpha通道来实现多个像素颜色值的融合。每个像素都包含四个分量:Alpha分量,红色分量,绿色分量和蓝色分量(ARGB)。其中Alpha用于指定像素的透明度,在0~255之间,0表示完全透明,255表示完全不透明。
融合领域有一个Alpha融合公式:
OutPutColor = (RGBsrc * Ksrc) OP (RGBdst * Kdst)
OutPutColor表示alpha混合后的颜色值,RGBsrc 和 RGBdst 分别表示源像素和目标像素的颜色值,为包含四个颜色分量的颜色值。Ksrc和Kdst分别表示源融合因子和目标融合因子。它们指定了源像素和目标像素的颜色值在融合过程中所占的比例,在[0,1]之间取值。
D3DBLENDOP枚举定义如下:
typedef enum D3DBLENDOP { D3DBLENDOP_ADD = 1, D3DBLENDOP_SUBTRACT = 2, D3DBLENDOP_REVSUBTRACT = 3, D3DBLENDOP_MIN = 4, D3DBLENDOP_MAX = 5, D3DBLENDOP_FORCE_DWORD = 0x7fffffff } D3DBLENDOP, *LPD3DBLENDOP;
融合因子的取法
源融合因子和目标融合因子可以在SetRenderState方法中第一个参数取D3DRS_SRCBLEND 和 D3DRS_DESTBLEND 分别进行设置,而第二个参数都是在一个D3DBLEND枚举体中进行取值的。
typedef enum D3DBLEND { D3DBLEND_ZERO = 1, D3DBLEND_ONE = 2, D3DBLEND_SRCCOLOR = 3, D3DBLEND_INVSRCCOLOR = 4, D3DBLEND_SRCALPHA = 5, D3DBLEND_INVSRCALPHA = 6, D3DBLEND_DESTALPHA = 7, D3DBLEND_INVDESTALPHA = 8, D3DBLEND_DESTCOLOR = 9, D3DBLEND_INVDESTCOLOR = 10, D3DBLEND_SRCALPHASAT = 11, D3DBLEND_BOTHSRCALPHA = 12, D3DBLEND_BOTHINVSRCALPHA = 13, D3DBLEND_BLENDFACTOR = 14, D3DBLEND_INVBLENDFACTOR = 15, D3DBLEND_SRCCOLOR2 = 16, D3DBLEND_INVSRCCOLOR2 = 17, D3DBLEND_FORCE_DWORD = 0x7fffffff } D3DBLEND, *LPD3DBLEND;
Alpha的三处来源
顶点Alpha分量,如果程序中直接指定每个顶点的颜色,那么可以直接给出每个顶点颜色的Alpha值,并且这些顶点的Alpha值是可以在程序运行过程中动态修改的。我们可以通过IDirect3DDevice9::SetTexttureStageState方法指定Alpha值的来源,把第三个参数指定为D3DTA_DIFFUSE,来指定Alpha值来自顶点颜色
HRESULT SetTextureStageState( [in] DWORD Stage, //指定当前设置的纹理层是第几层(0~7) [in] D3DTEXTURESTAGESTATETYPE Type, //将要设置的纹理渲染状态 [in] DWORD Value //所设置的状态的值,根据第二个参数来决定具体取什么值的 );
对于顶点Alpha分量,可以这样写:
材质的Alpha分量:顶点的Alpha值取决于材质属性中漫反射颜色的Alpha系数以及灯光颜色中的Alpha系数,通过材质和光照中的Alpha系数相互作用,计算得到。
比如将材质的漫反射颜色值的Alpha分量设为0.2:
纹理的Alpha分量:若是取自纹理,代码如下:
Alpha融合使用三步曲:
1.启用Alpha融合:Direct3D中,混合默认是关闭着的,通过设置D3DRS_ALPHABLENDENABLE渲染状态为true来启用:
2.设置融合因子:设置源融合因子和目标融合因子:
3.设置Alpha融合运算方式:Direct3D的默认融合运算方式是D3DBLENDOP_ADD,所以其实可以不同设置,但是有时候我们不想用这种融合运算方式,可通过如下代码来指定:
综合上面3步的代码就是:
第19章 深度测试与Z缓存
这个主要是用来处由于理物体之间的远近而存在的遮挡关系的。首先来理解什么是深度缓冲区,深度缓冲区也常常被称为Z缓存,是DIirect3D用来存储绘制到屏幕上的每个像素点的深度信息的一块内存缓冲区。如果我们绘制的屏幕分辨率的像素是800*600,那么深度缓存的大小也为600*800。
当Direct3D将一个场景渲染到目标表面上时,它使用深度缓冲区来决定光栅化后各个多边形的像素前后的遮挡关系,最终决定那个颜色会被绘制出来。
所以可以这样理解,Direct3D通过比较当前绘制的像素点的深度和对应深度缓冲区的点的深度值来决定是否绘制当前像素。如果深度测试的结果为TRUE,就会绘制当前像素,并用当前像素点的深度值来更新一下深度缓冲区,反之,则不予绘制。而在通常情况下,深度缓冲区对应于屏幕大小的一块二维区域。
深度测试使用的四个步骤:
1. 创建深度缓冲区
2. 开启深度测试
3. 设置深度测试函数
4. 更新深度缓冲区
需要注意的是,后3步的顺序任意,只要在渲染前即可。而且Direct3D默认是开启深度测试的,实际上不需要后3步。
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位存储每个像素的深度值
2. 开启深度测试
使用的仍是SerRenderState函数,给SetRenderState两个参数取不同的值就可以了。将第一个参数设为D3DRS_ZENABLE,表示第二个参数将对深度测试的开启或者关闭进行设置,第二个参数设为TRUE或者FALSE,表示开启或者关闭深度测试。
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE,true); //开启深度测试
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_LESS,表示当测试点深度值小于深度缓冲区中相应值时,通过测试并绘制相关像素,这样没有被遮挡的物体才显示,被遮挡住的物体是不显示的:
g_pd3dDevice->SetRenderState(D3DRS_ZFUNC,D3DCMP_LESS);
4. 更新深度缓冲区
配合第三步设置的深度测试函数,还需要设置深度测试成功时对深度缓冲区如何操作,是保持不变还是用当前的深度值来更新对应的数值。
使用的仍然是SetRenderState函数,把它的第一个参数设为D3DRS_ZWRITEENABLE,表示在第二个参数里面将对深度缓冲区更改与否做出选择。第二个参数设为TURE的话,表示深度测试成功之后,用当前像素的深度值更新深度缓冲区对应的数值。第二个参数设为TURE是设置更新深度缓冲区时最常用的设置,同时也是默认值。反之,第二个参数设为FALSE,则表示尽管深度测试成功,还是不更新深度缓冲区对应的数值。
g_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE,true); //深度测试成功后,更新深度缓存
第20章 模版技术
首先要提的是模版缓存和模版测试。
模板缓存(stencil buffer)是一个用于专门用于制作特效的离屏(off-screen)缓存。模板缓存的分辨率与之前讲过的后台缓存和深度缓存的分辨率完全相同,模板缓存的像素也后台缓存、深度缓存中的像素一一对应。模版缓存能让我们动态地,有正对性地决定是否将某个像素写到后台缓存中。 比如要实现的镜面特效,只需要在镜子那个特定的平面区域绘制,这个时候模版缓存就可以派上用场了。
模版测试:在运用模板技术来进行特效的绘制时,需要精确到每个像素。我们会根据每个像素的模板缓存的值,进行一些检查,最后得出这个像素是否需要绘制的结论,从而实现一些特殊的效果。而这个检查的过程,就是模板测试。
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;
第十一个参数,D3DFORMAT类型的AutoDepthStencilFormat,指定AutoDepthStencilFormat的深度缓冲区和模板缓冲区共同的像素格式。具体格式可以在结构体D3DFORMAT中进行选取。我们列举一些可以选取的值:
D3DFMT_D16 深度缓存用16位存储每个像素的深度值
D3DFMT_D24X8 深度缓存用24位存储每个像素的深度值
D3DFMT_D32深度缓存用32位存储每个像素的深度值
2. 清除模版缓冲区
使用模板测试渲染每一帧之前,都需要先清除上一帧保存在模板缓冲区中的模板值。而清除模板缓冲、颜色缓冲区以及深度缓冲区都是这个IDirect3DDevice9::Clear方法的工作。
HRESULT Clear( [in] DWORD Count, [in] const D3DRECT *pRects, [in] DWORD Flags, [in] D3DCOLOR Color, [in] float Z, [in] DWORD Stencil );
第一个参数,DWORD类型的Count,指定了接下来的一个参数pRect指向的矩形数组中矩形的数量
第二个参数,const D3DRECT类型的*pRects,指向一个D3DRECT结构体的数组指针,表明我们需要清空的目标矩形区域
第三个参数,DWORD类型的Flags,指定我们需要清空的缓冲区。它为D3DCLEAR_STENCIL、D3DCLEAR_TARGET、D3DCLEAR_ZBUFFER的任意组合,分别表示模板缓冲区、颜色缓冲区、深度缓冲区,用“|”连接。
第四个参数,D3DCOLOR类型的Color,用于指定我们在清空颜色缓冲区之后每个像素对应的颜色值
第五个参数,float类型的Z,用于指定清空深度缓冲区后每个像素对应的深度值
第六个参数,DWORD类型的Stencil,用于指定清空模板缓冲区之后模板缓冲区中每个像素对应的模板值
重点是第三个参数,DWORD类型的Flags,指定我们需要清空的缓冲区。它为D3DCLEAR_STENCIL、D3DCLEAR_TARGET、D3DCLEAR_ZBUFFER的任意组合,分别表示模板缓冲区、颜色缓冲区、深度缓冲区。在调用Clear方法的时候清空哪个缓冲区,就在这里写上,想要清空多个就写上多个,用“|”连接。
如果我们三种缓冲区都要清理,就这样写:
g_pd3dDevice->Clear(0,NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(60, 150, 150), 1.0f, 0);
使用模板测试实现各种效果的关键是正确设置于模板测试相关的各渲染状态。使用的仍然是SetRenderState()函数,它的第一个参数在一个庞大的枚举类型D3DRENDERSTATETYPE中取值;
typedef enum D3DRENDERSTATETYPE { D3DRS_ZENABLE = 7, D3DRS_FILLMODE = 8, D3DRS_SHADEMODE = 9, D3DRS_ZWRITEENABLE = 14, D3DRS_ALPHATESTENABLE = 15, D3DRS_LASTPIXEL = 16, D3DRS_SRCBLEND = 19, D3DRS_DESTBLEND = 20, D3DRS_CULLMODE = 22, D3DRS_ZFUNC = 23, D3DRS_ALPHAREF = 24, D3DRS_ALPHAFUNC = 25, D3DRS_DITHERENABLE = 26, D3DRS_ALPHABLENDENABLE = 27, D3DRS_FOGENABLE = 28, D3DRS_SPECULARENABLE = 29, D3DRS_FOGCOLOR = 34, D3DRS_FOGTABLEMODE = 35, D3DRS_FOGSTART = 36, D3DRS_FOGEND = 37, D3DRS_FOGDENSITY = 38, D3DRS_RANGEFOGENABLE = 48, D3DRS_STENCILENABLE = 52, D3DRS_STENCILFAIL = 53, D3DRS_STENCILZFAIL = 54, D3DRS_STENCILPASS = 55, D3DRS_STENCILFUNC = 56, D3DRS_STENCILREF = 57, D3DRS_STENCILMASK = 58, D3DRS_STENCILWRITEMASK = 59, D3DRS_TEXTUREFACTOR = 60, D3DRS_WRAP0 = 128, D3DRS_WRAP1 = 129, D3DRS_WRAP2 = 130, D3DRS_WRAP3 = 131, D3DRS_WRAP4 = 132, D3DRS_WRAP5 = 133, D3DRS_WRAP6 = 134, D3DRS_WRAP7 = 135, D3DRS_CLIPPING = 136, D3DRS_LIGHTING = 137, D3DRS_AMBIENT = 139, D3DRS_FOGVERTEXMODE = 140, D3DRS_COLORVERTEX = 141, D3DRS_LOCALVIEWER = 142, D3DRS_NORMALIZENORMALS = 143, D3DRS_DIFFUSEMATERIALSOURCE = 145, D3DRS_SPECULARMATERIALSOURCE = 146, D3DRS_AMBIENTMATERIALSOURCE = 147, D3DRS_EMISSIVEMATERIALSOURCE = 148, D3DRS_VERTEXBLEND = 151, D3DRS_CLIPPLANEENABLE = 152, D3DRS_POINTSIZE = 154, D3DRS_POINTSIZE_MIN = 155, D3DRS_POINTSPRITEENABLE = 156, D3DRS_POINTSCALEENABLE = 157, D3DRS_POINTSCALE_A = 158, D3DRS_POINTSCALE_B = 159, D3DRS_POINTSCALE_C = 160, D3DRS_MULTISAMPLEANTIALIAS = 161, D3DRS_MULTISAMPLEMASK = 162, D3DRS_PATCHEDGESTYLE = 163, D3DRS_DEBUGMONITORTOKEN = 165, D3DRS_POINTSIZE_MAX = 166, D3DRS_INDEXEDVERTEXBLENDENABLE = 167, D3DRS_COLORWRITEENABLE = 168, D3DRS_TWEENFACTOR = 170, D3DRS_BLENDOP = 171, D3DRS_POSITIONDEGREE = 172, D3DRS_NORMALDEGREE = 173, D3DRS_SCISSORTESTENABLE = 174, D3DRS_SLOPESCALEDEPTHBIAS = 175, D3DRS_ANTIALIASEDLINEENABLE = 176, D3DRS_MINTESSELLATIONLEVEL = 178, D3DRS_MAXTESSELLATIONLEVEL = 179, D3DRS_ADAPTIVETESS_X = 180, D3DRS_ADAPTIVETESS_Y = 181, D3DRS_ADAPTIVETESS_Z = 182, D3DRS_ADAPTIVETESS_W = 183, D3DRS_ENABLEADAPTIVETESSELLATION = 184, D3DRS_TWOSIDEDSTENCILMODE = 185, D3DRS_CCW_STENCILFAIL = 186, D3DRS_CCW_STENCILZFAIL = 187, D3DRS_CCW_STENCILPASS = 188, D3DRS_CCW_STENCILFUNC = 189, D3DRS_COLORWRITEENABLE1 = 190, D3DRS_COLORWRITEENABLE2 = 191, D3DRS_COLORWRITEENABLE3 = 192, D3DRS_BLENDFACTOR = 193, D3DRS_SRGBWRITEENABLE = 194, D3DRS_DEPTHBIAS = 195, D3DRS_WRAP8 = 198, D3DRS_WRAP9 = 199, D3DRS_WRAP10 = 200, D3DRS_WRAP11 = 201, D3DRS_WRAP12 = 202, D3DRS_WRAP13 = 203, D3DRS_WRAP14 = 204, D3DRS_WRAP15 = 205, D3DRS_SEPARATEALPHABLENDENABLE = 206, D3DRS_SRCBLENDALPHA = 207, D3DRS_DESTBLENDALPHA = 208, D3DRS_BLENDOPALPHA = 209, D3DRS_FORCE_DWORD = 0x7fffffff } D3DRENDERSTATETYPE, *LPD3DRENDERSTATETYPE;
■ D3DRS_STENCILENABLE:这个渲染状态用于启用或者禁用模板处理功能。这个参数指定为TRUE表示启用模板处理;指定为FALSE,则就表示禁用模板处理。
■ D3DRS_STENCILFAIL:这个渲染状态表示模板测试失败时进行的模板操作。而进行的模板操作默认为D3DSTENCILCAPS_KEEP。
■ D3DRS_STENCILZFAIL:该渲染状态表示模板测试通过时,但是深度测试失败时进行的模板操作。默认的模板操作依旧是D3DSTENCILCAPS_KEEP。
■ D3DRS_STENCILPASS:这个渲染状态表示模板测试通过时进行的模板操作。进行的模板操作默认依旧是为D3DSTENCILCAPS_KEEP。
■ D3DRS_STENCILFUNC:这个渲染状态可以指定用于模板测试的比较函数。比较函数可以是D3DCMPFUNC枚举常量之一,该比较函数将通过模板掩码的模板参考值与模板缓冲区中当前像素的对应模板值比较,如果为TRUE,则通过模板测试。
■ D3DRS_STENCILREF:这个渲染状态用于设置模板参考值,默认为0.
■ D3DRS_STENCILMASK:这个渲染状态用于设置模板掩码,决定对模板参考值和模板缓冲区值的哪位进行比较,默认掩码为0xffffffff。
■ D3DRS_STENCILWRITEMASK:这个渲染状态用于指定写入到模板缓冲区中的数值的掩码,默认掩码也为0xffffffff。
■ D3DRS_TWOSIDEDSTENCILMODE:这个渲染状态用于激活或者禁用双面缓冲区。
■ D3DRS_CCW_STENCILFAIL:这个渲染状态用于设置在启用了双面模板缓冲区后,顶点按照逆时针顺序组成的多边形当模板测试失败时进行的模板操作。
■ D3DRS_CCW_STENCILZFAIL:这个渲染状态用于设置在启用了双面模板缓冲区后,顶点按照逆时针顺序组成的多边形当模板测试成功但深度测试失败时进行的模板操作。
■ D3DRS_CCW_STENCILPASS:这个渲染状态用于设置在启用了双面模板缓冲区后,顶点按照逆时针顺序组成的多边形当模板测试成功时进行的模板操作。
■ D3DRS_CCW_STENCILFUNC:这个渲染状态指定了模板测试的比较函数,在D3DCMPFUNC枚举类型中取值
对于目标表面上的每一个像素,Direct3D首先将应用程序定义的模板参考值和模板掩码进行逐位与运算,然后将当前测试的像素在模板缓冲区中的数值与模板掩码进行逐位与运算,最后根据模板比较函数对得到的结果进行比较,如果模板测试成功,也就是测试结果为true,那么该像素就被写入后台缓存;如果模板测试失败的话,也就是测试结果为false,那么该像素就不会被写入后台缓存,也不会被写入深度缓存。
另外,上面的渲染状态 D3DRS_STENCILFAIL、D3DRS_STENCILZFAIL、D3DRS_STENCILPASS定义了模板测试、深度测试失败或者通过时进行的模板操作,他们也是在一个枚举类型中取值,这个枚举类型是D3DSTENCILOP,这个枚举类型的定义如下:
typedef enum D3DSTENCILOP { D3DSTENCILOP_KEEP = 1, D3DSTENCILOP_ZERO = 2, D3DSTENCILOP_REPLACE = 3, D3DSTENCILOP_INCRSAT = 4, D3DSTENCILOP_DECRSAT = 5, D3DSTENCILOP_INVERT = 6, D3DSTENCILOP_INCR = 7, D3DSTENCILOP_DECR = 8, D3DSTENCILOP_FORCE_DWORD =0x7fffffff } D3DSTENCILOP, *LPD3DSTENCILOP;
镜面特效的实现
想要在Direct3D程序中实现镜面特效,首先需要计算出物体先归于特定平面中的镜像,而这个过程可以通过镜面成像的数学原理来进行计算,然后通过模板技术将物体的镜像正确地绘制到所指定的平面(镜面)中。只要通过数学知识,求出q点到q‘点的镜像变换矩阵就可以了,这样知道q点,根据镜像变换矩阵,就可以求出q‘来。这个镜像变换矩阵的求法,微软早就为我们准备好了,那就是D3DX库中的D3DXMatrixReflect函数:
D3DXMATRIX* D3DXMatrixReflect( _Inout_ D3DXMATRIX *pOut, _In_ const D3DXPLANE *pPlane );
■ 第一个参数,D3DXMATRIX类型的*pOut,从类型上来看我们就知道他是一个D3DXMATRIX类型的4 X 4的矩阵,调用这个D3DXMatrixReflect方法,其实就是在为这个矩阵赋值,通过Direct3D的内部计算,让这个矩阵成为在第二个参数中提供的那个平面的镜像变换矩阵。
■ 第二个参数,const D3DXPLANE类型的*pPlane,显然就是一个D3DXPLANE结构体类型的平面了。
typedef struct D3DXPLANE { FLOAT a; FLOAT b; FLOAT c; FLOAT d; } D3DXPLANE, *LPD3DXPLANE;
其中的a,b,c,d四个参数显然就是三维平面方程 ax + by + cz + dw = 0.的四个系数了。
在Direct3D中计算某个物体相对于任意平面的镜像时,只要通过这个D3DXMatrixReflect计算一下该平面的镜像变换矩阵,然后把该物体的世界变换矩阵乘以镜像变换矩阵就可以了,得到的结果就是世界变换矩阵。接着再SetMatrix一下,接着写渲染的代码就可以了。
//这里假如物体的原始世界矩阵是matWorld D3DXMATRIXmatReflect; D3DXPLANEplane(0.0f, 1.0f, 1.0f, 0.0f); // 定义平面方程为y+z=0的平面 D3DXMatrixReflect(&matReflect,&plane);//计算y+z=0平面的镜像变换矩阵 matWorld=matWorld*matReflect; //镜像变换矩阵和原始世界矩阵相乘,得到镜像的世界矩阵 g_pd3dDevice->SetTransform(D3DTS_WORLD,& matReflect);//设置出镜像的世界矩阵 //接下来就写绘制镜像的代码就可以了
参考博客:
【Visual C++】游戏开发笔记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观
【Visual C++】游戏开发笔记四十五 浅墨DirectX教程十三 深度测试和Z缓存专场
【Visual C++】游戏开发笔记四十六 浅墨DirectX教程十四 模板测试与镜面特效专场