1. 程式人生 > >Visual C++ 遊戲開發筆記 紋理對映技術(一)

Visual C++ 遊戲開發筆記 紋理對映技術(一)

一、引言

江山如此多嬌,風景如畫。萬物都有其獨特的特點。真實世界中的物體,在計算機三維世界中如果單單用各種各樣的幾何體以及頂點座標,頂點顏色來模擬的話,往往缺乏生機,往往差強人意。

比如說我們要繪製出如下效果的一個貼了瓷磚的立方體:

綜合我們之前講過的知識,繪製一個立方體是小菜一碟。但關鍵就來了,如何繪製出像磚塊那樣坑坑窪窪的效果呢?難不成用成千上萬的頂點座標一個個去機械的模擬?這顯然不現實。

相信正確的做法大家應該很容易想到,先繪製一個立方體,接著準備一副描繪著磚塊狀的2D圖片,然後把這幅圖片像貼畫一樣貼到這個立方體的六個面,這樣繪製磚塊立方體的效果就可以實現了。

其實上面我們講到的這種方法,就是計算機圖形學中“紋理對映”這套技術思想的真實寫照。

在我們的計算機三維世界中,想要模擬出真實而複雜的三維物體,讓他們從表面上看起來逼真,就需要請出“紋理對映”這套技術。

二、紋理對映的概念

一言以蔽之,紋理對映是一種將2D影象對映到3D物體上的技術。

一般來說,紋理是表示物體表面細節的一副或者幾幅二維圖形,也稱紋理貼圖(texture)。當我們把紋理按照特定的方式對映到物體表面上的時候,能使物體看上去更加真實。在當今流行的計算機圖形系統中,紋理繪製已經成了渲染方法中的中流砥柱了。

想要理解紋理對映其實非常簡單,把紋理看做是應用在物體表面上的畫素顏色就可以了。

在真實世界中,紋理表示一個物件的顏色、圖案或者觸覺特性。但在計算機圖形學中,紋理只表示物件表面的彩色圖案,它不能改變物件的幾何形式,換言之,就像貼在幾何體表面上的貼畫一樣。

好了,基本概念應該講明白了,下面我們來看看如果要使用這種紋理對映技術的話,需要了解的具體知識。

三、紋理對映使用四步曲

這一小節裡面我們詳細講解了使用紋理對映的具體細節。

1. 紋理對映使用四步曲之一,紋理座標的定義

我們一般把紋理對映所使用的2D影象稱作紋理貼圖。Direct3D支援多種的紋理貼圖,比如有.bmp、.dds、.dib、.png以及.tga等等.雖然說Direct3D對紋理貼圖的大小沒有限制,但是為了提高程式使用紋理的效率,通常使用邊長為2的N次方冪的正方形圖片,比如128 x128,256 x 256,512 x 512等等。

紋理貼圖往往都通過一個二維陣列儲存每個點的顏色值,該顏色值被稱作紋理元素,而每個紋理元素在紋理中都有唯一的地址。而為了將紋理貼圖對映到三維圖形中,Direct3D使用了紋理座標確定紋理貼圖上的每個紋理元素。

紋理座標由一個二維座標系指定,這個座標系由沿水平方向的u軸和沿垂直方向的v軸構成,他們兩個合起來就是座標對(u,v),其實和我們習慣的(x,y)座標對是一個概念,只不過在紋理貼圖中我們習慣用(u,v)而已。其中u軸正方向為水平向右,v軸正方向為垂直向下,且他們的取值範圍都在[0,1]之間。

紋理座標位於紋理空間之中,是相對座標,相對於紋理座標系中的原點(0,0)。當把紋理貼到三維模型表面上時,紋理元素首先被對映到物體模型的區域性座標系中,然後再變換到螢幕座標系中對應畫素的位置處。

另外的一點,紋理座標是通過紋理層和紋理聯絡到一起的,通常情況下只需使用一層紋理就夠了。記得我們原來講FVF靈活頂點格式的時候講到過,一個靈活頂點格式中允許最多定義八組紋理座標,而每組紋理座標都對應一個紋理層,這就是說每個頂點最多可以使用八層紋理。

比如這樣寫:

