《逐夢旅程 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;View Code
■ 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教程十四 模板測試與鏡面特效專場
《逐夢旅程 WINDOWS遊戲編程之從零開始》筆記8——載入三維模型&Alpha混合技術&深度測試與Z緩存