1. 程式人生 > >在DirectX9.0中渲染文字的幾種方法

在DirectX9.0中渲染文字的幾種方法


本文翻譯自《Introduction to 3D Game Programming with DirectX 9.0》第九章“Fonts”,敬請斧正!
 
在遊戲中,文字資訊的顯示是必不可少的。本章將討論在D3D中使用的三種顯示字型的方法。同樣,這裡只列出每種方法的演示程式的主體框架。學完本章將達到如下目標:
l          學習如何使用ID3DXFont介面渲染文字
l          學習如何使用CD3DFont類渲染文字
l          學習計算遊戲幀速度的方法
l          學習使用D3DXCreateText函式建立3D文字
9.1. ID3DXFont
要在D3D程式輸出文字,使用D3DX庫提供的ID3DXFont介面很方便。其實該介面是使用GDI實現的,所以,其執行效率有所降低,但是它可以很方便地處理複雜的字型和格式。
9.1.1.    建立一個ID3DXFont
可使用D3DXCreateFontIndirect函式建立ID3DXFont:
HRESULT WINAPI D3DXCreateFontIndirect(
    LPDIRECT3DDEVICE9 pDevice,
    CONST D3DXFONT_DESC *pDesc,
    LPD3DXFONT *ppFont
);
譯者注:我所使用的DirectX SDK是Microsoft DirectX 9.0 SDK Update (Summer 2004),也就是使用DirectX 9.0c的那個版本,在較早的版本里,該函式的原型有所不同:第二個引數為CONST LOGFONT *pLogFont,它是GDI中的結構。以後,如無特別說明,將以DirectX 9.0 SDK Update (Summer 2004)中宣告的函式原型為準,特此宣告!
使用這個函式時,需要一個D3DXFONT_DESC結構:
D3DXFONT_DESC d3dFont;
memset(&d3dFont,0,sizeof(d3dFont));
d3dFont.Height=25; // in logical units
d3dFont.Width=12;  // in logical units
d3dFont.Weight=500;// boldness, range 0(light) - 1000(bold)
d3dFont.Italic=FALSE;
d3dFont.CharSet=DEFAULT_CHARSET;
strcpy(d3dFont.FaceName,"Times New Roman");
ID3DXFont* font=0;
D3DXCreateFontIndirect(Device,&d3dFont,&font);
如果你使用的是LOGFONT結構,則:
LOGFONT lf;
ZeroMemory(&lf, sizeof(LOGFONT));
lf.lfHeight = 25; // in logical units
lf.lfWidth = 12; // in logical units
lf.lfWeight = 500; // boldness, range 0(light) - 1000(bold)
lf.lfItalic = false;
lf.lfUnderline = false;
lf.lfStrikeOut = false;
lf.lfCharSet = DEFAULT_CHARSET;
strcpy(lf.lfFaceName, "Times New Roman"); // font style
ID3DXFont* font = 0;
D3DXCreateFontIndirect(Device, &lf, &font);
另外,還可以使用D3DXCreateFont函式建立ID3DXFont的物件。
9.1.2.    繪製文字
得到ID3DXFont介面後,繪製文字就很簡單了,只需要呼叫ID3DXFont::DrawText方法:
INT ID3DXFont::DrawText(
    LPD3DXSPRITE pSprite,
    LPCTSTR pString,
    INT Count,
    LPRECT pRect,
    DWORD Format,
    D3DCOLOR Color
);
l          pSprite –輸出目標,為ID3DXSprite物件指標,可以為NULL值,此時字串輸出到預設物件
l          pString –需要輸出的字串
l          Count –字串的字元數,如果為-1,則以0字元為結束標誌
l          pRect –繪製字串的區域
l          Format –文字的輸出格式
l          Color –文字顏色
例如,可以這樣使用該方法:
Font->DrawText(NULL,
    "Hello World", // String to draw.
    -1, // Null terminating string.
    &rect, // Rectangle to draw the string in.
    DT_TOP | DT_LEFT, // Draw in top-left corner of rect.
    0xff000000); // Black.