[cpp] view plaincopyprint?
  1. struct CUSTOMVERTEX
  2. {
  3. FLOAT_x, _y, _z; // 頂點的位置
  4. FLOAT_u1, _v1; // 第一層紋理座標
  5. FLOAT_u2, _v2; // 第二層紋理座標
  6. FLOAT_u3, _v3; // 第三層紋理座標
  7. };
  8. #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ |D3DFVF_TEX1| D3DFVF_TEX2| D3DFVF_TEX3)
[cpp] view plaincopyprint?
  1. struct CUSTOMVERTEX  
  2. {  
  3.        FLOAT_x, _y, _z;               // 頂點的位置
  4.        FLOAT_u1, _v1;                   // 第一層紋理座標
  5.        FLOAT_u2, _v2;                   // 第二層紋理座標
  6.        FLOAT_u3, _v3;                   // 第三層紋理座標
  7. };  
  8. #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ |D3DFVF_TEX1| D3DFVF_TEX2| D3DFVF_TEX3)
struct CUSTOMVERTEX
{
       FLOAT_x, _y, _z;               // 頂點的位置
       FLOAT_u1, _v1;                   // 第一層紋理座標
       FLOAT_u2, _v2;                   // 第二層紋理座標
       FLOAT_u3, _v3;                   // 第三層紋理座標
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ |D3DFVF_TEX1| D3DFVF_TEX2| D3DFVF_TEX3)




2.紋理對映使用四步曲之二,頂點的訪問

在FVF靈活頂點格式中定義好紋理座標之後,想要把紋理座標存到頂點快取中,頂點快取的訪問這一步當然必不可少。

也就是根據我們在第一步裡面定義的頂點格式,來像做填空題一樣填充頂點資料。

下面上一段例項程式碼:

比如我們在第一步中定義的是這樣的頂點格式:

[cpp] view plaincopyprint?
  1. struct CUSTOMVERTEX
  2. {
  3. FLOAT_x, _y, _z; // 頂點的位置
  4. FLOAT_u, _v; // 紋理座標
  5. CUSTOMVERTEX(FLOATx, FLOAT y, FLOAT z, FLOAT u, FLOAT v)
  6. :_x(x), _y(y), _z(z), _u(u), _v(v) {}
  7. };
  8. #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ |D3DFVF_TEX1)
[cpp] view plaincopyprint?
  1. struct CUSTOMVERTEX  
  2. {  
  3.        FLOAT_x, _y, _z;               // 頂點的位置
  4.        FLOAT_u, _v;                   // 紋理座標
  5.        CUSTOMVERTEX(FLOATx, FLOAT y, FLOAT z, FLOAT u, FLOAT v)  
  6.               :_x(x), _y(y), _z(z), _u(u), _v(v) {}  
  7. };  
  8. #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ |D3DFVF_TEX1)
struct CUSTOMVERTEX
{
       FLOAT_x, _y, _z;               // 頂點的位置
       FLOAT_u, _v;                   // 紋理座標
       CUSTOMVERTEX(FLOATx, FLOAT y, FLOAT z, FLOAT u, FLOAT v)
              :_x(x), _y(y), _z(z), _u(u), _v(v) {}
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ |D3DFVF_TEX1)


接著我們這一步裡就要做如下填空題的操作:
[cpp] view plaincopyprint?
  1. //--------------------------------------------------------------------------------------
  2. //【頂點快取、索引快取繪圖四步曲之三】:訪問頂點快取和索引快取
  3. //--------------------------------------------------------------------------------------
  4. //填充頂點快取
  5. CUSTOMVERTEX*pVertices;
  6. if(FAILED( g_pVertexBuffer->Lock( 0, sizeof(CUSTOMVERTEX),(void**)&pVertices, 0 ) ) )
  7. return E_FAIL;
  8. //正面頂點資料
  9. pVertices[0]= CUSTOMVERTEX(-10.0f, 10.0f, -10.0f,0.0f, 0.0f);
  10. pVertices[1]= CUSTOMVERTEX( 10.0f, 10.0f, -10.0f,1.0f, 0.0f);
  11. pVertices[2]= CUSTOMVERTEX( 10.0f, -10.0f, -10.0f, 1.0f, 1.0f);
  12. pVertices[3]= CUSTOMVERTEX(-10.0f, -10.0f, -10.0f, 0.0f, 1.0f);
  13. g_pVertexBuffer->Unlock();
