本文由哈利_蜘蛛俠原創,轉載請註明出處!有問題請聯繫[email protected]
這一次我們繼續來講述Jim Adams 老哥的RPG編程書籍第二版第二章的第8節:Using Fonts(使用字體),和第9節:Billboards (廣告牌)。這兩節的內容都不多,所以就放在一期裏面講了。
原文翻譯:
===============================================================================
2.8 Using Fonts (使用字體)
DirectX 9的一個缺陷就是它不支持字體。DirectX的老一些的版本(在DX 8之前)可以操控Windows的字體繪製函數。而在DX 9裏面,你必須手動地繪製一個字體到一個紋理表面上,然後將字體的每個字母作爲一個小的帶紋理映射的多邊形進行繪製。
管理一個包含有字體的紋理對於僅僅是繪製文本來說有點太費勁了,但是多虧了D3DX,你可以利用一個特殊的對象ID3DXFont,它可以爲你處理這些紋理映射字體。ID3DXFont對象包含有7個函數,但是我們在這裏只對其中的3個感興趣。
這3個函數是:(注意:下面的Begin 函數和End 函數現在已經沒有了。另外DrawText 函數也與現在的版本不一樣了。這充分說明了我之前提到過的“即使同樣是DirectX 9也有很多區別”這一點。詳情請見這一節後面我的補充文字。)
● ID3DXFont::Begin。這個函數代表一個文本繪製操作的開始。
● ID3DXFont::End。這個函數代表一個文本繪製操作的結束。
● ID3DXFont::DrawText。這個函數繪製文本。
注意
===============================================================================
你也許會覺得有用的第四個函數是ID3DXFont::OnResetDevice。當你失去對顯示設備的控制的時候(例如當用戶切換到另外一個應用程序的時候),你會丟失資源,例如你的字體對象。只要你失去了資源(被錯誤代碼所指出——查閱DX SDK文檔來獲取更多信息),你就得調用OnResetDevice 函數來恢復這些資源。
===============================================================================
在你走遠之前,你先來看看如何創建一個字體吧!
2.8.1 Creating theFont (創建字體)
爲了使用ID3DXFont對象,你必須首先使用D3DXCreateFontIndirect 函數來將它初始化:
HRESULT D3DXCreateFontIndirect(
IDirect3DDevice9*pDevice, // Device to associate font to
CONSTLOGFONT *pLogFont, // Structuredefining font
ID3DXFont **ppFont); // Pointer to createdfont object
當心
===============================================================================
因爲ID3DXFont 是一個COM對象,你始終要記得在用完它之後對其進行釋放。
===============================================================================
你向這個函數提供之前初始化的設備對象以及指向你正在初始化的ID3DXFont對象的指針,但是你認爲pLogFont 參數是幹什麼的呢?
正如你所見到的,pLogFont 指向一個LOGFONT(它代表logical font)結構體,該結構體看上去像這個樣子:
typedef struct tagLOGFONT {
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
TCHAR lfFaceName[LF_FACESIZE];
} LOGFONT;
哎喲喂,好多信息啊!但是你可以安全地跳過對LOGFONT 結構體中大多數成員的設置,將它們設爲我給出的默認值。(大哥,你並沒有給出默認值啊!)你唯一需要改變的就是lfHeight, lfWeight, lfItalic, lfUnderline 以及lfFaceName 這些成員。
從最簡單的成員開始,你可以將 lfItaic 和 lfUnderline 設爲0或1來分別設定或清除對斜體或下劃線的使用。你用lfWeight 來設定你在繪製時使用的粗體等級;你將它設爲0來獲得正常字體,或者設爲700來獲得粗體字。lfHeight代表字體的點尺寸(poing size)。lfHeight 的值有一點奇特,因爲它本質上並不取一個直接的尺寸。相反,你必須給它一個代表它以像素計量的高度的負值。例如,對於一個有16像素高的字體,你使用-16的值。(但是對於現在的ID3DXFont對象而言,不需要再添加負號了。)
最後的是lfFaceName,它是你想使用的字體的名字。它可以是Times New Roman,Courier New,或者任何其他安裝在你係統中的字體。你只需要將名字複製進lfFaceName 成員中。下面是一個使用具有16 的點尺寸的Times New Roman 字體例子:
// g_pD3DDevice = pre-initialized deviceobject
// hWnd = handle to parent window
ID3DXFont *pD3DFont;
LOGFONT lf;
// Clear out the font structure
ZeroMemory(&lf, sizeof(LOGFONT));
// Set the font name and height
strcpy(lf.lfFaceName, “Times New Roman”);
lfHeight = -16;
// Create the font object
if(FAILED(D3DXCreateFontIndirect(g_pD3DDevice, \
&lf,&pD3DFont))) {
//Error occurred
}
2.8.2 Drawing with Fonts (用字體進行繪製)
一旦你的ID3DXFont對象被初始化後,你可以使用ID3DXFont::DrawText 函數來開始繪製文本了:(現在版本的DrawText函數原型與之不同,詳情參見SDK文檔。)
HRESULT ID3DXFont::DrawText(
LPCSTR pString, // String to print
INT Count, // -1
LPRECT pRect, // Area to draw text in
DWORD Format, // 0
D3DCOLOR Color); // Color to use to draw with
使用DrawText 函數時唯一需要注意的是pRect 參數,它是指向一個包含了用於繪製文本的區域的RECT 結構體的指針。你可以將這個區域設爲屏幕的尺寸,或者如果你想要在一個特定的區域中包含文本的話,那麼就使用屏幕的座標。RECT 結構體看起來像這個樣子:
typedef struct tagRECT {
LONG left; // Left coordinate
LONG top; // Top coordinate
LONG right; // Right coordinate
LONG bottom; // Bottom coordinate
} RECT;
DrawText 函數最後的參數是Color,它確定了繪製文本所使用的顏色。使用方便的D3DCOLOR_RGBA 或D3DCOLOR_COLORVALUE 宏來定義繪製文本的顏色。
哦,對了,還有一件事。你必須將你對DrawText 函數的調用夾在ID3DXFont::Begin 和ID3DXFont::End 函數之間。這兩個函數告訴Direct3D 你準備渲染文本了。
下面的例子假設你已經初始化了字體對象,並準備好繪製文本:
// g_pD3DDevice = pre-initialized deviceobject
// pD3DXFont = pre-initialized font object
// Setup the RECT structure with drawablearea
RECT rect = { 0, 0, 200, 100 };
// Begin the drawing code block
if(SUCCEEDED(g_pD3DDevice->BeginScene())){
// Begin font rendering
pD3DXFont->Begin();
// Draw some text
pD3DXFont->DrawText(“I can draw with text!”, -1, \
&rect,0, D3DCOLOR_RGBA(255,255,255,255);
// End font rendering
pD3DXFont->End();
// End the scene
g_pD3DDevice->EndScene();
}
===============================================================================
作者寫了一個Font 程序來演示ID3DXFont 對象的使用。不過由於我前面的紅字部分說的那些話,你是沒法直接對其進行編譯的。所以我就對其進行了相應的變動。這應該是目前爲止我做出來的第一個本質性的改動了。由於這裏沒有用到頂點,所以那些跟頂點有關的例如頂點聲明和繪製圖元的部分就沒有了;另外由於這裏的變動實在是太大了,所以爲了讓大家看得更明白,我就沒有用之前更新過的shell框架,而是在作者的代碼基礎上進行變動而得到。
爲了更好玩,我弄了兩個ID3DXFont對象,一個用來顯示英文,一個用來顯示中文。效果如下圖所示:
有這麼幾個值得注意的地方:
1、 現在的DirectX 9.0c 版本里的ID3DXFont 對象沒有Begin和End 函數了。
2、 現在的DirectX 9.0c 版本里的ID3DXFont 對象的DrawText 函數的參數列表與文中不同了,多了第一個參數,具體意義可以查看DirectX SDK 文檔;對於我們的目的而言,將其設爲0即可。
3、 關於字體的名字。首先字體可以是系統安裝的任何字體,也就是C:\Windows\Font 文件夾下的任何字體。至於字體的名字,右擊字體文件,在“詳細信息”中找到“標題”一欄,那就是你所要用的字體的名字。
下面是代碼的下載地址:
下面繼續翻譯第9節:
===============================================================================
2.9 Billboards (廣告牌)
廣告牌技術(billboarding)是一種很酷的技術,它允許2-D物體以三維的形式出現。例如,一個如一棵樹般複雜的物體可以根據一個建模程序的側視圖渲染出來,然後將它在一個矩形的多邊形上以紋理的形式繪製出來。這個矩形多邊形總是面朝觀察點,所以不管從什麼角度觀察這個多邊形,看上去似乎這個樹紋理總是從它被渲染的那一邊被觀察到的(如圖2.21所示)。
許多程序員使用廣告牌技術來製作遊戲,因爲他們可以很容易實現它。使用廣告牌技術的一個完美的例子可以在N64上的Paper Mario(《紙片馬里奧》)遊戲中看到。所有的遊戲角色都是用2-D繪製成的,然後被紋理映射到多邊形上。這個遊戲還增添了一點花樣,就是允許你在角色轉身的時候看見廣告牌的多邊形,這樣就給了圖形一種很滑稽的風格。
廣告牌技術是通過使用一個將多邊形與視角對齊的世界矩陣而起作用的。因爲你已經知道了視角(或者可以獲得一個視角變換矩陣),你只需要使用相反的觀察角來構造一個矩陣就行了。你不需要改變多邊形的位置,因爲你只關心其角度。
構造廣告牌世界矩陣(你可以將之運用到一個網格模型或多邊形上)的第一種方法是使用你已經知道的視角的相反值。例如,假設一個具有頂點的頂點緩存已經建立起來了。觀察點的角度被儲存在XRot, YRot 和ZRot 中,而廣告牌物體的座標是XCoord, YCoord 和ZCoord。下面告訴你如何建立用於渲染廣告牌頂點緩存的矩陣:
// g_pD3DDevice = pre-initialized deviceobject
D3DXMATRIX matBillboard;
D3DXMATRIX matBBXRot, matBBYRot, matBBZRot;
D3DXMATRIX matBBTrans;
// Construct the billboard matrix
// Use the opposite angles of the viewpointto align to view
D3DXMatrixRotationX(&matBBXRot, -XRot);
D3DXMatrixRotationY(&matBBYRot, -YRot);
D3DXMatrixRotationZ(&matBBZRot, -ZRot);
// Use the billboard object coordinates toposition
D3DXMatrixTranslation(&matBBTrans,XCoord, YCoord, ZCoord);
// Combine the matrices
D3DXMatrixIdentity(&matBillboard);
D3DXMatrixMultiply(&matBillboard,&matBillboard, &matBBTrans);
D3DXMatrixMultiply(&matBillboard,&matBillboard, &matBBZRot);
D3DXMatrixMultiply(&matBillboard,&matBillboard, &matBBYRot);
D3DXMatrixMultiply(&matBillboard,&matBillboard, &matBBXRot);
// Set the matrix
g_pD3DDevice->SetTransform(D3DTS_WORLD,&matBillboard);
// Continue to draw the vertex buffer,which is aligned
// to face the viewport, but at the propercoordinates.
在最後一行代碼之後,世界變換矩陣已經建立起來了,並且可以用來渲染廣告牌物體了。
創建一個廣告牌世界矩陣的第二種方法是從Direct3D 獲得當前的視角變換矩陣並將其轉置(取逆)。這個轉置後的矩陣會正確地讓所有的物體面朝觀察點的。你只需要運用網格模型的平移矩陣來將其正確地擺放到你的世界中。下面告訴你如何從視角變換矩陣來構造廣告牌矩陣並用之來繪製廣告牌物體:(原書這裏的代碼有一點小錯誤,下面已經改正。)
// g_pD3DDevice = pre-initialized deviceobject
D3DXMATRIX matTrans, matWorld, matTranspose;
// Get the current Direct3D view matrix
g_pD3DDevice->GetTransform(D3DTS_VIEW,&matTranspose);
// Get the billboard’s rotation matrix.
D3DXMatrixTranspose(&matTranspose,&matTranspose);
// Create the mesh’s translation matrix
D3DXMatrixTranslation(&matTrans,XCoord, YCoord, ZCoord);
// Multiply them together to form worldtransformation matrix
D3DXMatrixMultiply(&matWorld,&matTranspose, &matTrans);
// Set the world transformation matrix
g_pD3DDevice->SetTransform(D3DTS_WORLD,&matWorld);
// Continue to draw the vertex buffer,which is aligned
// to face the viewport, but at the proper coordinates.
廣告牌是一種強大的技術;事實上,它是其他一些特殊效果——例如粒子——的基礎。
===============================================================================
爲了演示廣告牌技術,作者寫了一個Billboard 程序,效果還不錯。然後我又將它改寫了一下。這一次的改寫的幅度也挺大的,因爲用到的shader 方法已經產生了本質性的影響;這是用“龍書”第二版第12章習題3的方法寫的。
下面是程序運行時的截圖:
下面是代碼下載地址: