上一次已經講了DXUT的基本工作模式,汝在DirectX SDK里面把EmptyProject找出來,然后把工程建立好,把include和lib等也配置,可能有的不知道怎么配置,咱這里還是說一下吧


若汝使用的是Visual Sutdio,那么 工具->選項->項目和解決方案->VC++目錄。右邊在“包含文件”里加上DirectX SDK目錄底下的include目錄,“庫文件”里加上DirectX SDK目錄底下的lib/xXX目錄,若汝使用的是32位機,就選x86,64位機選擇x64。若是其他IDE,在系統環境變量里配置就好。


現在汝按下F5,就會出現一個640*480的黑色的窗口。這個就是DXUT底層完全準備好了,就等汝干事兒了。

接下來咱們要干嘛?很容易想到的就是畫圖。于是給汝介紹一下在Direct3D中處理2D圖像的核心內容——Sprite

Sprite是Direct3D中處理2D圖像的十分便利的一個武器。它的用處就是繪制某張指定的紋理,并且可以加上簡單的顏色化渲染。

HRESULT Draw(
  LPDIRECT3DTEXTURE9 pTexture,
  CONST RECT * pSrcRect,
  CONST D3DXVECTOR3 * pCenter,
  CONST D3DXVECTOR3 * pPosition,
  D3DCOLOR Color
);

Sprite::Draw函數的原型就是這樣。不過這個函數必須在Sprite::Begin()和Sprite::End()這兩個函數之間才能使用。這個Begin和End便是加在OnFrameRender()這個回調函數里面。那么下來我們細說這些參數。
pTexture 這個要傳入需要繪制的紋理對象的指針
pSrcRect 這個傳入需要繪制紋理pTexture的哪一部分,就是一個矩形限定框
pCenter 以pCenter為中心繪制紋理
pPosition 以pPosition為左上角為起始點繪制
Color 用Color顏色來渲染此次繪制

利用這個函數咱就可以創造一個世界了~有圖就有真相。那么咱是如何創造這個世界呢?
咱可以將這個函數封裝一下,讓咱可以非常方便的使用它。
平常繪制圖片,因為不存在3D,所以咱需要的也就這幾個
texture 需要繪制的紋理
srcRect 繪制紋理的srcRect
darwRect 將紋理的srcRect部分拉伸繪制在屏幕的drawRect部分
color 用color顏色來渲染此次繪制

于是咱就可以計劃咱的簡單的圖形引擎(雖然很簡陋)了。總結出下列的封裝后的接口,咱們將它們封裝進咱們的圖像引擎,也就是GraphEngine:
class GraphEngine
{
    
public
        
//-------------------------繪制接口----------------------------------------
        void Draw(string whichone, int dx, int dy);                       //在(dx,dy)處繪制路徑名為whichone的整個紋理
        void Draw(string whichone, RECT drawRect);                       //在drawRect內繪制整個紋理
        void Draw(string whichone, RECT srcRect, int dx, int dy);      //在(dx,dy)處繪制紋理的srcRect部分
        void Draw(string whichone, RECT srcRect, RECT drawRect);       //在drawRect內繪制紋理的srcRect部分
        
//---------------------這四個就是前面的接口加上一個渲染的顏色------------------------
        void Draw(string whichone, int dx, int dy, D3DCOLOR color);        
        
void Draw(string whichone, RECT drawRect, D3DCOLOR color);        
        
void Draw(string whichone, RECT srcRect, int dx, int dy, D3DCOLOR color);       
        
void Draw(string whichone, RECT srcRect, RECT drawRect, D3DCOLOR color);                
            //------------------------------------------------------------------------------------
}
;

具體實現咱們先放一放。規劃一個程序千萬不要直接從底層開始想。雖然我們這個是面向對象編程,但是“自頂向下,逐步求精”依然對我們很有幫助。

以上的那些接口如果實現了,那么我們就可以真真正正地開始繪圖了。