[cpp] view plaincopyprint?
  1. //--------------------------------------------------------------------------------------
  2.  //【頂點快取、索引快取繪圖四步曲之三】:訪問頂點快取和索引快取
  3.  //--------------------------------------------------------------------------------------
  4.               //填充頂點快取
  5.               CUSTOMVERTEX*pVertices;  
  6.               if(FAILED( g_pVertexBuffer->Lock( 0, sizeof(CUSTOMVERTEX),(void**)&pVertices, 0 ) ) )  
  7.                      return E_FAIL;  
  8.               //正面頂點資料
  9.               pVertices[0]= CUSTOMVERTEX(-10.0f,  10.0f, -10.0f,0.0f, 0.0f);  
  10.               pVertices[1]= CUSTOMVERTEX( 10.0f,  10.0f, -10.0f,1.0f, 0.0f);  
  11.               pVertices[2]= CUSTOMVERTEX( 10.0f, -10.0f, -10.0f, 1.0f, 1.0f);  
  12.               pVertices[3]= CUSTOMVERTEX(-10.0f, -10.0f, -10.0f, 0.0f, 1.0f);  
  13.           g_pVertexBuffer->Unlock();  
//--------------------------------------------------------------------------------------
 //【頂點快取、索引快取繪圖四步曲之三】:訪問頂點快取和索引快取
 //--------------------------------------------------------------------------------------
              //填充頂點快取
              CUSTOMVERTEX*pVertices;
              if(FAILED( g_pVertexBuffer->Lock( 0, sizeof(CUSTOMVERTEX),(void**)&pVertices, 0 ) ) )
                     return E_FAIL;
 
              //正面頂點資料
              pVertices[0]= CUSTOMVERTEX(-10.0f,  10.0f, -10.0f,0.0f, 0.0f);
              pVertices[1]= CUSTOMVERTEX( 10.0f,  10.0f, -10.0f,1.0f, 0.0f);
              pVertices[2]= CUSTOMVERTEX( 10.0f, -10.0f, -10.0f, 1.0f, 1.0f);
              pVertices[3]= CUSTOMVERTEX(-10.0f, -10.0f, -10.0f, 0.0f, 1.0f);
	      g_pVertexBuffer->Unlock();


關於上面這段程式碼,我們來簡單講解一下,這四個頂點,pVertices[0]~ pVertices[3]聯合在一起定義了一個包含了頂點座標和紋理座標的矩陣。我們就拿其中的pVertices[0] = CUSTOMVERTEX(-10.0f, 10.0f, -10.0f, 0.0f, 0.0f);來具體看吧,CUSTOMVERTEX結構體依次定義了頂點座標_x, _y, _z和紋理座標_u, _v。所以頂點pVertices[0]中前三個引數-10.0f, 10.0f, -10.0f就分別對應著頂點座標_x, _y, _z,後兩個引數0.0f, 0.0f就分別對應著紋理座標_u, _v。所以通過上面這句pVertices[0] =CUSTOMVERTEX(-10.0f, 10.0f, -10.0f,0.0f, 0.0f);的程式碼,我們就指定了pVertices[0]的頂點座標為(-10.0f, 10.0f, -10.0f),紋理座標為(0.0f, 0.0f)。另外大家也許會發現在頂點結構體中我們比之前多出了一句:

[cpp] view plaincopyprint?
  1. CUSTOMVERTEX(FLOATx, FLOAT y, FLOAT z, FLOAT u, FLOAT v)
  2. :_x(x), _y(y), _z(z), _u(u), _v(v) {}
[cpp] view plaincopyprint?
  1. CUSTOMVERTEX(FLOATx, FLOAT y, FLOAT z, FLOAT u, FLOAT v)  
  2.               :_x(x), _y(y), _z(z), _u(u), _v(v) {}  
CUSTOMVERTEX(FLOATx, FLOAT y, FLOAT z, FLOAT u, FLOAT v)
              :_x(x), _y(y), _z(z), _u(u), _v(v) {}


這是我們為頂點結構體定義的一個預設建構函式,方便了後面我們的賦值操作。

3.紋理對映使用四步曲之三,紋理的建立

頂點這邊的內容經過以上兩步已經大功告成,下面就是建立一個紋理物件並從檔案中讀取一副紋理並儲存在這個物件中了。

