《逐梦旅程 WINDOWS游戏编程之从零开始》笔记8——载入三维模型&Alpha混合技术&深度测试与Z缓存

第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教程十四 模板测试与镜面特效专场

时间: 2024-10-14 04:24:52

《逐梦旅程 WINDOWS游戏编程之从零开始》笔记8——载入三维模型&Alpha混合技术&深度测试与Z缓存的相关文章

《逐梦旅程 WINDOWS游戏编程之从零开始》笔记6——Direct3D中的顶点缓存和索引缓存

第12章 Direct3D绘制基础 1. 顶点缓存 计算机所描绘的3D图形是通过多边形网格来构成的,网网格勾勒出轮廓,然后在网格轮廓的表面上贴上相应的图片,这样就构成了一个3D模型.三角形网格是构建物体模型的基本单元,而一个三角形有3个顶点,为了能够使用大量三角形组成三角形网格来描述物体,需要首先定义号三角形的顶点(Vertex),3个顶点确定一个三角形,顶点除了定义每个顶点的坐标位置外,还还含有颜色等其他属性. 在Direct3D中,顶点的具体表现形式是顶点缓存,顶点缓存保存了顶点数据的内存空

《逐梦旅程 WINDOWS游戏编程之从零开始》笔记10——三维天空的构建&amp;三维粒子的实现&amp;多游戏模型的载入

第23章 三维天空的构建 目前描述三维天空的技术主要包括三种类型,直接来介绍使用最广泛的模拟技术,详细的描述可以见作者的博文. 天空盒(Sky Box),即放到场景的是一个立方体.它是目前使用最广泛的三维天空模拟技术,网络上素材丰富,所以这次就用教大家用天空盒来模拟三维天空.天空盒经常是由24个顶点.六个面组成的立方体(或者直接从做好的X模型文件载入天空盒),并经常会随着视点的移动而移动,来刻画极远处玩家无法达到位置的天空 天空盒的设计 1.准备天空盒纹理素材 天空盒的纹理自然就是我们这个天空盒

《逐梦旅程 WINDOWS游戏编程之从零开始》笔记5——Direct3D编程基础

第11章 Direct3D编程基础 2D游戏是贴图的艺术,3D游戏是渲染的艺术.这句话在我学过了之前的GDI编程之后,前一句算是有所体会,现在是来理解后一句的时候了. 安装DirectX SDK配置啥的就不说了,直接进入正题,先来个典型的Direct3D程序框架图: 主要分为5个部分: 创建一个Windows窗口 Direct3D的初始化 消息循环 渲染图形 结束应用程序,清除在初始化阶段锁创建的COM对象,退出程序 至于COM (Component Object Model, 组件对象模型)

《逐梦旅程 WINDOWS游戏编程之从零开始》读书笔记1——创建窗口

步骤: 窗口类的设计 窗口类的注册 窗口的正式创建 窗口的显示与更新 1. 设计:使用WNDCLASSEX结构体,这里注意的是C++中的结构体中的成员默认是共有的,所以可以直接通过 . 来调用. typedef struct tagWNDCLASSEX { UINT cbSize; //UINT类型的cbSize,表示该结构体的字节数大小 UINT style; //指定窗口的风格样式 WNDPROC lpfnWndProc; //指向窗口过程函数的函数指针 int cbClsExtra; //

《逐梦旅程 WINDOWS游戏编程之从零开始》笔记7——四大变换

第13章 世界变换,取景变换,投影变换,视口变换 在Direct3D中,如果为进行任何空间坐标变换而直接绘图的话,图形将始终处于应用程序窗口的中心位置,默认这个位置就成为世界坐标系的原点(0,0,0).而且我们也不能改变观察图形的视角方向.默认情况下的观察方向是世界坐标系的z轴正向方向. 世界变换运算是为了能在世界空间中的指定位置来绘制图形 取景变换运算是为了以不同的视角观察图形 投影变换为了将相对较远的图形投影到同一个平面上并体现出"近大远小"的真实视觉效果 视口变换是为了控制显示图

《逐梦旅程 WINDOWS游戏编程之从零开始》笔记7——DirectInput&amp;纹理映射

第15章 DirectInput接口 DirectInput作为DirectX的组件之一,依然是一些COM对象的集合.DirectInput由IDirectinput8.IDirectInputDevice8和IDirectInputEffect这3个接口组成.其中IDirectInput8作为DirectInput API中最主要的接口,用于初始化系统以及创建输入设备接口,DirectInput中其他所有接口都需要依赖于我们的IDirectInput8之上,都是通过这个接口进行查询的.而Dir

《逐梦旅程 WINDOWS游戏编程之从零开始》笔记8——光照与材质

第14章 绘制出质感的世界--光照与材质 1. 光照与光源 在Direct3D中的光源类型和光照类型是不同的两个概念,光照模型描述的是光线的反射特征,而光源类型主要强调的是能够产生这些光照模型的方式以及光线的位置,方向,强度等特征. 四大光照类型 环境光:基于整个自然界环境的整体亮度,称为环境光或者背景光,没有位置或者方向上的特征,只有一个颜色亮度值,不会衰减,在所有方向和所有物体表面上投射的环境光的数量是恒定不变的(有点像我们白天的自然光).在Direc3D中设置环境光可以直接使用setRen

《逐梦旅程 WINDOWS游戏编程之从零开始》源码分析2——GDI

GDI: 图形设备接口 1. 取得设备环境的句柄(如屏幕) 使用BeginPaint和EndPaint这两个函数,或者使用GetDC和ReleaseDC这两个函数.关于函数的具体说明可以参考mdsn文档. 一个GDI程序通用框架: 1 #include <windows.h> 2 3 #define WINDOW_WIDTH 800 //为窗口宽度定义的宏,以方便在此处修改窗口宽度 4 #define WINDOW_HEIGHT 600 //为窗口高度定义的宏,以方便在此处修改窗口高度 5 #

《逐梦旅程 WINDOWS游戏编程之从零开始》笔记9——游戏摄像机&amp;三维地形的构建

第21章 游戏摄像机的构建 之前的程序示例,都是通过封装的DirectInput类来处理键盘和鼠标的输入,对应地改变我们人物模型的世界矩阵来达到移动物体,改变观察点的效果.其实我们的观察方向乃至观察点都是没有变的,变的只是我们3D人物的位置.说白了就是用D3DXMatrixLookAtLH在资源初始化时固定住视角,在程序运行过程中接收到消息并改变三维人物模型的世界矩阵而已.这章的主要内容就是创建出一个可以在三维空间中自由移动的摄像机类,我们准备给这个摄像机类取名为CameraClass. 设计摄