9.1.3.    計算幀速率
幀速率用FPS(Frame per Second)表示。首先宣告三個全域性變數:
DWORD FrameCnt; // The number of frames that have occurred.
float TimeElapsed; // The time that has elapsed so far.
float FPS; // The frames rendered per second.
每秒計算一次FPS,這裡計算的是平均值。我們也可以有足夠的時間去讀FPS值,而不至於因FPS的快速變化而產生較大誤差。
每渲染一幀,累加一次幀數和所用的時間值:
FrameCnt++;
TimeElapsed += timeDelta;
這裡的timeDelta表示兩幀間的時間間隔。每過去一秒,就可以使用下面的公式計算FPS了:
FPS = (float)FrameCnt / TimeElapsed;
計算完成後,需要將FrameCnt和TimeElapsed重新置0,這樣下一次的計算結果才正確:
void CalcFPS(float timeDelta)
{
    FrameCnt++;
    TimeElapsed += timeDelta;
    if(TimeElapsed >= 1.0f)
    {
        FPS = (float)FrameCnt / TimeElapsed;
        TimeElapsed = 0.0f;
        FrameCnt = 0;
    }
}
9.2. CD3DFont
在DirectX SDK的安裝目錄下,提供了很多有用的程式碼CD3DFont類就位於其中。該類使用紋理三角形和Direct3D渲染文字,而不是通過GDI渲染,所以,其效率高於ID3DXFont。但是,CD3DFont不支援複雜的字型和文字格式,如果只需要高速渲染簡單的字型,CD3DFont應是首選。
使用CD3DFont類時,需要加入以下原始檔:d3dfont.h,d3dfont.cpp,d3dutil.h,d3dutil.cpp,dxutil.h,dxutil.cpp。這些檔案可在DirectX SDK的安裝目錄下找到。在DirectX 9.0 SDK Update (Summer 2004)中,該類卻消失了,所以,該節內容只適用於較早的DirectX 9.0 SDK版本!
9.2.1.    建立CD3DFont物件
建立CD3DFont的物件,就像建立一個普通的C++物件一樣,其建構函式如下:
CD3DFont(const TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags=0L);
l          strFontName –所使用的字型名稱
l          dwHeight –字型高度
l          dwFlags –可選的標誌,如D3DFONT_BOLD、D3DFONT_ITALIC、D3DFONT_ZENABLE等。
得到CD3DFont物件後,還需按順序進行初始化,如:
Font = new CD3DFont("Times New Roman", 16, 0); // instantiate
Font->InitDeviceObjects( Device );
Font->RestoreDeviceObjects();
9.2.2.    繪製文字
使用CD3DFont物件的如下方法渲染文字:
HRESULT CD3DFont::DrawText(FLOAT x, FLOAT y, DWORD dwColor,
    const TCHAR* strText, DWORD dwFlags=0L);
l          x –螢幕的x座標
l          y –螢幕的y座標
l          dwColor –文字的顏色
l          strText –要繪製的字串
l          dwFlags –可選的渲染標誌,可為0或下面的值:D3DFONT_CENTERED、D3DFONT_TWOSIDED、D3DFONT_FILTERED。
例如:
Font->DrawText(20, 20, 0xff000000, “Hello, World”);
9.2.3.    資源清除
在刪除CD3DFont物件前,還需作一些清除的工作:
Font->InvalidateDeviceObjects();
Font->DeleteDeviceObjects();
delete Font;
9.3. 函式D3DXCreateText
該函式用於建立3D文字Mesh,原型如下:
HRESULT WINAPI D3DXCreateText(
    LPDIRECT3DDEVICE9 pDevice,
    HDC hDC,
    LPCTSTR pText,
    FLOAT Deviation,
    FLOAT Extrusion,
    LPD3DXMESH *ppMesh,
    LPD3DXBUFFER *ppAdjacency,
    LPGLYPHMETRICSFLOAT pGlyphMetrics
);
l          pDevice –D3D裝置
l          hDC –描述字型的裝置上下文控制代碼
l          pText –要渲染的字串
l          Deviation –TrueType字型的一個屬性,該值不能小於0。如果為0,則使用字型的預設值
l          Extrusion –字型的深度,相對於Z座標軸
l          ppMesh –返回生成的Mesh物件指標
l          ppAdjacency –返回鄰接資訊。如果不需要該資訊,可使用NULL指標
l          pGlyphMetrics –結構GLYPHMETRICSFLOAT的陣列,返回輪廓矩陣資料。如果不需要該資料,可以設該引數為NULL
下面就舉例說明如何使用該函式建立3D文字的Mesh物件。
// Obtain a handle to a device context.
HDC hdc = CreateCompatibleDC( 0 );
// Fill out a LOGFONT structure that describes the font’s properties.
LOGFONT lf;
ZeroMemory(&lf, sizeof(LOGFONT));
lf.lfHeight = 25; // in logical units
lf.lfWidth = 12; // in logical units
lf.lfWeight = 500; // boldness, range 0(light) - 1000(bold)
lf.lfItalic = false;
lf.lfUnderline = false;
lf.lfStrikeOut = false;
lf.lfCharSet = DEFAULT_CHARSET;
strcpy(lf.lfFaceName, "Times New Roman"); // font style
// Create a font and select that font with the device context.
HFONT hFont;
HFONT hFontOld;
hFont = CreateFontIndirect(&lf);
hFontOld = (HFONT)SelectObject(hdc, hFont);
// Create the 3D mesh of text.
ID3DXMesh* Text = 0;
D3DXCreateText(_device, hdc, "Direct3D", 0.001f, 0.4f, &Text, 0, 0);
// Reselect the old font, and free resources.
SelectObject(hdc, hFontOld);
DeleteObject( hFont );
DeleteDC( hdc );
這時,Mesh物件已經得到了。最後,直接使用DrawSubset方法渲染即可:
Text->DrawSubset( 0 );
譯者注:上面的例子程式碼,使用的是較早的DirectX 9.0 SDK,不是Summer 2004版本。
9.4. 總結
l          如果需要渲染較複雜的字型和格式,使用ID3DXFont介面會很方便。但是該介面是使用GDI實現的,故效率較低。
l          CD3DFont類可以快速的渲染簡單的字型。該類實用D3D的紋理三角形渲染文字,故速度較ID3DXFont為快。
l          使用D3DXCreateText函式可以建立文字的3D網格模型