可以這樣說,在Direct3D中,紋理是以COM物件的形式存在的,也就是IDirect3DTexture9這個介面。我們要對物體表面進行紋理對映的話,首先要建立紋理物件,建立時需要指定紋理的寬度、高度、格式等屬性,然後還需要將圖形檔案載入到紋理物件中。我們可以用D3DX庫中的D3DXCreateTexture函式建立一個紋理物件。不過這個方函式比較冷門,平常用得少,還是先來簡單介紹一下這個函式的用法:

[cpp] view plaincopyprint?
  1. HRESULT D3DXCreateTexture(
  2. __in LPDIRECT3DDEVICE9 pDevice,
  3. __in UINT Width,
  4. __in UINT Height,
  5. __in UINT MipLevels,
  6. __in DWORD Usage,
  7. __in D3DFORMAT Format,
  8. __in D3DPOOL Pool,
  9. __out LPDIRECT3DTEXTURE9 *ppTexture
  10. );
[cpp] view plaincopyprint?
  1. HRESULT D3DXCreateTexture(  
  2.   __in   LPDIRECT3DDEVICE9 pDevice,  
  3.   __in   UINT Width,  
  4.   __in   UINT Height,  
  5.   __in   UINT MipLevels,  
  6.   __in   DWORD Usage,  
  7.   __in   D3DFORMAT Format,  
  8.   __in   D3DPOOL Pool,  
  9.   __out  LPDIRECT3DTEXTURE9 *ppTexture  
  10. );  
HRESULT D3DXCreateTexture(
  __in   LPDIRECT3DDEVICE9 pDevice,
  __in   UINT Width,
  __in   UINT Height,
  __in   UINT MipLevels,
  __in   DWORD Usage,
  __in   D3DFORMAT Format,
  __in   D3DPOOL Pool,
  __out  LPDIRECT3DTEXTURE9 *ppTexture
);

■ 第一個引數,LPDIRECT3DDEVICE9型別的pDevice,無需多言,這就是我們的繪製金鑰匙——Direct3D裝置物件了。

■ 第二個引數,UINT型別的Width,表示我們建立的紋理物件的寬度。

■ 第三個引數,UINT型別的Height,表示我們建立的紋理物件的高度。

■ 第四個引數,UINT型別的MipLevels,表示我們建立的紋理的漸進級別,通常取預設值D3DX_DEFAULT就可以了,表示建立一個完整MIP貼圖鏈。。其實取0也無妨,和取預設值D3DX_DEFAULT效果是一樣的。

■ 第五個引數,DWORD型別的Usage,指定了我們紋理的使用方式,取值在0、D3DUSAGE_RENDERTARGET、D3DUSAGE_DYMANIC中三取一。

■ 第六個引數,D3DFORMAT型別的Format,用於指定紋理中每個儲存每個顏色成分所使用的位數,在D3DFORMAT列舉體中取值,這個引數也可以設為0,表示使用預設值。

■ 第七個引數,D3DPOOL型別的Pool,指定了紋理物件停駐的記憶體的類別,在D3DPOOL列舉體中取值,不過我們經常是在這個列舉體其中的兩個成員D3DPOOL_DEFAULT和D3DPOOL_MANAGED之間取一個。

■ 第八個引數,LPDIRECT3DTEXTURE9型別的*ppTexture,指標的指標,也就是指向IDirect3DTexture9介面的地址,顯然我們呼叫D3DXCreateTexture,就是把最終建立的紋理交給這個引數保管了,後面如果要使用我們建立的這個紋理的話,通過這個引數就可以了。

但更多情況下我們是從檔案中讀取紋理圖形的,我們可以任意讀取我們喜歡的圖片檔案到Direct3D中,這就用到

[cpp] view plaincopyprint?
  1. D3DXCreateTextureFromFile函式:
  2. HRESULT D3DXCreateTextureFromFile(
  3. __in LPDIRECT3DDEVICE9 pDevice,
  4. __in LPCTSTR pSrcFile,
  5. __out LPDIRECT3DTEXTURE9 *ppTexture
  6. );
[cpp] view plaincopyprint?
  1. D3DXCreateTextureFromFile函式:  
  2.  HRESULT  D3DXCreateTextureFromFile(  
  3.   __in   LPDIRECT3DDEVICE9 pDevice,  
  4.   __in   LPCTSTR pSrcFile,  
  5.   __out  LPDIRECT3DTEXTURE9 *ppTexture  
  6. );  
