從本篇文章開始我們就來開始來學習固定渲染流水線這套渲染體系。
其實固定渲染流水線和之後我們要學習的可編程渲染流水線體系有很多異曲同工之妙,所以先學習固定功能渲染流水線體系,再學可編程渲染流水線體系,就可以循序漸進,步步爲營地掌握好DirectX。
空間中的物體需要使用三維座標來描述,而我們的顯示器是顯然是二維的,所以在屏幕上渲染一個三維場景時,首先需要將物體描述空間物體的三維座標變換爲二維座標(也就是世界座標到屏幕座標),這在Direct3D中稱爲頂點座標變換。頂點座標變換通常通過矩陣來完成。我們之前的幾個demo中,演示的是如何顯示一個二維的平面圖形,它的頂點是以屏幕座標系的二維值表示的,也是經過頂點座標變換之後的頂點座標數據,可以把頂點座標變換想象成攝像的過程,三維世界的景物通過攝像機拍攝顯示在二維的相片之上,有所不同的是把相片換成了屏幕。
文章開頭,我們先來看一下固定功能渲染流水線這套渲染體系的核心思想。
一、固定功能渲染流水線概述
在固定功能渲染流水線這套體系中,大體分爲兩個階段,第一階段我們將它稱爲座標變換和光照處理階段(Transforming &Lighting,簡稱T&L階段)。在這個階段中,每個對象的頂點從一個抽象的、浮點座標變換到基於像素的屏幕空間當中。這裏需要注意的是,座標變換不僅包含物體頂點位置,它還可能包括頂點的法線、紋理座標等等。並根據場景中光源和物體表面的材質對物體頂點應用不同類型的光照效果。還有其他一些比較重要的任務,比如視口的設置和裁剪也是在第一階段進行的。講完第一階段,我們再來看看第二階段,第二階段稱爲光柵化處理階段。頂點在經過第一階段也就是變換與光照階段的“洗禮”之後,已經略有雛形,在第二階段,Direct3D將這些已經完成變換和光照階段的頂點組織爲以點、線、面爲基礎的圖元,應用紋理貼圖和物體頂點的顏色屬性,並根據相關渲染狀態的設置(比如着色模式等)
,決定每個像素最終的顏色值,並且在屏幕上顯示出來。爲了大家更宏觀和更深入的理解,淺墨依然是配了一幅圖。通過這幅圖,大家可以對固定功能渲染流水線的結構脈絡做到一目瞭然。
需要注意的是,渲染流水線中的步驟並不一定都要有的,根據實際情況可以省略一些。比如之前我們給出的幾個demo,都是省略了變換和光照階段,直接將頂點作爲屏幕座標輸出顯示。因爲我們在定義頂點的屬性的時候,給我們的頂點定的“標籤”是D3DFVF_XYZRHW,表示包含經過座標變換的頂點座標值,這樣Direct3D就知道這些頂點座標不需要再經過頂點座標變換了,他們的座標值就是最終顯示屏幕上的座標值了。
目前我們重點介紹座標變換和光照處理階段(Transforming &Lighting,簡稱T&L階段),也就是T&L階段。在這個過程中,未經過變換和光照的頂點從一端進入,在流水線內部這些頂點將完成幾個連續的操作,這幾個操作按順序分別爲世界變換,取景變換,光照處理,投影變換以及視口變換。經過這些處理之後的頂點從另一端出來,表示已經完成座標變換和光照處理了。我們的應用程序是通過指定幾個矩陣、視口以及所使用的光線來建立T&L流水線的,然後應用程序將頂點送入流水線,並對這些頂點在流水線中進行座標變換、照明以及裁剪,將其投影到屏幕空間當中,並根據視口的規定對其進行縮放。頂點在T&L流水線中進過“涅槃”之後,就可以去到第二階段——光柵化處理階段去完成新的試煉了。
關於變換和光照渲染流水線階段,也就是T&L階段,淺墨也配了一幅圖,方便大家的記憶與理解:
二、攜手邁向三維世界:四大變換顯身手
首先來講講周邊的概念。
在Direct3D中,如果我們未進行任何空間座標變換而來繪製圖形的話,圖形將始終處於應用程序窗口的中心位置,在默認情況下這個位置就會成爲世界座標系的原點(0,0,0)。另外,我們也不能改變觀察圖形的視角方向。默認情況下的觀察方向是世界座標系的z軸正方向,也就是垂直程序窗口並向裏觀察的方向。之前我們的幾個demo都是默認的z軸正方向爲觀察方向的。
而爲了邁向三維世界,爲了能夠隨心所欲地在三維世界中繪製和觀察遊戲畫面,就是四大變換的show time了。
所謂四大變換,其實是淺墨自己在學習Direct3D的時候自己歸納的一個東西- -,也就是世界變換,取景變換,投影變換和視口變換的總稱。
下面我們先來大致概括一下這四大變換的用途。
1. 爲了能在世界空間中的指定位置來繪製圖形,就需要在繪製圖形前進行四大變換之一的世界變換運算。
2. 爲了以不同的視角觀察圖形,就需要用到四大變換之二的取景變換運算。
3. 爲了將相對較遠的圖形投影到同一個平面上並體現出“近大遠小”的真實視覺效果,就需要用到四大變換之三的投影變換。
4. 爲了控制顯示圖形的窗口的大小,比例以及深度等等信息,就要用到四大變換之四的視口變換。
下面我們就開始分別展開講解這四大變換的細節知識。在講解之前,有必要先跟大家說明一下。這裏的四大變換的前三都是以矩陣作爲載體的。這四大變換相關知識中或多或少會涉及到矩陣、向量,平面和射線等相關的數學知識,這些數學知識淺墨不準備專門花篇幅講,因爲講起來都是概念,漫無目的,而且篇幅很長或許會花費幾次更新的時間,況且寫出來的東西肯定沒新意而且沒勁,所以沒有寫出來的必要。如果去學習Direct3D這款API的話,大可不必去糾結那麼多數學概念。對矩陣和向量稍微有些瞭解就足夠了,餘下的高中數學(高中數學中沒有矩陣相關的知識,所以矩陣相關概念需要另外學習)完全可以對付。如果實際過程中有不理解的地方再有的放矢地去查書和資料,這樣學習起來會輕鬆很多,學習效率也會高得多。關於這方面數學知識的查閱資料,可以參看Direct3D 9龍書《Direct3D 9.0 3D遊戲開發編程基礎》的第一部分,Frank Luna已經爲我們講解得非常到位了。
好了,下面就開始依次講解吧。
Ⅰ.四大變換之一:世界變換
根據物體模型的大小、方向以及其他模型之間的相對關係,世界變換將物體模型從自身的局部座標系中轉換到時間座標系中,並將所有的物體模型組織爲一個場景,如下圖:
就像這個世界上有很多不同的個體,我們每個個體都有自己獨特的局部座標系,而我們每個個體僅僅是這個世界中的一份子而已。就像那句英文名言的前半句一樣,to the world,you maybe one person。
世界變換包括平移、旋轉和縮放變換,我們可以通過D3DX庫中D3DXMatrixTranslation、D3DXMatrixRotation*和D3DXMatrixSaling函數來進行變換,並得到一個世界變換矩陣。
其中D3DXMatrixTranslation用於矩陣的平移操作,D3DXMatrixRotation*用於矩陣的旋轉操作,D3DXMatrixSaling用於矩陣的縮放操作(稍後會分別介紹)。這三種變換是矩陣的最常用的變換。
調用這些函數將我們矩陣調整好之後,接着我們就調用IDirect3DDevice9接口的SetTransform方法來運用世界變換矩陣,表示認定某某矩陣就是我們的世界變換矩陣了。
我們可以在DirectX SDK中查到SetTransform方法的原型如下:
- HRESULT SetTransform(
- [in] D3DTRANSFORMSTATETYPE State,
- [in] const D3DMATRIX *pMatrix
- );<span style="font-family:Microsoft YaHei;font-size:14px;"> </span>
■ 第一個參數,D3DTRANSFORMSTATETYPE類型的State,明顯的可以看到,它是一個D3DTRANSFORMSTATETYPE枚舉類型,用於表示變換的類型,是四大變換前三者之中的哪一種。可以取值爲D3DTS_WORLD,表示世界矩陣,或者是D3DTRANSFORMSTATETYPE枚舉中的一種,這個D3DTRANSFORMSTATETYPE枚舉類型定義如下:
- typedef enum D3DTRANSFORMSTATETYPE {
- D3DTS_VIEW = 2,
- D3DTS_PROJECTION = 3,
- D3DTS_TEXTURE0 = 16,
- D3DTS_TEXTURE1 = 17,
- D3DTS_TEXTURE2 = 18,
- D3DTS_TEXTURE3 = 19,
- D3DTS_TEXTURE4 = 20,
- D3DTS_TEXTURE5 = 21,
- D3DTS_TEXTURE6 = 22,
- D3DTS_TEXTURE7 = 23,
- D3DTS_FORCE_DWORD = 0x7fffffff
- } D3DTRANSFORMSTATETYPE, *LPD3DTRANSFORMSTATETYPE;
其中D3DTS_VIEW表示取景變換,D3DTS_PROJECTION表示投影變換。而D3DTS_TEXTURE0~7顯然表示的就是某層紋理(0到7層)對應的變換矩陣了 。
第二個參數,const D3DMATRIX類型的*pMatrix,是一個實實在在有內容的矩陣,顯然就是我們想要設置爲和第一個參數中指定的類型相掛鉤的候選人矩陣了。
接着我們來分別介紹經常會用到的矩陣的一些基本變換方法:
1.矩陣的平移
首先介紹D3DXMatrixTranslation方法,D3D中的平移函數。
在Direct3D裏,平移矩陣應是使用最多的矩陣。這是因爲每個物體的相對位置,都是通過平移矩陣來創造出來的。物體的整體移動,說白了就是座標點的移動。比如從點(x,y,z)移動到新的位置(x', y', z')。在Direct3D就爲我們提供了D3DXMatrixTranslation方法用於矩陣的平移。我們先來看一下這個函數的原型:
- D3DXMATRIX * D3DXMatrixTranslation(
- __inout D3DXMATRIX *pOut,
- __in FLOAT x,
- __in FLOAT y,
- __in FLOAT z
- );
這個函數其實就是在創造一個有相對於原點(0,0,0)有偏移量的矩陣出來。我們先來看一下各個參數的意義。
█ 第一個參數,D3DXMATRIX類型的*pOut,從類型上來看我們就知道他是一個D3DXMATRIX類型的4 X 4的矩陣,我們調用這個D3DXMatrixTranslation方法,其實就是在爲這個矩陣賦值,讓這個矩陣相對於原點有一個偏移量,也就是我們需要平移的距離。
而具體的平移操作其實並不是由這個函數完成的。,這個函數其實是在創建一個平移矩陣。
█ 第二個參數,FLOAT類型的x,顯然是X軸的平移量。
█ 第三個參數,FLOAT類型的y,顯然是Y軸的平移量。
█ 第四個參數,FLOAT類型的z,顯然是Z軸的平移量。
比如要沿着Z軸的正方向平移10個單位,就需要按下面來設置平移矩陣:
- D3DXMATRIX mTrans;
- D3DXMatrixTranslation(&mTrans,0,0,10);
然後我們把需要進行平移操作的矩陣,乘以這個創建好mTrans矩陣,就完成了平移操作。比如說我們有一個mMtrix矩陣,我們需要這個mMtrix矩陣沿Z軸正方向平移10個單位,就讓mTrans和mMtrix相乘就可以了,他們相乘的結果就是mMtrix矩陣向上平移10個單位後的矩陣。其中相乘操作用 D3DXMatrixMultiply來完成。
- D3DXMATRIX * D3DXMatrixMultiply(
- __inout D3DXMATRIX *pOut,
- __in const D3DXMATRIX *pM1,
- __in const D3DXMATRIX *pM2
- );
第一個參數,爲輸出的結果。第二和第三個參數爲參加乘法的兩個矩陣,且pM1在左,pM2在右,因爲矩陣相乘涉及到相乘順序的問題。這個函數的作用可以用一個式子來表示,也就是pM1 * pM2=pOut。
所以,整體來看,要把mMtrix矩陣向Z軸正方向平移10個單位,就是如下的代碼:
- D3DXMATRIX mTrans;
- D3DXMatrixTranslation(&mTrans,0,0,10);
- D3DXMatrixMultiply(&mMtrix,&mMtrix,&&mTrans);
再舉一個例子,如果我們僅僅是將一個物體沿X軸正方向平移5個單位,Y軸負方向平移3個單位就可以了,也就是隻做了平移操作,就可以直接把我們創建的這個中間矩陣作爲世界矩陣使用,也就是如下代碼:
- D3DXMATRIX mTrans;
- D3DXMatrixTranslation(&mTrans,5,-3,0);
- g_pd3dDevice->SetTransform(D3DTS_WORLD,&mTrans);
2.矩陣的旋轉
旋轉和平移類似,也是先用一個函數創建好用於旋轉的一箇中間矩陣,然後讓我們需要旋轉的那個矩陣右乘這個中間矩陣就好了。關於這個旋轉中間矩陣的創建,用的就是地d9
dx9.lib庫中的D3DXMatrixRotationX, D3DXMatrixRotationY以及D3DXMatrixRotationZ。這三個函數非常相似,我們就打包起來介紹得了,這三個函數的原型聲明如下:
- D3DXMATRIX * D3DXMatrixRotationX(
- __inout D3DXMATRIX *pOut,
- __in FLOAT Angle
- );
- D3DXMATRIX * D3DXMatrixRotationY(
- __inout D3DXMATRIX *pOut,
- __in FLOAT Angle
- );
- D3DXMATRIX * D3DXMatrixRotationZ(
- __inout D3DXMATRIX *pOut,
- __in FLOAT Angle
- );
█ 第一個參數,D3DXMATRIX類型的*pOut,依然是作爲輸出結果的旋轉矩陣。
█ 第二個參數,FLOAT 類型的angle表示要旋轉的弧度值。
下面繼續來舉例子,同樣,如果我們只對某個物體在世界座標系中進行旋轉操作的話,也可以把D3DXMatrixRotationX系列函數創造出來的中間矩陣作爲我們的世界矩陣。
若要將一個對象沿Y軸旋轉90度,也就是如下代碼:
- D3DXMATRIX mTrans;
- float fAngle=90*(2.0f*D3DX_PI)/360.0f;
- D3DXMatrixRotationY(&mTrans, fAngle);
- g_pd3dDevice->SetTransform(D3DTS_WORLD,&mTrans);
3.矩陣的縮放
與旋轉和平移類似,矩陣縮放也是先用一個函數創建好用於縮放的一箇中間矩陣,然後讓我們需要縮放那個矩陣右乘這個中間矩陣就好了。關於這個縮放中間矩陣的創建,爲D3DXMatrixScaling函數,這個函數的原型如下:
- D3DXMATRIX * D3DXMatrixScaling(
- __inout D3DXMATRIX *pOut,
- __in FLOAT sx,
- __in FLOAT sy,
- __in FLOAT sz
- );
█ 第一個參數,D3DXMATRIX類型的*pOut,依然是作爲輸出結果的縮放矩陣。
█ 第二個參數到第四個參數,顯然就是浮點型的X,Y,Z軸上的縮放比例了。
比如,要將一個物體在Z軸上放大5倍,代碼就是這樣寫:
- D3DXMATRIX mTrans;
- D3DXMatrixScaling(&mTrans,1.0f,1.0f,5.0f);
- g_pd3dDevice->SetTransform(D3DTS_WORLD,&mTrans);
這一小節的最後,最後我們來一個綜合的調用實例,比如要將一個物體在X軸上放大3倍,然後又繞Y軸旋轉120度,最後又沿Z軸平移正方向10個單位,實現代碼如下:
- D3DXMATRIX matWorld;
- D3DXMATRIX matTranslate,matRotation,matScale;
- D3DXMatrixScaling( & matScale,3.0f,1.0f,1.0f);
- float fAngle=120*(2.0f*D3DX_PI)/360.0f;
- D3DXMatrixRotationY(&matRotation, fAngle);
- D3DXMatrixMultiply(&matWorld,& matScale,& matRotation);
- D3DXMatrixTranslation(&matTranslate,0.0f,0.0f,10.0f);
- D3DXMatrixMultiply (&matWorld ,&matWorld,& matTranslate);
- g_pd3dDevice->SetTransform(D3DTS_WORLD,&mTrans);
另外提一個單位化矩陣的函數,也就是D3DXMatrixIdentity函數。其用法非常簡單,唯一的參數就是單位化之後的輸出矩陣,也就是這樣寫:
D3DXMATRIX matWorld;
D3DXMatrixIdentity(&matWorld); // 單位化世界矩陣
另外再提一點,關於矩陣的乘法,因爲Direct3D對D3DXMATRIX矩陣類型進行了擴展,對矩陣乘法進行了重載,所以矩陣的乘法運算可以不用拘泥於上面講到的D3DXMatrixMultiply()來實現了,可以簡單地使用乘法運算符“*”就可以了,即多個矩陣的乘積可以這樣寫:
matAnswer=mat1*mat2*mat3*mat4……*matN
Ⅱ.四大變換之二:取景變換
取景變換,人如其名,就是用來取景的,也就是設置Direct3D中的虛擬攝像機的位置和觀察點。對於處於不同位置的虛擬攝像機和觀察點,其觀察物體模型的視角方向也有所差異,因此實際看到的物體模型的實際形狀也有所不同。正所謂“橫看成嶺側成峯”,就像這幅圖所傳達的觀點一樣:
爲了確定一個虛擬攝像機的位置和觀察方向,需要指定虛擬攝像機在世界座標系中的位置、觀察點位置以及正方向。爲了能夠進行取景變換,首先需要通過D3DX庫中的D3DXMatrixLookAtLH函數計算並得到一個取景變換矩陣(或觀察矩陣),然後同樣調用IDirect3DDevice接口的SetTransform方法應用取景變換。
其中,D3DXMatrixLookAtLH函數的聲明如下:
- D3DXMATRIX * D3DXMatrixLookAtLH(
- __inout D3DXMATRIX *pOut,
- __in const D3DXVECTOR3 *pEye,
- __in const D3DXVECTOR3 *pAt,
- __in const D3DXVECTOR3 *pUp
- );
然後依然是函數參數的介紹:
█ 第一個參數,D3DXMATRIX類型的*pOut,最終生成的觀察矩陣。
█ 第二個參數,const D3DXVECTOR3類型的*pEye,指定虛擬攝像機在世界座標系中的位置。
█ 第三個參數,const D3DXVECTOR3類型的*pAt,爲觀察點在世界座標系中的位置。
█ 第四個參數,const D3DXVECTOR3類型的*pUp,爲攝像機的上向量,通常設爲(0,1,0)就可以了。
依然是一個使用的例子,代碼如下:
- //建立並設置觀察矩陣
- D3DXVECTOR3 vEyePt( 0.0f, 8.0f,-10.0f ); //攝像機的位置
- D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );//觀察點的位置
- D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );//向上的向量
- D3DXMATRIX matView;//定義一個世界矩陣
- D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );//計算出取景變換矩陣
- g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); //應用取景變換矩陣
Ⅲ.四大變換之三:投影變換
經過上一步的取景變換之後,物體模型就位於觀察座標系中了,然而爲了能夠將三維場景顯示在我們二維的顯示平面上(因爲我們的顯示屏是二維的),還需要通過投影變換將三維物體投影到二維的平面上,這個過程我們就把它叫做透視投影。下面這幅圖中以及顯示得很清楚了:
我們可以看到,攝像機的距離和角度唯一確定了落地窗的大小。
就好比一個人站在的一間封閉房間裏,房間唯一可以接收到光線的地方是一塊高大的落地窗,而房間外面對着落地窗這邊的,是一堵無限長無限寬,綿延到世界盡頭的白色的牆。人眼透過落地窗看到房子外邊三維的世界,最終投影在了那堵二維的牆上。
顯然,投影窗口是一個二維平面,用於描述三維物體模型經過透視投影后的二維圖像,在Direct3D中投影窗口平面默認定義爲z=1的平面。
虛擬攝像機與投影窗口平面共同構成了一個對觀察者可見的三維空間。在3D圖形學中這部分空間被稱作視截體(View Frustum),位於視截體內的物體模型被映射到二維投影投影平面上,而位於視截體外的物體模型或者其中一部分將不可見,這個過程我們稱作裁剪(Clipping),(取屏幕內的景物,專注,屏幕外的一律無視)。
投影變換負責將位於視截體內的物體模型映射到投影窗口中。D3DX庫中的D3DXMatrixPerspectiveFovLH函數可以用來計算一個視截體,並根據該視截體的描述信息創建一個投影矩陣變換。這個D3DXMatrixPerspectiveFovLH方法可以在MSDN中查到如下函數原型;
- D3DXMATRIX * D3DXMatrixPerspectiveFovLH(
- __inout D3DXMATRIX *pOut,
- __in FLOAT fovy,
- __in FLOAT Aspect,
- __in FLOAT zn,
- __in FLOAT zf
- );
■ 第一個參數,D3DXMATRIX類型的*pOut,它爲我們最終生成的投影變換矩陣。
■ 第二個參數,FLOAT類型的fovy,用於指定以弧度爲單位的虛擬攝像機在y軸上的成像角度,即視域角度(View of View),成像角度越大,映射到投影窗口中的圖形就越小;反之,投影圖像就越大。
■ 第三個參數,FLOAT類型的Aspect,用於描述屏幕顯示區的橫縱比,他的值就爲屏幕的寬度/高度。對應不同比例的顯示屏幕,比如16/9,4/3等等,最終顯示的投影圖像可能會使圖像被拉伸
■ 第四個參數, FLOAT類型的zn,表示視截體中近裁剪面距我們攝像機的位置,即人眼到“室內落地窗”之間的距離
■ 第五個參數,FLOAT類型的zf,表示視截體中遠裁剪面距我們攝像機的位置,即人眼到“室外黑色牆壁”之間的距離
依舊是一個調用實例:
- //建立並設置投影矩陣
- D3DXMATRIXA16 matProj;
- float aspect = (float)(800/600);
- D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, aspect, 1.0f, 100.0f );
- g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
Ⅳ. 四大變換之四:視口變換
終於講到四大變換的最後一個了。其實這一步非常的簡單,也就是填下填空題,然後設置一下就好了。並不像前三大變換一樣要用到矩陣相關的知識。下面就一起來具體看看視口變換的相關概念吧。
視口變換用於將投影窗口中的圖形轉換到顯示屏幕的程序窗口中。視口是程序窗口中前的一個矩形區域,他可以是整個程序窗口,也可以是窗口的客戶區,也可以是窗口中其他矩形區域,如圖所示:
在Direct3D中,視口是由D3DVIEWPROT9結構體來描述的,其中定義了視口的位置,寬度高度等信息,在DirectX SDK 中我們可以查到該結構體的聲明如下:
- typedef struct D3DVIEWPORT9 {
- DWORD X; //表示視口相對於窗口的X座標
- DWORD Y; //視口相對對窗口的Y座標
- DWORD Width;//視口的寬度
- DWORD Height;//視口的高度
- float MinZ;//視口在深度緩存中的最小深度值
- float MaxZ; //視口在深度緩存中的最大深度值
- } D3DVIEWPORT9, *LPD3DVIEWPORT9;
講解完概念下面依舊是調用的實例:
方法一:
先做填空題,做完之後調用IDirect3D9Device9接口的SetViewPort方法設置當前窗口中的視口,然後Direct3D將自動完成視口變換。
- D3DVIEWPORT9 vp={0,0,800,600,0,1};
- g_pD3dDevice->SetViewport(&vp);
方法二:
結構體的內容別開來賦值:
- D3DVIEWPORT9 vp;
- vp.X = 0;
- vp.Y = 0;
- vp.Width = 800;
- vp.Height = 600;
- vp.MinZ = 0.0f;
- vp.MaxZ = 1.0f;
- g_pd3dDevice->SetViewport(&vp);
其實也就是填結構體填空題的兩種方式而已。
Ⅴ.總結
四大變換分爲:世界變換,取景變換,投影變換,視口變換。
1.爲了能在世界空間中的指定位置來繪製圖形,就需要在繪製圖形前進行四大變換之一的世界變換運算。
2.爲了以不同的視角觀察圖形,就需要用到四大變換之二的取景變換運算。
3.爲了將相對較遠的圖形投影到同一個平面上並體現出“近大遠小”的真實視覺效果,就需要用到四大變換之三的投影變換。
4.爲了控制顯示圖形的窗口的大小,比例以及深度等等信息,就要用到四大變換之四的視口變換。
三,詳細註釋的源代碼欣賞
這裏依舊是通過一個小程序,來把本篇文章所學的知識融會貫通。這篇文章裏我們講解了繪製3D圖形時用到的四大變換,下面就是一個利用四大變換來繪製一個轉動的顏色隨機的彩色立方體的demo。我們將四大變換封裝在了一個全局的Matrix_Set()函數中了,並在渲染五步曲之三中進行了Matrix_Set()函數的調用。
在放出完整的源代碼之前,讓我們首先我們來看一下本篇文章介紹的核心知識,也就是實現了四大變換封裝的Matrix_Set()函數的具體實現方法:
- //*****************************************************************************************
- // Name:Matrix_Set()
- // Desc: 設置世界矩陣
- // Point:【Direct3D四大變換】
- // 1.【四大變換之一】:世界變換矩陣的設置
- // 2.【四大變換之二】:取景變換矩陣的設置
- // 3.【四大變換之三】:投影變換矩陣的設置
- // 4.【四大變換之四】:視口變換的設置
- //*****************************************************************************************
- VOID Matrix_Set()
- {
- //--------------------------------------------------------------------------------------
- //【四大變換之一】:世界變換矩陣的設置
- //--------------------------------------------------------------------------------------
- D3DXMATRIX matWorld, Rx, Ry, Rz;
- D3DXMatrixIdentity(&matWorld); // 單位化世界矩陣
- D3DXMatrixRotationX(&Rx, D3DX_PI *(::timeGetTime() / 1000.0f)); // 繞X軸旋轉
- D3DXMatrixRotationY(&Ry, D3DX_PI *( ::timeGetTime() / 1000.0f/2)); // 繞Y軸旋轉
- D3DXMatrixRotationZ(&Rz, D3DX_PI *( ::timeGetTime() / 1000.0f/3)); // 繞Z軸旋轉
- matWorld = Rx * Ry * Rz * matWorld; // 得到最終的組合矩陣
- g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld); //設置世界變換矩陣
- //--------------------------------------------------------------------------------------
- //【四大變換之二】:取景變換矩陣的設置
- //--------------------------------------------------------------------------------------
- D3DXMATRIX matView; //定義一個矩陣
- D3DXVECTOR3 vEye(0.0f, 0.0f, -200.0f); //攝像機的位置
- D3DXVECTOR3 vAt(0.0f, 0.0f, 0.0f); //觀察點的位置
- D3DXVECTOR3 vUp(0.0f, 1.0f, 0.0f);//向上的向量
- D3DXMatrixLookAtLH(&matView, &vEye, &vAt, &vUp); //計算出取景變換矩陣
- g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView); //應用取景變換矩陣
- //--------------------------------------------------------------------------------------
- //【四大變換之三】:投影變換矩陣的設置
- //--------------------------------------------------------------------------------------
- D3DXMATRIX matProj; //定義一個矩陣
- D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4.0f, 1.0f, 1.0f, 1000.0f); //計算投影變換矩陣
- g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj); //設置投影變換矩陣
- //--------------------------------------------------------------------------------------
- //【四大變換之四】:視口變換的設置
- //--------------------------------------------------------------------------------------
- D3DVIEWPORT9 vp; //實例化一個D3DVIEWPORT9結構體,然後做填空題給各個參數賦值就可以了
- vp.X = 0; //表示視口相對於窗口的X座標
- vp.Y = 0; //視口相對對窗口的Y座標
- vp.Width = SCREEN_WIDTH; //視口的寬度
- vp.Height = SCREEN_HEIGHT; //視口的高度
- vp.MinZ = 0.0f; //視口在深度緩存中的最小深度值
- vp.MaxZ = 1.0f; //視口在深度緩存中的最大深度值
- g_pd3dDevice->SetViewport(&vp); //視口的設置
- }
其中timeGetTime方法以毫秒爲時間單位返回從Windows系統開機時起所經過的時間。這樣就可以通過(::timeGetTime() / 1000.0f)這個式子來構造一個從0到1的連續的時間週期.而D3DX_PI 爲Direct3D中定義的一個宏,原型爲:
- #define D3DX_PI ((FLOAT) 3.141592654f)
接着我們就貼出這個小程序全部的詳細註釋的源代碼,按鍵盤上的1鍵和2鍵可以在線框填充模式和實體填充模式之間切換:
- //*****************************************************************************************
- //
- //【Visual C++】遊戲開發筆記系列配套源碼 三十八 淺墨DirectX提高班之六 攜手邁向三維世界:四大變換展身手
- // VS2010版
- // 2012年 12月23日 Create by 淺墨
- //圖標素材: Dota2 白虎第1技能 流星
- //源碼配套博文鏈接:
- //更多內容請訪問我的博客: http://blog.csdn.net/zhmxy555
- //此刻心情:耿耿於懷着過去和忐忑不安着未來的人,也常常揮霍無度着現在。希望我們都做那個把握好現在的人。
- //
- //*****************************************************************************************
- //*****************************************************************************************
- // Desc: 頭文件定義部分
- //*****************************************************************************************
- #include <d3d9.h>
- #include <d3dx9.h>
- #include <tchar.h>
- #include <time.h>
- //*****************************************************************************************
- // Desc: 庫文件定義部分
- //*****************************************************************************************
- #pragma comment(lib,"d3d9.lib")
- #pragma comment(lib,"d3dx9.lib")
- #pragma comment(lib, "winmm.lib ")
- //*****************************************************************************************
- // Desc: 宏定義部分
- //*****************************************************************************************
- #define SCREEN_WIDTH 800 //爲窗口寬度定義的宏,以方便在此處修改窗口寬度
- #define SCREEN_HEIGHT 600 //爲窗口高度定義的宏,以方便在此處修改窗口高度
- #define WINDOW_TITLE _T("【Visual C++遊戲開發筆記】博文配套demo之三十八 淺墨DirectX提高班之六 攜手邁向三維世界:四大變換展身手") //爲窗口標題定義的宏
- #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } //自定義一個SAFE_RELEASE()宏,便於資源的釋放
- //*****************************************************************************************
- // 【頂點緩存、索引緩存繪圖四步曲之一】:設計頂點格式
- //*****************************************************************************************
- struct CUSTOMVERTEX
- {
- FLOAT x, y, z;
- DWORD color;
- };
- #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE) //FVF靈活頂點格式
- //*****************************************************************************************
- // Desc: 全局變量聲明部分
- //*****************************************************************************************
- LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; //Direct3D設備對象
- ID3DXFont* g_pFont=NULL; //字體COM接口
- float g_FPS = 0.0f; //一個浮點型的變量,代表幀速率
- wchar_t g_strFPS[50]; //包含幀速率的字符數組
- LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer = NULL; //頂點緩存對象
- LPDIRECT3DINDEXBUFFER9 g_pIndexBuffer = NULL; // 索引緩存對象
- //*****************************************************************************************
- // Desc: 全局函數聲明部分
- //*****************************************************************************************
- LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
- HRESULT Direct3D_Init(HWND hwnd);
- HRESULT Objects_Init();
- void Direct3D_Render( HWND hwnd);
- void Direct3D_CleanUp( );
- float Get_FPS();
- VOID Matrix_Set();
- //*****************************************************************************************
- // Name: WinMain( )
- // Desc: Windows應用程序入口函數
- //*****************************************************************************************
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
- {
- //開始設計一個完整的窗口類
- WNDCLASSEX wndClass = { 0 }; //用WINDCLASSEX定義了一個窗口類,即用wndClass實例化了WINDCLASSEX,用於之後窗口的各項初始化
- wndClass.cbSize = sizeof( WNDCLASSEX ) ; //設置結構體的字節數大小
- wndClass.style = CS_HREDRAW | CS_VREDRAW; //設置窗口的樣式
- wndClass.lpfnWndProc = WndProc; //設置指向窗口過程函數的指針
- wndClass.cbClsExtra = 0;
- wndClass.cbWndExtra = 0;
- wndClass.hInstance = hInstance; //指定包含窗口過程的程序的實例句柄。
- wndClass.hIcon=(HICON)::LoadImage(NULL,_T("icon.ico"),IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //從全局的::LoadImage函數從本地加載自定義ico圖標
- wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); //指定窗口類的光標句柄。
- wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //爲hbrBackground成員指定一個灰色畫刷句柄
- wndClass.lpszMenuName = NULL; //用一個以空終止的字符串,指定菜單資源的名字。
- wndClass.lpszClassName = _T("ForTheDreamOfGameDevelop"); //用一個以空終止的字符串,指定窗口類的名字。
- if( !RegisterClassEx( &wndClass ) ) //設計完窗口後,需要對窗口類進行註冊,這樣才能創建該類型的窗口
- return -1;
- HWND hwnd = CreateWindow( _T("ForTheDreamOfGameDevelop"),WINDOW_TITLE, //喜聞樂見的創建窗口函數CreateWindow
- WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, SCREEN_WIDTH,
- SCREEN_HEIGHT, NULL, NULL, hInstance, NULL );
- //Direct3D資源的初始化,調用失敗用messagebox予以顯示
- if (!(S_OK==Direct3D_Init (hwnd)))
- {
- MessageBox(hwnd, _T("Direct3D初始化失敗~!"), _T("淺墨的消息窗口"), 0); //使用MessageBox函數,創建一個消息窗口
- }
- MoveWindow(hwnd,200,50,SCREEN_WIDTH,SCREEN_HEIGHT,true); //調整窗口顯示時的位置,窗口左上角位於屏幕座標(200,50)處
- ShowWindow( hwnd, nShowCmd ); //調用Win32函數ShowWindow來顯示窗口
- UpdateWindow(hwnd); //對窗口進行更新,就像我們買了新房子要裝修一樣
- //消息循環過程
- MSG msg = { 0 }; //初始化msg
- while( msg.message != WM_QUIT ) //使用while循環
- {
- if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) //查看應用程序消息隊列,有消息時將隊列中的消息派發出去。
- {
- TranslateMessage( &msg ); //將虛擬鍵消息轉換爲字符消息
- DispatchMessage( &msg ); //該函數分發一個消息給窗口程序。
- }
- else
- {
- Direct3D_Render(hwnd); //調用渲染函數,進行畫面的渲染
- }
- }
- UnregisterClass(_T("ForTheDreamOfGameDevelop"), wndClass.hInstance);
- return 0;
- }
- //*****************************************************************************************
- // Name: WndProc()
- // Desc: 對窗口消息進行處理
- //*****************************************************************************************
- LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) //窗口過程函數WndProc
- {
- switch( message ) //switch語句開始
- {
- case WM_PAINT: // 客戶區重繪消息
- Direct3D_Render(hwnd); //調用Direct3D_Render函數,進行畫面的繪製
- ValidateRect(hwnd, NULL); // 更新客戶區的顯示
- break; //跳出該switch語句
- case WM_KEYDOWN: // 鍵盤按下消息
- if (wParam == VK_ESCAPE) // ESC鍵
- DestroyWindow(hwnd); // 銷燬窗口, 併發送一條WM_DESTROY消息
- break;
- case WM_DESTROY: //窗口銷燬消息
- Direct3D_CleanUp(); //調用Direct3D_CleanUp函數,清理COM接口對象
- PostQuitMessage( 0 ); //向系統表明有個線程有終止請求。用來響應WM_DESTROY消息
- break; //跳出該switch語句
- default: //若上述case條件都不符合,則執行該default語句
- return DefWindowProc( hwnd, message, wParam, lParam ); //調用缺省的窗口過程來爲應用程序沒有處理的窗口消息提供缺省的處理。
- }
- return 0; //正常退出
- }
- //*****************************************************************************************
- // Name: Direct3D_Init( )
- // Desc: 初始化Direct3D
- // Point:【Direct3D初始化四步曲】
- // 1.初始化四步曲之一,創建Direct3D接口對象
- // 2.初始化四步曲之二,獲取硬件設備信息
- // 3.初始化四步曲之三,填充結構體
- // 4.初始化四步曲之四,創建Direct3D設備接口
- //*****************************************************************************************
- HRESULT Direct3D_Init(HWND hwnd)
- {
- //--------------------------------------------------------------------------------------
- // 【Direct3D初始化四步曲之一,創接口】:創建Direct3D接口對象, 以便用該Direct3D對象創建Direct3D設備對象
- //--------------------------------------------------------------------------------------
- LPDIRECT3D9 pD3D = NULL; //Direct3D接口對象的創建
- if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) //初始化Direct3D接口對象,並進行DirectX版本協商
- return E_FAIL;
- //--------------------------------------------------------------------------------------
- // 【Direct3D初始化四步曲之二,取信息】:獲取硬件設備信息
- //--------------------------------------------------------------------------------------
- D3DCAPS9 caps; int vp = 0;
- if( FAILED( pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps ) ) )
- {
- return E_FAIL;
- }
- if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
- vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; //支持硬件頂點運算,我們就採用硬件頂點運算,妥妥的
- else
- vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持硬件頂點運算,無奈只好採用軟件頂點運算
- //--------------------------------------------------------------------------------------
- // 【Direct3D初始化四步曲之三,填內容】:填充D3DPRESENT_PARAMETERS結構體
- //--------------------------------------------------------------------------------------
- D3DPRESENT_PARAMETERS d3dpp;
- ZeroMemory(&d3dpp, sizeof(d3dpp));
- d3dpp.BackBufferWidth = SCREEN_WIDTH;
- d3dpp.BackBufferHeight = SCREEN_HEIGHT;
- d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
- d3dpp.BackBufferCount = 2;
- d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
- d3dpp.MultiSampleQuality = 0;
- d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
- d3dpp.hDeviceWindow = hwnd;
- d3dpp.Windowed = true;
- d3dpp.EnableAutoDepthStencil = true;
- d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
- d3dpp.Flags = 0;
- d3dpp.FullScreen_RefreshRateInHz = 0;
- d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
- //--------------------------------------------------------------------------------------
- // 【Direct3D初始化四步曲之四,創設備】:創建Direct3D設備接口
- //--------------------------------------------------------------------------------------
- if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
- hwnd, vp, &d3dpp, &g_pd3dDevice)))
- return E_FAIL;
- if(!(S_OK==Objects_Init())) return E_FAIL;
- SAFE_RELEASE(pD3D) //LPDIRECT3D9接口對象的使命完成,我們將其釋放掉
- return S_OK;
- }
- HRESULT Objects_Init()
- {
- //創建字體
- if(FAILED(D3DXCreateFont(g_pd3dDevice, 30, 0, 0, 1, FALSE, DEFAULT_CHARSET,
- OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, _T("宋體"), &g_pFont)))
- return E_FAIL;
- srand((unsigned)time(NULL)); //初始化時間種子
- //--------------------------------------------------------------------------------------
- // 【頂點緩存、索引緩存繪圖四步曲之二】:創建頂點緩存和索引緩存
- //--------------------------------------------------------------------------------------
- //創建頂點緩存
- if( FAILED( g_pd3dDevice->CreateVertexBuffer( 8*sizeof(CUSTOMVERTEX),
- 0, D3DFVF_CUSTOMVERTEX,
- D3DPOOL_DEFAULT, &g_pVertexBuffer, NULL ) ) )
- {
- return E_FAIL;
- }
- // 創建索引緩存
- if( FAILED( g_pd3dDevice->CreateIndexBuffer(36* sizeof(WORD), 0,
- D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_pIndexBuffer, NULL)) )
- {
- return E_FAIL;
- }
- //--------------------------------------------------------------------------------------
- // 【頂點緩存、索引緩存繪圖四步曲之三】:訪問頂點緩存和索引緩存
- //--------------------------------------------------------------------------------------
- //頂點數據的設置,
- //頂點數據
- CUSTOMVERTEX Vertices[] =
- {
- { -20.0f, 20.0f, -20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },
- { -20.0f, 20.0f, 20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },
- { 20.0f, 20.0f, 20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },
- { 20.0f, 20.0f, -20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },
- { -20.0f, -20.0f, -20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },
- { -20.0f, -20.0f, 20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },
- { 20.0f, -20.0f, 20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },
- { 20.0f, -20.0f, -20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },
- };
- //填充頂點緩存
- VOID* pVertices;
- if( FAILED( g_pVertexBuffer->Lock( 0, sizeof(Vertices), (void**)&pVertices, 0 ) ) )
- return E_FAIL;
- memcpy( pVertices, Vertices, sizeof(Vertices) );
- g_pVertexBuffer->Unlock();
- // 填充索引數據
- WORD *pIndices = NULL;
- g_pIndexBuffer->Lock(0, 0, (void**)&pIndices, 0);
- // 頂面
- pIndices[0] = 0, pIndices[1] = 1, pIndices[2] = 2;
- pIndices[3] = 0, pIndices[4] = 2, pIndices[5] = 3;
- // 正面
- pIndices[6] = 0, pIndices[7] = 3, pIndices[8] = 7;
- pIndices[9] = 0, pIndices[10] = 7, pIndices[11] = 4;
- // 左側面
- pIndices[12] = 0, pIndices[13] = 4, pIndices[14] = 5;
- pIndices[15] = 0, pIndices[16] = 5, pIndices[17] = 1;
- // 右側面
- pIndices[18] = 2, pIndices[19] = 6, pIndices[20] = 7;
- pIndices[21] = 2, pIndices[22] = 7, pIndices[23] = 3;
- // 背面
- pIndices[24] = 2, pIndices[25] = 5, pIndices[26] = 6;
- pIndices[27] = 2, pIndices[28] = 1, pIndices[29] = 5;
- // 底面
- pIndices[30] = 4, pIndices[31] = 6, pIndices[32] = 5;
- pIndices[33] = 4, pIndices[34] = 7, pIndices[35] = 6;
- g_pIndexBuffer->Unlock();
- return S_OK;
- }
- //*****************************************************************************************
- // Name:Matrix_Set()
- // Desc: 設置世界矩陣
- // Point:【Direct3D四大變換】
- // 1.【四大變換之一】:世界變換矩陣的設置
- // 2.【四大變換之二】:取景變換矩陣的設置
- // 3.【四大變換之三】:投影變換矩陣的設置
- // 4.【四大變換之四】:視口變換的設置
- //*****************************************************************************************
- VOID Matrix_Set()
- {
- //--------------------------------------------------------------------------------------
- //【四大變換之一】:世界變換矩陣的設置
- //--------------------------------------------------------------------------------------
- D3DXMATRIX matWorld, Rx, Ry, Rz;
- D3DXMatrixIdentity(&matWorld); // 單位化世界矩陣
- D3DXMatrixRotationX(&Rx, D3DX_PI *(::timeGetTime() / 1000.0f)); // 繞X軸旋轉
- D3DXMatrixRotationY(&Ry, D3DX_PI *( ::timeGetTime() / 1000.0f/2)); // 繞Y軸旋轉
- D3DXMatrixRotationZ(&Rz, D3DX_PI *( ::timeGetTime() / 1000.0f/3)); // 繞Z軸旋轉
- matWorld = Rx * Ry * Rz * matWorld; // 得到最終的組合矩陣
- g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld); //設置世界變換矩陣
- //--------------------------------------------------------------------------------------
- //【四大變換之二】:取景變換矩陣的設置
- //--------------------------------------------------------------------------------------
- D3DXMATRIX matView; //定義一個矩陣
- D3DXVECTOR3 vEye(0.0f, 0.0f, -200.0f); //攝像機的位置
- D3DXVECTOR3 vAt(0.0f, 0.0f, 0.0f); //觀察點的位置
- D3DXVECTOR3 vUp(0.0f, 1.0f, 0.0f);//向上的向量
- D3DXMatrixLookAtLH(&matView, &vEye, &vAt, &vUp); //計算出取景變換矩陣
- g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView); //應用取景變換矩陣
- //--------------------------------------------------------------------------------------
- //【四大變換之三】:投影變換矩陣的設置
- //--------------------------------------------------------------------------------------
- D3DXMATRIX matProj; //定義一個矩陣
- D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4.0f, 1.0f, 1.0f, 1000.0f); //計算投影變換矩陣
- g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj); //設置投影變換矩陣
- //--------------------------------------------------------------------------------------
- //【四大變換之四】:視口變換的設置
- //--------------------------------------------------------------------------------------
- D3DVIEWPORT9 vp; //實例化一個D3DVIEWPORT9結構體,然後做填空題給各個參數賦值就可以了
- vp.X = 0; //表示視口相對於窗口的X座標
- vp.Y = 0; //視口相對對窗口的Y座標
- vp.Width = SCREEN_WIDTH; //視口的寬度
- vp.Height = SCREEN_HEIGHT; //視口的高度
- vp.MinZ = 0.0f; //視口在深度緩存中的最小深度值
- vp.MaxZ = 1.0f; //視口在深度緩存中的最大深度值
- g_pd3dDevice->SetViewport(&vp); //視口的設置
- }
- //*****************************************************************************************
- // Name: Direct3D_Render()
- // Desc: 進行圖形的渲染操作
- // Point:【Direct3D渲染五步曲】
- // 1.渲染五步曲之一,清屏操作
- // 2.渲染五步曲之二,開始繪製
- // 3.渲染五步曲之三,正式繪製
- // 4.渲染五步曲之四,結束繪製
- // 5.渲染五步曲之五,翻轉顯示
- //*****************************************************************************************
- void Direct3D_Render(HWND hwnd)
- {
- //--------------------------------------------------------------------------------------
- // 【Direct3D渲染五步曲之一】:清屏操作
- //--------------------------------------------------------------------------------------
- g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
- //定義一個矩形,用於獲取主窗口矩形
- RECT formatRect;
- GetClientRect(hwnd, &formatRect);
- //--------------------------------------------------------------------------------------
- // 【Direct3D渲染五步曲之二】:開始繪製
- //--------------------------------------------------------------------------------------
- g_pd3dDevice->BeginScene(); // 開始繪製
- Matrix_Set();
- // 獲取鍵盤消息並給予設置相應的填充模式
- if (::GetAsyncKeyState(0x31) & 0x8000f) // 若數字鍵1被按下,進行線框填充
- g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
- if (::GetAsyncKeyState(0x32) & 0x8000f) // 若數字鍵2被按下,進行實體填充
- g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
- //--------------------------------------------------------------------------------------
- // 【Direct3D渲染五步曲之三】:正式繪製,利用頂點緩存繪製圖形
- //--------------------------------------------------------------------------------------
- //--------------------------------------------------------------------------------------
- // 【頂點緩存、索引緩存繪圖四步曲之四】:繪製圖形
- //--------------------------------------------------------------------------------------
- // 設置渲染狀態
- g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
- g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); //開啓背面消隱
- g_pd3dDevice->SetStreamSource( 0, g_pVertexBuffer, 0, sizeof(CUSTOMVERTEX) );//把包含的幾何體信息的頂點緩存和渲染流水線相關聯
- g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );//指定我們使用的靈活頂點格式的宏名稱
- g_pd3dDevice->SetIndices(g_pIndexBuffer);//設置索引緩存
- g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);//利用索引緩存配合頂點緩存繪製圖形
- //在窗口右上角處,顯示每秒幀數
- int charCount = swprintf_s(g_strFPS, 20, _T("FPS:%0.3f"), Get_FPS() );
- g_pFont->DrawText(NULL, g_strFPS, charCount , &formatRect, DT_TOP | DT_RIGHT, D3DCOLOR_XRGB(255,239,136));
- //--------------------------------------------------------------------------------------
- // 【Direct3D渲染五步曲之四】:結束繪製
- //--------------------------------------------------------------------------------------
- g_pd3dDevice->EndScene(); // 結束繪製
- //--------------------------------------------------------------------------------------
- // 【Direct3D渲染五步曲之五】:顯示翻轉
- //--------------------------------------------------------------------------------------
- g_pd3dDevice->Present(NULL, NULL, NULL, NULL); // 翻轉與顯示
- }
- //*****************************************************************************************
- // Name:Get_FPS()函數
- // Desc: 用於計算幀速率
- //*****************************************************************************************
- float Get_FPS()
- {
- //定義四個靜態變量
- static float fps = 0; //我們需要計算的FPS值
- static int frameCount = 0;//幀數
- static float currentTime =0.0f;//當前時間
- static float lastTime = 0.0f;//持續時間
- frameCount++;//每調用一次Get_FPS()函數,幀數自增1
- currentTime = timeGetTime()*0.001f;//獲取系統時間,其中timeGetTime函數返回的是以毫秒爲單位的系統時間,所以需要乘以0.001,得到單位爲秒的時間
- //如果當前時間減去持續時間大於了1秒鐘,就進行一次FPS的計算和持續時間的更新,並將幀數值清零
- if(currentTime - lastTime > 1.0f) //將時間控制在1秒鐘
- {
- fps = (float)frameCount /(currentTime - lastTime);//計算這1秒鐘的FPS值
- lastTime = currentTime; //將當前時間currentTime賦給持續時間lastTime,作爲下一秒的基準時間
- frameCount = 0;//將本次幀數frameCount值清零
- }
- return fps;
- }
- //*****************************************************************************************
- // Name: Direct3D_CleanUp()
- // Desc: 對Direct3D的資源進行清理,釋放COM接口對象
- //*****************************************************************************************
- void Direct3D_CleanUp()
- {
- //釋放COM接口對象
- SAFE_RELEASE(g_pIndexBuffer)
- SAFE_RELEASE(g_pVertexBuffer)
- SAFE_RELEASE(g_pFont)
- SAFE_RELEASE(g_pd3dDevice)
- }
多次運行這個demo,我們看到顏色隨機的絢爛的立方體在三維空間中以一定的節奏轉動着,按下鍵盤上的1鍵,我們會看到立方體的線框輪廓,而默認情況下或者按下鍵盤上的2鍵,我們會看到立方體的實體輪廓。
淺墨相信通過之前兩篇文章的講解,理解起這個立方體的創建過程並不是什麼難事。
在demo之中,按鍵更改填充模式的代碼實現如下,也就是在渲染五步曲第三步中加入如下代碼就可以了:
- // 獲取鍵盤消息並給予設置相應的填充模式
- if (::GetAsyncKeyState(0x31) & 0x8000f) // 若數字鍵1被按下,進行線框填充
- g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
- if (::GetAsyncKeyState(0x32) & 0x8000f) // 若數字鍵2被按下,進行實體填充
- g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
其中,GetAsyncKeyState函數用於獲取當前鍵盤上按鍵的按下狀態。關於渲染狀態,後面我們會講解到,這裏暫時就給大家一個印象吧。
最後們我們放出這個demo的運行截圖:
轉載出自http://blog.csdn.net/poem_qianmo/article/details/8408723