大概汝也發現了,需要畫的是一個紋理的指針,可是我這里傳入的是一個路徑名。為什么呢?這是因為每次繪制的時候,汝要是每次都以“紋理的指針”方式代表一個紋理,那么可以想見不是很好管理。于是我們就引入紋理池的概念。紋理池正如其名,是用來管理紋理的一個池子。Direct3D中有專門的東西來管理,可是咱這里用不到里面的復雜的東西,只用手動模擬一個便可。咱的代碼里是直接用數組存的,也對應存了那紋理的路徑名。查找一個紋理就直接for過去,是O(n)級別的。要是汝追求效率的話可以寫棵平衡樹,用map也可以。

汝說汝沒看見代碼,不信?好吧,咱可是賢狼赫羅啊!給汝解析Draw(string whichone,RECT srcRect,RECT drawRect,D3DCOLOR color);這個函數吧,解析了汝明白了后,其他的也就不在話下。

///-------------------------------------
///在矩形drawRect內伸縮繪制紋理的srcRect部分,并用制定顏色渲染
///-------------------------------------

void GraphEngine::Draw(string whichone,RECT srcRect,RECT drawRect, D3DCOLOR color)
{
    D3DXMATRIX ptransform,ptransform2,ptransform3,ptransform4;
    pSprite
->GetTransform(&ptransform);
    
float fx = (float)(drawRect.right-drawRect.left)/(float)(srcRect.right-srcRect.left);
    
float fy = (float)(drawRect.bottom-drawRect.top)/(float)(srcRect.bottom-srcRect.top);
    D3DXMatrixTranslation(
&ptransform2,drawRect.left/fx,drawRect.top/fy,0.0f);
    D3DXMatrixScaling(
&ptransform3, fx, fy, 1.0f);
    D3DXMatrixMultiply(
&ptransform4,&ptransform2,&ptransform3);
    D3DXMatrixMultiply(
&ptransform4,&ptransform4,&ptransform);
    pSprite
->SetTransform(&ptransform4);
    pSprite
->Draw(TexturePool[findTexture(whichone)],&srcRect,NULL,NULL,color);
    pSprite
->SetTransform(&ptransform);
}

D3DXMATRIX 申明了幾個變換矩陣(什么是變換矩陣?一會兒咱會解釋,汝先繼續看)。在Direct3D中,坐標系以矩陣形式存儲,并且可以通過矩陣變換來變換坐標系。
我們要吧圖畫在drawRect部分里,于是我們首先把坐標系原點變換到drawRect矩形的左上角。因為后面還會伸縮坐標系,所以不能直接移動過去,而要以drawRect和srcRect的比例移動過去。
pSprite->GetTransform(&ptransform);這句將原本的坐標系存儲下來,好一會兒還原回去。fx記錄了X軸方向的比例,fy記錄了Y軸方向的比例。
D3DXMatrixTranslation(&ptransform2,drawRect.left/fx,drawRect.top/fy,0.0f);這句將ptransform2這個矩陣改變成記錄了“在X軸方向平移drawRect.left/fx,在Y軸方向平移drawRect.top/fy,在Z軸方向平移0(因為我們不涉及3D)”的變換信息的矩陣。
D3DXMatrixScaling(&ptransform3,fx,fy,1.0f);這句將ptransform3這個矩陣改變成記錄了“X軸拉伸比例為fx,Y軸拉伸比例為fy,Z軸拉伸比例為1.0(因為我們不涉及3D)”的變換信息的矩陣。
D3DXMatrixMultiply(&ptransform4,&ptransform2,&ptransform3);這句便是矩陣乘法了,將ptransform2*ptransform3的結果存入ptransform4中。
接下來的一句就是將目前的和原始的變換矩陣相乘。
pSprite->SetTransform(&ptransform4);這句將ptransform4變換矩陣設置成坐標系。
pSprite->Draw(TexturePool[findTexture(whichone)],&srcRect,NULL,NULL,color);Draw函數的原型已經介紹過了,其中的pCenter參數傳入NULL,因為pCenter大多用到的地方是在3D里,咱不用關心。而pPosition傳入NULL是因為咱們前面已經變換過了坐標系,只用直接在原點繪制出來就好了。findTexture(whichone)是一個需要自己寫的函數,具體就是傳入文件名路徑,然后返回值是這個文件在紋理池中的索引。若此紋理不存在于紋理池中便重新加載一張紋理然后返回索引。
最后一句將坐標系還原。