D3DXCreateTextureFromFile函式:
 HRESULT  D3DXCreateTextureFromFile(
  __in   LPDIRECT3DDEVICE9 pDevice,
  __in   LPCTSTR pSrcFile,
  __out  LPDIRECT3DTEXTURE9 *ppTexture
);

■ 第一個引數,LPDIRECT3DDEVICE9型別的pDevice,無需多言,這就是我們的繪製金鑰匙——Direct3D裝置物件了。

■ 第二個引數,LPCTSTR型別的pSrcFile,指向了用於建立紋理的圖示檔名字的字串,也就是我們要使用的紋理圖片的檔案地址了。支援的圖片格式多種多樣,有.bmp、.dds、.dib、.png以及.tga等等。

■ 第三個引數,LPDIRECT3DTEXTURE9型別的*ppTexture,指標的指標,也就是指向IDirect3DTexture9介面的地址,顯然我們呼叫D3DXCreateTexture,就是把最終建立的紋理交給這個引數保管了,後面如果要使用我們建立的這個紋理的話,通過這個引數就可以了。

上面我們剛講到了D3DXCreateTextureFromFile函式,其實這個函式有一個孿生的哥哥,叫D3DXCreateTextureFromFileEx。我們可以看到兩兄弟從名字上來說唯一的區別就是哥哥在尾巴上多了一個Ex,表示“額外”的意思,表示是弟弟D3DXCreateTextureFromFile的加強版。(可以結合CreateWindow和CreateWindowEx一起理解記憶,算是微軟對他們寫的API函式一套慣用的命名規則吧)此函式不僅包含了它的孿生弟弟D3DXCreateTextureFromFile的全部功能(從磁碟上的圖形檔案上載入檔案並建立紋理物件),而且還能指定建立的紋理物件的寬度、高度以及各式等等。這裡我們貼出它的原型即可:

[cpp] view plaincopyprint?
  1. HRESULT D3DXCreateTextureFromFileEx(
  2. _In_ LPDIRECT3DDEVICE9 pDevice,
  3. _In_ LPCTSTR pSrcFile,
  4. _In_ UINT Width,
  5. _In_ UINT Height,
  6. _In_ UINT MipLevels,
  7. _In_ DWORD Usage,
  8. _In_ D3DFORMAT Format,
  9. _In_ D3DPOOL Pool,
  10. _In_ DWORD Filter,
  11. _In_ DWORD MipFilter,
  12. _In_ D3DCOLOR ColorKey,
  13. _Inout_ D3DXIMAGE_INFO*pSrcInfo,
  14. _Out_ PALETTEENTRY *pPalette,
  15. _Out_ LPDIRECT3DTEXTURE9 *ppTexture
  16. );
[cpp] view plaincopyprint?
  1. HRESULT D3DXCreateTextureFromFileEx(  
  2.  _In_     LPDIRECT3DDEVICE9 pDevice,  
  3.  _In_     LPCTSTR pSrcFile,  
  4.  _In_     UINT Width,  
  5.  _In_     UINT Height,  
  6.  _In_     UINT MipLevels,  
  7.  _In_     DWORD Usage,  
  8.  _In_     D3DFORMAT Format,  
  9.  _In_     D3DPOOL Pool,  
  10.  _In_     DWORD Filter,  
  11.  _In_     DWORD MipFilter,  
  12.  _In_     D3DCOLOR ColorKey,  
  13.   _Inout_  D3DXIMAGE_INFO*pSrcInfo,  
  14.  _Out_    PALETTEENTRY *pPalette,  
  15.  _Out_    LPDIRECT3DTEXTURE9 *ppTexture  
  16. );  
HRESULT D3DXCreateTextureFromFileEx(
 _In_     LPDIRECT3DDEVICE9 pDevice,
 _In_     LPCTSTR pSrcFile,
 _In_     UINT Width,
 _In_     UINT Height,
 _In_     UINT MipLevels,
 _In_     DWORD Usage,
 _In_     D3DFORMAT Format,
 _In_     D3DPOOL Pool,
 _In_     DWORD Filter,
 _In_     DWORD MipFilter,
 _In_     D3DCOLOR ColorKey,
  _Inout_  D3DXIMAGE_INFO*pSrcInfo,
 _Out_    PALETTEENTRY *pPalette,
 _Out_    LPDIRECT3DTEXTURE9 *ppTexture
);



