在Direct3D渲染中当然也少不了文字。作为所有游戏最重要的一部分,绘制文字的问题也成了非常重要的一个话题。于是咱们接着研究关于文字的绘制。这部分学习完了以后,咱们就会拥有一个有着基本朴实功能的GraphEngine。可以绘制图片以及文字以后,剩下的事便可以干完了。
作为Direct3D中绘制文字的利器ID3DXFont有着完备的文字绘制功能。可是首先要说明的是,ID3DXFont在绘制文字的时候使用的是GDI的API,众所周知GDI是效率非常低下的东西。于是在这个基础上,DXUT提供了一个东西叫做CDXUTTextHelper,它封装了需要的ID3DXFont以及Sprite,并提高了工作效率。那么具体是怎么提高的呢?那就是它在内存中开辟了一块纹理,将绘制过的文字绘制在这上面,以后再次遇到这个字的时候就直接当做普通纹理处理了。这样做的代价就是内存的开销变大,经过咱的测试,稳定下来的大概会占掉200-300MB的内存,即使是在目前普及1G-2G内存的时代,这样的开销也算是非常巨大的,因此汝可以在ID3DXFont和CDXUTTextHelper之间作出选择,是牺牲空间换来时间还是牺牲时间换来空间。或者汝有更好的方法请回复。
那么接下来就是正题了,这里以ID3DXFont为讲解对象,CDXUTTextHelper跟这个类似。
跟以前一样先来看构造ID3DXFont的函数原型:
HRESULT D3DXCreateFont(
LPDIRECT3DDEVICE9 pDevice,
INT Height,
UINT Width,
UINT Weight,
UINT MipLevels,
BOOL Italic,
DWORD CharSet,
DWORD OutputPrecision,
DWORD Quality,
DWORD PitchAndFamily,
LPCTSTR pFacename,
LPD3DXFONT * ppFont
);
pDevice:Direct3D设备
Height:字符的逻辑单位高度
Weight:字符的逻辑单位宽度
Weight:字符粗细的枚举量,有以下几种:
/* Font Weights */
#define FW_DONTCARE 0
#define FW_THIN 100
#define FW_EXTRALIGHT 200
#define FW_LIGHT 300
#define FW_NORMAL 400
#define FW_MEDIUM 500
#define FW_SEMIBOLD 600
#define FW_BOLD 700
#define FW_EXTRABOLD 800
#define FW_HEAVY 900
MipLevels:好吧咱自重咱也不知道这个是干啥的不过也不需要了解它是干啥的填0就好了
Italic:是否斜体
CharSet:字体字符的设置,也就是一些字体的指定,一般填DEFAULT_CHARSET
#define ANSI_CHARSET 0
#define DEFAULT_CHARSET 1
#define SYMBOL_CHARSET 2
#define SHIFTJIS_CHARSET 128
#define HANGEUL_CHARSET 129
#define HANGUL_CHARSET 129
#define GB2312_CHARSET 134
#define CHINESEBIG5_CHARSET 136
#define OEM_CHARSET 255
#if(WINVER >= 0x0400)
#define JOHAB_CHARSET 130
#define HEBREW_CHARSET 177
#define ARABIC_CHARSET 178
#define GREEK_CHARSET 161
#define TURKISH_CHARSET 162
#define VIETNAMESE_CHARSET 163
#define THAI_CHARSET 222
#define EASTEUROPE_CHARSET 238
#define RUSSIAN_CHARSET 204
OutputPrecision:指定Windows如何把指定的字体大小和实际的字体相配,一般填OUT_DEFAULT_PRECIS
#define OUT_DEFAULT_PRECIS 0
#define OUT_STRING_PRECIS 1
#define OUT_CHARACTER_PRECIS 2
#define OUT_STROKE_PRECIS 3
#define OUT_TT_PRECIS 4
#define OUT_DEVICE_PRECIS 5
#define OUT_RASTER_PRECIS 6
#define OUT_TT_ONLY_PRECIS 7
#define OUT_OUTLINE_PRECIS 8
#define OUT_SCREEN_OUTLINE_PRECIS 9
#define OUT_PS_ONLY_PRECIS 10
Quality:指定Windows如何把指定的字体和实际的字体相配,一般填DEFAULT_QUALITY
#define DEFAULT_QUALITY 0
#define DRAFT_QUALITY 1
#define PROOF_QUALITY 2
#if(WINVER >= 0x0400)
#define NONANTIALIASED_QUALITY 3
#define ANTIALIASED_QUALITY 4
PitchAndFamily:
Pitch:一个位图内存地址距下一个位图内存地址的距离,一般填DEFAULT_PITCH
#define DEFAULT_PITCH 0
#define FIXED_PITCH 1
#define VARIABLE_PITCH 2
Family:这个看注释就知道了,不知道的话就填FF_DONTCARE
/* Font Families */
#define FF_DONTCARE (0<<4) /* Don't care or don't know. */
#define FF_ROMAN (1<<4) /* Variable stroke width, serifed. */
/* Times Roman, Century Schoolbook, etc. */
#define FF_SWISS (2<<4) /* Variable stroke width, sans-serifed. */
/* Helvetica, Swiss, etc. */
#define FF_MODERN (3<<4) /* Constant stroke width, serifed or sans-serifed. */
/* Pica, Elite, Courier, etc. */
#define FF_SCRIPT (4<<4) /* Cursive, etc. */
#define FF_DECORATIVE (5<<4) /* Old English, etc. */
pFacename:字体名称的宽字符串,例如L”Airia”
ppFont:ID3DXFont类型的指针,传入进去以此建立字体对象。
用D3DXCreateFont就可以创建各种各样的字体了。那么如何在实际的程序运行中用多种字体呢?你可以用map来管理,这样预处理出来是非常好的。
在附件中的工程的GameEngine.cpp的OnCreateDevice中可以看见字体的初始化过程。
字体对象建立好了后便是绘制了,下面看一下ID3DXFont::DrawText:
INT DrawText(
LPD3DXSPRITE pSprite,
LPCTSTR pString,
INT Count,
LPRECT pRect,
DWORD Format,
D3DCOLOR Color
);
pSprite:ID3DXFont要借用Sprite来绘制文字,于是第一个参数传入已经建立好的sprite
pString:需要绘制的文字的宽字符串
Count:需要绘制的文字的长度,若是-1则整个字符串一起画,否则按字数绘制
pRect:指定一个矩形来让文字按Format格式在此矩形内排列
Color:绘制文字的颜色
Format:绘制文字的格式。ID3DFont提供了多样的绘制格式,见下:
注:我的程序中已经将ID3DXFont封装进graphEngine,具体参见代码
DT_BOTTOM:指定在矩形底部绘制
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,0,200,200),DT_BOTTOM);
DT_CALCRECT:改变传入的矩形的长宽,变成刚好能容下需要画文字的矩形
DT_CENTER:指定在矩形的横向的中间绘制
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,0,200,200),DT_CENTER);
DT_EXPANDTABS:不忽略制表符绘制(\t),一个TAB占8个位置
graphEngine->DrawTexts(L"\tSumreen",MakeRect(0,0,200,200),DT_EXPANDTABS);
DT_LEFT:指定横向在矩形的左侧绘制
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,0,200,200),DT_LEFT);
DT_NOCLIP:不作任何格式限制及裁剪,以矩形左上角为绘制点绘制
graphEngine->DrawTexts(L"SumreenSumreenSumreenSumreen",MakeRect(0,0,50,200),DT_NOCLIP);
DT_RIGHT:指定在矩形右侧绘制文字
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,0,200,200),DT_RIGHT);
DT_SINGLELINE:在单行中绘制,忽略换行等
graphEngine->DrawTexts(L"Sumreen\nSumreen",MakeRect(0,0,200,200),DT_SINGLELINE);
DT_TOP:指定纵向在矩形顶部绘制
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,0,200,200),DT_TOP);
DT_VCENTER:指定纵向在矩形中间绘制
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,0,200,200),DT_VCENTER);
DT_WORDBREAK:指定在矩形中自动换行绘制,还可以智能换行以不切断单词
graphEngine->DrawTexts(L"Sumreen Sumreen Sumreen Sumreen Sumreen Sumreen Sumreen Sumreen",MakeRect(0,0,200,200),DT_WORDBREAK);
以上为常用的一些标示符,如果需要更多信息请参考DirectX SDK。
其中汝觉得标示符不冲突就可以用或运算同时使用:
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,0,200,200),DT_RIGHT|DT_BOTTOM);
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,0,200,200),DT_CENTER|DT_VCENTER);
其中常用的就是DT_NOCLIP了,即不作任何限制
最后要注意的是因为ID3DXFont是用Sprite在绘制内存中的纹理式的文字,也就是相当于普通的绘制纹理,于是一定要在Device和Sprite的Begin以及End之间再调用DrawText。
以上就是利用ID3DXFont绘制文字的基础内容。虽然说是基础,却也提供了非常多的实用的效果。对于制作GALGAME来说已经足够了。
顺便再说一个经常用到的效果:阴影以及边框
其实这个也不复杂,就只是平移一两个单位像素按阴影颜色再绘制一下就行了:
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,1,200,200),D3DCOLOR_ARGB(255,255,0,0),DT_NOCLIP);
graphEngine->DrawTexts(L"Sumreen",MakeRect(1,0,200,200),D3DCOLOR_ARGB(255,255,0,0),DT_NOCLIP);
graphEngine->DrawTexts(L"Sumreen",MakeRect(-1,0,200,200),D3DCOLOR_ARGB(255,255,0,0),DT_NOCLIP);
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,-1,200,200),D3DCOLOR_ARGB(255,255,0,0),DT_NOCLIP);
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,0,200,200),D3DCOLOR_ARGB(255,0,255,0),DT_NOCLIP);
graphEngine->DrawTexts(L"Sumreen",MakeRect(1,1,200,200),D3DCOLOR_ARGB(255,0,0,0),DT_NOCLIP);
graphEngine->DrawTexts(L"Sumreen",MakeRect(0,0,200,200),D3DCOLOR_ARGB(255,0,255,0),DT_NOCLIP);
嘛,文字的绘制就是这样了。现在绘制东西已经不成问题了,于是下一次便开始讲整个引擎的框架。
文章中可能会有疏忽和错误之处,也请大家不吝赐教。
代码工程下载(VS2008):
/Files/CK985/Direct3D_2D.rar