于是這個就解釋完了。那么留下的問題就是所謂的變換矩陣。矩陣就不解釋了。變換矩陣就是可以通過矩陣乘法來將你的坐標變換至目標坐標,這樣利用矩陣乘法十分方便快捷,效率很高。
3D里的矩陣變換是由一個1行4列矩陣(點(x0,x1,x2)),以及一個4行4行矩陣(變換矩陣)的矩陣乘法組成,是這樣的(這里矩陣只以3*3為例,意在表現矩陣乘法):


其中(x0,x1,x2)是原始點,(y0,y1,y2)是最終得出的目標點。變換信息存儲于矩陣中,由矩陣乘法得出結果。而實際根據變換不同需要的變換矩陣也不同。下面介紹一下3D中常用的變換矩陣(請注意矩陣表示方法,上面那圖是從上到下,下面的是從左往右):
1、平移變換

這個變換需要4乘4的變換矩陣,如上圖所示,其得出的結果便是(x,y,z)平移(Tx,Ty,Tz)得到的坐標。
過程名:D3DXMatrixTranslation

2、伸縮變換

結果是(x,y,z)在X軸方向伸縮比例Sx,Y軸方向伸縮比例Sy,Z軸方向伸縮比例Sz得到的坐標。
過程名:D3DXMatrixScaling

3、旋轉變換

旋轉變換分為3種:繞X軸,繞Y軸,繞Z軸。圖示分別如下:
X軸:
Y軸:
Z軸:
結果是(x,y,z)繞X軸或Y軸或Z軸旋轉角度θ(弧度制)
過程名:D3DXMatrixRotationX D3DXMatrixRotationY D3DXMatrixRotationZ

咱使用中只要忽略Z軸,便可達到想達到的變換效果。

再說一下所謂的渲染顏色
D3DCOLOR是由4個部分組成的:A(alpha,透明值)以及RGB(Red,Green&Blue),
這個值指定了紋理中A、R、G、B的表現程度
下面我們用半透明的灰色來遮住一張圖看看效果:
原圖:


用color (255,50,50,50)繪制了以后的效果:

也就是所有顏色的“表現力”從255變成50了

若用(255,0,255,255)繪制了以后,紅色部分就無法表現出來了:


若是ALPHA值低一點,會變得透明。

這些所有的了解了后,你就可以畫圖了!注意,因為Direct3D中存儲的紋理長寬必須是2的方冪的。因為這樣對硬件加速方面很有幫助(咱不需要了解)。咱把到目前為止的代碼全部打包發出來一下,代碼里也有注釋的,汝研究一下便可以領會透徹。

代碼下載地址(工程文件屬于Visual Sutio 2008,若汝的VS版本過低,請網上百度一下VS工程轉換器):
/Files/CK985/Direct3D_2D.rar

里面的工程咱都是調制出來的,編譯運行沒問題,會在800*600的窗口里畫出上面那張原圖。代碼里面除了今天講的繪制紋理方面,也有文字的繪制,還有一些其他的東西,都是為了使之運行更完美。汝可對比一下空的EmptyProject和咱寫的。

那今天就到這里了。有不懂的可以回帖,也可以單獨聯系我QQ670188680。
下篇:Direct3D中的簡單2D繪制(下)——文字的繪制以及為了GAL GAME而完善的框架。