給這步一個例項吧,其實就是簡單定義一個紋理介面物件,然後從本地圖片檔案讀取圖片資料然後存到這個紋理介面物件中。

[cpp] view plaincopyprint?
  1. LPDIRECT3DTEXTURE9 g_pTexture = NULL; // 紋理介面物件
  2. D3DXCreateTextureFromFile(g_pd3dDevice,L"darksider.jpg", &g_pTexture); // 建立紋理
[cpp] view plaincopyprint?
  1. LPDIRECT3DTEXTURE9      g_pTexture   = NULL;   // 紋理介面物件
  2. D3DXCreateTextureFromFile(g_pd3dDevice,L"darksider.jpg", &g_pTexture); // 建立紋理
LPDIRECT3DTEXTURE9      g_pTexture   = NULL;   // 紋理介面物件
D3DXCreateTextureFromFile(g_pd3dDevice,L"darksider.jpg", &g_pTexture); // 建立紋理


看到這裡大家如果累了,不妨來看一組遊戲美圖。淺墨相信本週中國遊戲界的最大新聞應該就是國產單機遊戲第一品牌《仙劍奇俠傳》的第七部作品《仙劍奇俠傳五前傳》的釋出了。作為有十年“修為”的仙劍鐵桿粉絲,淺墨在1月15號凌晨,也就是《仙五前傳》發售並開放啟用的第一時間就買好啟用碼並激活,開始了又一段的仙劍之旅。

就在前天,淺墨在困難模式下打通了仙劍五前傳。總體上來說《仙劍五前傳》非常的出色,準神作,完虐《仙劍五》,在某種意義上甚至超越了神作《仙劍奇俠傳四》。《仙劍五前傳》的配音非常贊,一路玩過來就像看電視劇一樣。劇情也同樣出色,和美劇相比有過之而無不及。畫質方面,經過大量的演算法的優化,頗顯陳舊的RenderWare遊戲引擎能達到這樣的畫質效果,已經非常不容易了。3D人物建模方面與前作相比顯得更加精雕細琢,有很大的質量飛躍。

總之淺墨相信這款作品會超越《仙劍奇俠傳五》的銷量,讓北軟賺個“盆滿鉢滿”。然後北軟在盈利的基礎上,有能力購買一款比RenderWare更出色的商業遊戲引擎的授權。這樣仙劍六會用上更加出色的技術,仙劍奇俠傳系列作品的質量則會更加優秀,中國單機遊戲界則會更加繁榮。這是我們都希望看到的。

好了,說了這麼多了,下面我們就來貼圖,這是淺墨自己在《仙劍五前傳》遊戲過程中截的圖:

好了,美圖看完了,繼續來學習吧。另外提一點,《仙劍奇俠傳五前傳》就是用我們目前正在學習的DirectX 9.0c配合RenderWare遊戲引擎來進行研發的,所以為了振興國產遊戲,大家共同努力學習吧~

4.紋理對映使用四步曲之四,紋理的啟用

載入完紋理後,就可以呼叫IDirect3DDevice9介面的SetTexture方法,設定我們當前需要啟用的紋理。我們可以在MSDN中查到SetTexture方法有如下原型:

[cpp] view plaincopyprint?
  1. HRESULT SetTexture(
  2. [in] DWORD Sampler,
  3. [in] IDirect3DBaseTexture9 *pTexture
  4. );
[cpp] view plaincopyprint?
  1. HRESULT SetTexture(  
  2.   [in]  DWORD Sampler,  
  3.   [in]  IDirect3DBaseTexture9 *pTexture  
  4. );  
HRESULT SetTexture(
  [in]  DWORD Sampler,
  [in]  IDirect3DBaseTexture9 *pTexture
);

■ 第一個引數,DWORD型別的Sampler,指定了應用的紋理是哪一層。我們知道Direct3D中最多可以設定8層紋理,所以這個引數取值就在0~7之間了。

■ 第二個引數,IDirect3DBaseTexture9型別的*pTexture,表示我們將要啟用的紋理的IDirect3DBaseTexture9介面物件,比如我們填上面第二步末尾例項中定義過的g_pTexture引數,注意要加取地址符號,也就是這樣填&g_pTexture,則表示我們現在繪圖時用的就是“pal5q.jpg”這張圖了。

