9. 字体
本文翻译自《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网格模型。