另外,如果場景中繪製每個物體模型所使用的紋理不相同,那麼在繪製每個物體模型之前都需要呼叫該方法設定對應的紋理。也就是說當前在用的紋理就一種,就像上場打比賽的就一名隊員,其他的都是替補,要換紋理的話,就用SetTexture申請換人。

下面我們依舊來個例項:

[cpp] view plaincopyprint?
  1. g_pd3dDevice->SetTexture(0,&g_pTexture1); //設定第一個物體的紋理
  2. g_pMesh1->DrawSubset(0); //進行第一個物體的繪製
  3. g_pd3dDevice->SetTexture(0,&g_ pTexture2); //設定第一個物體的紋理
  4. g_pMesh2->DrawSubset(0); //進行第二個物體的繪製
[cpp] view plaincopyprint?
  1. g_pd3dDevice->SetTexture(0,&g_pTexture1);  //設定第一個物體的紋理
  2. g_pMesh1->DrawSubset(0);                              //進行第一個物體的繪製
  3. g_pd3dDevice->SetTexture(0,&g_ pTexture2);  //設定第一個物體的紋理
  4. g_pMesh2->DrawSubset(0);                              //進行第二個物體的繪製
g_pd3dDevice->SetTexture(0,&g_pTexture1);  //設定第一個物體的紋理
g_pMesh1->DrawSubset(0);                              //進行第一個物體的繪製
 
g_pd3dDevice->SetTexture(0,&g_ pTexture2);  //設定第一個物體的紋理
g_pMesh2->DrawSubset(0);                              //進行第二個物體的繪製





四,總結與昇華

上面使用方法講解中夾雜著各種概念講解,講得比較散,為了方便想快速上手掌握並使用紋理對映技術的朋友們,下面我們來做個知識整理,突出一下重點。

Ⅰ.四步曲的大綱

首先是這四步曲的大綱,紋理對映使用四步曲,簡明扼要20個字:

頂點的定義,頂點的訪問,紋理的建立,紋理的啟用。

Ⅱ.知識點的關鍵字總結

接著我們以關鍵字的形式來總結一下這四步的知識點:

四步曲之一、頂點的定義。

FVF頂點格式的定義,最多八層紋理

四步曲之二、頂點的訪問。

訪問頂點快取內容,紋理座標的填充

四步曲之三、紋理的建立。

一個物件:IDIRECT3DTEXTURE9介面物件,

一個方法:D3DXCreateTextureFromFile方法

四步曲之四、紋理的啟用。

一個方法:SetTexture方法

Ⅲ.以程式碼為載體記憶

下面我們給一段程式碼,並分這四步做了註釋,大家若是要快速掌握Direct3D中繪製紋理方法,消化這段程式碼就可以了。

[cpp] view plaincopyprint?
  1. //--------------------------------------------------------------------------------------
  2. //【紋理繪製四步曲之一】:頂點的定義
  3. //--------------------------------------------------------------------------------------
  4. struct CUSTOMVERTEX
  5. {
  6. FLOAT_x, _y, _z; // 頂點的位置
  7. FLOAT_u, _v; // 紋理座標
  8. CUSTOMVERTEX(FLOATx, FLOAT y, FLOAT z, FLOAT u, FLOAT v)
  9. :_x(x), _y(y), _z(z), _u(u), _v(v) {}
  10. };
  11. #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ |D3DFVF_TEX1)
  12. //--------------------------------------------------------------------------------------
  13. //【紋理繪製四步曲之二】:頂點的訪問
  14. //--------------------------------------------------------------------------------------
  15. //填充頂點快取
  16. CUSTOMVERTEX*pVertices;
  17. if(FAILED( g_pVertexBuffer->Lock( 0, sizeof(CUSTOMVERTEX),(void**)&pVertices, 0 ) ) )
  18. returnE_FAIL;
  19. //填充資料
  20. pVertices[0]= CUSTOMVERTEX(-10.0f, 10.0f, -10.0f,0.0f, 0.0f);
  21. pVertices[1]= CUSTOMVERTEX( 10.0f, 10.0f, -10.0f,1.0f, 0.0f);
  22. pVertices[2]= CUSTOMVERTEX( 10.0f, -10.0f, -10.0f, 1.0f, 1.0f);
  23. pVertices[3]= CUSTOMVERTEX(-10.0f, -10.0f, -10.0f, 0.0f, 1.0f);
  24. g_pVertexBuffer->Unlock();
  25. //--------------------------------------------------------------------------------------
  26. // 【紋理繪製四步曲之三】:紋理的建立
  27. //--------------------------------------------------------------------------------------
  28. LPDIRECT3DTEXTURE9 g_pTexture = NULL; // 紋理介面物件
  29. D3DXCreateTextureFromFile(g_pd3dDevice,L"pal5q.jpg", &g_pTexture); // 建立紋理
  30. //--------------------------------------------------------------------------------------
  31. // 【紋理繪製四步曲之四】:紋理的啟用
  32. //--------------------------------------------------------------------------------------
  33. g_pd3dDevice->BeginScene(); // 繪製五步曲之二
  34. g_pd3dDevice->SetTexture(0, g_pTexture);
  35. /*紋理設定完之後,就開始繪製,用DrawIndexedPrimitive,DrawSubset 之類的函式*/
  36. g_pd3dDevice->EndScene(); // 結束繪製
[cpp] view plaincopyprint?
  1. //--------------------------------------------------------------------------------------
  2. //【紋理繪製四步曲之一】:頂點的定義              
  3. //--------------------------------------------------------------------------------------
  4. struct CUSTOMVERTEX   
  5. {  
  6.        FLOAT_x, _y, _z;               // 頂點的位置
  7.        FLOAT_u, _v;                   // 紋理座標
  8.        CUSTOMVERTEX(FLOATx, FLOAT y, FLOAT z, FLOAT u, FLOAT v)  
  9.               :_x(x), _y(y), _z(z), _u(u), _v(v) {}  
  10. };  
  11. #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ |D3DFVF_TEX1)
  12. //--------------------------------------------------------------------------------------
  13. //【紋理繪製四步曲之二】:頂點的訪問              
  14. //--------------------------------------------------------------------------------------       
  15. //填充頂點快取
  16.               CUSTOMVERTEX*pVertices;  
  17.               if(FAILED( g_pVertexBuffer->Lock( 0, sizeof(CUSTOMVERTEX),(void**)&pVertices, 0 ) ) )  
  18.                      returnE_FAIL;  
  19.               //填充資料
  20.               pVertices[0]= CUSTOMVERTEX(-10.0f,  10.0f, -10.0f,0.0f, 0.0f);  
  21.               pVertices[1]= CUSTOMVERTEX( 10.0f,  10.0f, -10.0f,1.0f, 0.0f);  
  22.               pVertices[2]= CUSTOMVERTEX( 10.0f, -10.0f, -10.0f, 1.0f, 1.0f);  
  23.               pVertices[3]= CUSTOMVERTEX(-10.0f, -10.0f, -10.0f, 0.0f, 1.0f);  
  24. g_pVertexBuffer->Unlock();  
  25. //--------------------------------------------------------------------------------------
  26. // 【紋理繪製四步曲之三】:紋理的建立
  27. //--------------------------------------------------------------------------------------
  28. LPDIRECT3DTEXTURE9      g_pTexture   = NULL;   // 紋理介面物件
  29. D3DXCreateTextureFromFile(g_pd3dDevice,L"pal5q.jpg", &g_pTexture); // 建立紋理
  30. //--------------------------------------------------------------------------------------
  31. // 【紋理繪製四步曲之四】:紋理的啟用
  32. //--------------------------------------------------------------------------------------
  33. g_pd3dDevice->BeginScene();                    // 繪製五步曲之二
  34. g_pd3dDevice->SetTexture(0, g_pTexture);  
  35. /*紋理設定完之後,就開始繪製,用DrawIndexedPrimitive,DrawSubset 之類的函式*/
  36. g_pd3dDevice->EndScene();                       // 結束繪製
//--------------------------------------------------------------------------------------
//【紋理繪製四步曲之一】:頂點的定義              
//--------------------------------------------------------------------------------------
 
struct CUSTOMVERTEX 
{
       FLOAT_x, _y, _z;               // 頂點的位置
       FLOAT_u, _v;                   // 紋理座標
       CUSTOMVERTEX(FLOATx, FLOAT y, FLOAT z, FLOAT u, FLOAT v)
              :_x(x), _y(y), _z(z), _u(u), _v(v) {}
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ |D3DFVF_TEX1)
 
//---------------------------------------------------