D3DX库提供了ID3DXFont 接口,该接口用于在Direct3D应用程序中绘制文本。这个接口的使用,有优点,也有缺点。缺点就是在接口的内部使用了GDI图形设备接口,导致了该接口在性能上会有稍微的损失。优点就是由于在接口的内部使用了GDI图形设备接口,导致了它能够处理一些复杂的字体和格式。
1.如何使用ID3DXFont接口绘制文本信息
//字体接口
ID3DXFont* Font = 0;
在创建完毕后,我们需要对索要使用的字体进行描述,那么这个时候就需要用到D3DXFONT_DESC结构。该结构的参数如下
typedef struct D3DXFONT_DESC {
INT Height;//Height, in logical units,指的是字体的高度,
UINT Width;//Width, in logical units
UINT Weight;//Weight of the font in the range from 0 through 1000.
UINT MipLevels;//文本的Miplevels
BOOL Italic;//是否斜体
BYTE CharSet;//字符集
BYTE OutputPrecision;// 指定Windows与期望字体大小的匹配方法
BYTE Quality;//输出质量
BYTE PitchAndFamily;//斜度和family索引
TCHAR FaceName;//字体类型名称
} D3DXFONT_DESC, *LPD3DXFONT_DESC;
D3DXFONT_DESC df;
ZeroMemory(&df, sizeof(D3DXFONT_DESC));
df.Height = 25;
df.Width = 12;
df.MipLevels = D3DX_DEFAULT;
df.Italic = false;
df.CharSet = DEFAULT_CHARSET;
df.OutputPrecision = 0;
df.Quality = 0;
df.PitchAndFamily = 0;
strcpy_s(df.FaceName, "TIME NEW ROMAN");
INT DrawText(
[in] LPD3DXSPRITE pSprite,//用来绘制文字的sprite对象
[in] LPCTSTR pString,//指向字符串的指针
[in] INT Count,//字数.假如字符是以null结束的字符串则可将其指定为-1
[in] LPRECT pRect,//指向一个RECT结构,它定义一个文字被绘制在屏幕上的范围。
[in] DWORD Format,//可选参数,指定文字怎样被格式化,具体可以查sdk文档
[in] D3DCOLOR Color//文字的颜色
);
Font->DrawTextA(NULL, "Hello World", -1,&rect, DT_TOP | DT_LEFT, 0xff000000);
以上就完成了。
#include
#include
#include
#include
using namespace std;
#define SCREEN_WIDTH 800 //窗口宽度
#define SCREEN_HEIGHT 600 //窗口高度
#define FVF_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1)
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } //自定义一个SAFE_RELEASE()宏,便于资源的释放
IDirect3DDevice9* _device; //Direct3D设备
//字体接口
ID3DXFont* Font = 0;
//函数声明
//初始化Direct3D
//消息过程,处理消息
bool InitD3D(HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9 ** device);
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
//以下为窗口过程
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY: //销毁
::PostQuitMessage(0);//终止请求
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
::DestroyWindow(hwnd);
break;
}
//调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。
//该函数确保每一个消息得到处理
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
//初始化D3D
bool InitD3D(HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9 ** device)
{
//定义一个完整的窗口类
WNDCLASS wc;
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszClassName = "LSZDX";
wc.lpszMenuName = 0;
//注册窗口类
if (!(RegisterClass(&wc)))
{
MessageBox(0, "RegisterClass is FAILED", 0, 0);
return false;
}
//创建窗口
HWND hwnd = 0;
hwnd = CreateWindow("LSZDX", "LSZDXDEMO", WS_OVERLAPPEDWINDOW, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, hInstance, 0);
if (hwnd == 0)
{
MessageBox(0, "CreateWIndow is FAILED", 0, 0);
return false;
}
//绘制和更新窗口
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
//以下为初始化D3D
//*---------------
//获取IDirect3D9的指针
IDirect3D9 *d3d9 = 0;
d3d9 = Direct3DCreate9((D3D_SDK_VERSION));
if (!d3d9)
{
MessageBox(0, "Direct3D9Create9 is FAILED", 0, 0);
return false;
}
//检验硬件顶点运算
D3DCAPS9 caps;
d3d9->GetDeviceCaps(
D3DADAPTER_DEFAULT,
deviceType,
&caps);
int vp = 0;
if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
//填充D3DPRESENT_PARAMETERS
D3DPRESENT_PARAMETERS d3dpp;
d3dpp.BackBufferWidth = width;
d3dpp.BackBufferHeight = height;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferCount = 1;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hwnd;
d3dpp.Windowed = windowed;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
//创建IDirect3DDevice9接口
if (FAILED(d3d9->CreateDevice(
D3DADAPTER_DEFAULT,
deviceType,
hwnd,
vp,
&d3dpp,
device)))
{
MessageBox(0, "CreateDevice is FAILED", 0, 0);
return false;
}
d3d9->Release();
return true;
}
//初始化工作
bool Setup()
{ //填充D3DXFONT_DESC结构
D3DXFONT_DESC df;
ZeroMemory(&df, sizeof(D3DXFONT_DESC));
df.Height = 25;
df.Width = 12;
df.MipLevels = D3DX_DEFAULT;
df.Italic = false;
df.CharSet = DEFAULT_CHARSET;
df.OutputPrecision = 0;
df.Quality = 0;
df.PitchAndFamily = 0;
strcpy_s(df.FaceName, "TIME NEW ROMAN");
//创建ID3DXFont对象
D3DXCreateFontIndirect(_device, &df, &Font);
return true;
}
//显示操作
bool DisPlay()
{
MSG msg;
::ZeroMemory(&msg, sizeof(MSG));//用0来填充消息可类比为:memset()函数
while (msg.message != WM_QUIT)
{
//PeekMessage函数是以查看的方式从系统中获取消息
//并将该消息(如果存在)放于指定的结构
if (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
//PM_REMOVE:PeekMessage处理后,消息从队列里除掉。
//TranslateMessage函数将虚拟键消息转换为字符消息。
//字符消息被寄送到调用线程的消息队列里,
//当下一次线程调用函数GetMessage或PeekMessage时被读出。
//TranslateMessage只能用于转换调用GetMessage或PeekMessage接收的消息。
::TranslateMessage(&msg);
//DispatchMessage函数
//该函数分发一个消息给窗口程序。
//通常消息从GetMessage函数获得。
//消息被分发到回调函数(过程函数),作用是消息传递给操作系统,
//然后操作系统去调用我们的回调函数,也就是说我们在窗体的过程函数中处理消息
::DispatchMessage(&msg);
}
else
{
if (_device)
{
//
// 绘制场景
//
_device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
_device->BeginScene();
//绘制文本
RECT rect = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
Font->DrawTextA(NULL, "Hello World", -1,&rect, DT_TOP | DT_LEFT, 0xff000000);
_device->EndScene();
_device->Present(0, 0, 0, 0);
}
}
}
return true;
}
//
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
if (!InitD3D(hInstance, SCREEN_WIDTH, SCREEN_HEIGHT, true, D3DDEVTYPE_HAL, &_device))
{
MessageBox(0, "InitD3D is FAILED", 0, 0);
return 0;
}
if (!Setup())
{
MessageBox(0, "Setup() - FAILED", 0, 0);
return 0;
}
DisPlay();
_device->Release();
return 0;
}
那么如何创建3D字体呢?
ID3DXMesh * Text = 0;
如上一样填充字体存储的结构,但是这里我们填充的是LOGFONT结构:
老版本的DirectX使用LOGFONT作为参数创建字体,在DirectX9.0c以及以后的版本里面改为使用D3DXFONT_DESC作为参数创建字体。这两个结构体大体是相同的。
下面分别列举一个使用D3DXFONT_DESC和LOGFONT作为参数的例子:
使用这个函数时,需要一个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);
我们可以这样填充:
HDC hdc = CreateCompatibleDC(0);
HFONT hFont;
HFONT hFontOld;
LOGFONT lf;
ZeroMemory(&lf, sizeof(LOGFONT));
lf.lfHeight = 25; // in logical units
lf.lfWidth = 12; // in logical units
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = 500; // boldness, range 0(light) - 1000(bold)
lf.lfItalic = false;
lf.lfUnderline = false;
lf.lfStrikeOut = false;
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfOutPrecision = 0;
lf.lfClipPrecision = 0;
lf.lfQuality = 0;
lf.lfPitchAndFamily = 0;
strcpy_s(lf.lfFaceName, "Times New Roman"); // font style
hFont = CreateFontIndirect(&lf);
hFontOld = (HFONT)SelectObject(hdc, hFont);
在D3D中,自带了一个函数:D3DXCreateText函数,就是用此函数来创建3D文本:
D3DXCreateText函数参数如下:
HRESULT D3DXCreateText(
LPDIRECT3DDEVICE8 pDevice,//与网格相关的设备
HDC hDC, //一个环境句柄
LPCTSTR pText, //只想所要生成的文本字符串指针
FLOAT Deviation, //TureType字体轮廓的最大弦偏差。
FLOAT Extrusion, //Z、轴负方向度量的字体深度
LPD3DXMESH* ppMesh, //返回所创建的网格
LPD3DXMESH* ppAdjacency, //返回所创建的网格的邻接信息
LPGLYPHMETRICSFLOAT pGlyphMetrics
);
#include
#include
#include
#include
using namespace std;
#define SCREEN_WIDTH 800 //窗口宽度
#define SCREEN_HEIGHT 600 //窗口高度
#define FVF_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1)
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } //自定义一个SAFE_RELEASE()宏,便于资源的释放
IDirect3DDevice9* _device; //Direct3D设备
ID3DXMesh * Text = 0; //字体网格结构
//函数声明
//初始化Direct3D
//消息过程,处理消息
bool InitD3D(HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9 ** device);
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
D3DMATERIAL9 InitMtrl(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p);
D3DLIGHT9 InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color);
const D3DXCOLOR BLACK(D3DCOLOR_XRGB(0, 0, 0));
const D3DXCOLOR WHITE(D3DCOLOR_XRGB(255, 255, 255));
D3DMATERIAL9 WHITE_MTRL = InitMtrl(WHITE, WHITE, WHITE, BLACK, 2.0f);
//初始化材质:
D3DMATERIAL9 InitMtrl(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p)
{
D3DMATERIAL9 mtrl;
mtrl.Ambient = a;
mtrl.Diffuse = d;
mtrl.Specular = s;
mtrl.Emissive = e;
mtrl.Power = p;
return mtrl;
}
//初始化直射光:
D3DLIGHT9 InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color)
{
D3DLIGHT9 light;
::ZeroMemory(&light, sizeof(light));
light.Type = D3DLIGHT_DIRECTIONAL;
light.Ambient = *color * 0.4f;
light.Diffuse = *color;
light.Specular = *color * 0.6f;
light.Direction = *direction;
return light;
}
//函数声明
//初始化Direct3D
//消息过程,处理消息
bool InitD3D(HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9 ** device);
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
//以下为窗口过程
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY: //销毁
::PostQuitMessage(0);//终止请求
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
::DestroyWindow(hwnd);
break;
}
//调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。
//该函数确保每一个消息得到处理
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
//初始化D3D
bool InitD3D(HINSTANCE hInstance, int width, int height, bool windowed, D3DDEVTYPE deviceType, IDirect3DDevice9 ** device)
{
//定义一个完整的窗口类
WNDCLASS wc;
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszClassName = "LSZDX";
wc.lpszMenuName = 0;
//注册窗口类
if (!(RegisterClass(&wc)))
{
MessageBox(0, "RegisterClass is FAILED", 0, 0);
return false;
}
//创建窗口
HWND hwnd = 0;
hwnd = CreateWindow("LSZDX", "LSZDXDEMO", WS_OVERLAPPEDWINDOW, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, hInstance, 0);
if (hwnd == 0)
{
MessageBox(0, "CreateWIndow is FAILED", 0, 0);
return false;
}
//绘制和更新窗口
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
//以下为初始化D3D
//*---------------
//获取IDirect3D9的指针
IDirect3D9 *d3d9 = 0;
d3d9 = Direct3DCreate9((D3D_SDK_VERSION));
if (!d3d9)
{
MessageBox(0, "Direct3D9Create9 is FAILED", 0, 0);
return false;
}
//检验硬件顶点运算
D3DCAPS9 caps;
d3d9->GetDeviceCaps(
D3DADAPTER_DEFAULT,
deviceType,
&caps);
int vp = 0;
if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
//填充D3DPRESENT_PARAMETERS
D3DPRESENT_PARAMETERS d3dpp;
d3dpp.BackBufferWidth = width;
d3dpp.BackBufferHeight = height;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferCount = 1;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hwnd;
d3dpp.Windowed = windowed;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
//创建IDirect3DDevice9接口
if (FAILED(d3d9->CreateDevice(
D3DADAPTER_DEFAULT,
deviceType,
hwnd,
vp,
&d3dpp,
device)))
{
MessageBox(0, "CreateDevice is FAILED", 0, 0);
return false;
}
d3d9->Release();
return true;
}
//初始化工作
bool Setup()
{
HDC hdc = CreateCompatibleDC(0);
HFONT hFont;
HFONT hFontOld;
LOGFONT lf;
ZeroMemory(&lf, sizeof(LOGFONT));
lf.lfHeight = 25; // in logical units
lf.lfWidth = 12; // in logical units
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = 500; // boldness, range 0(light) - 1000(bold)
lf.lfItalic = false;
lf.lfUnderline = false;
lf.lfStrikeOut = false;
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfOutPrecision = 0;
lf.lfClipPrecision = 0;
lf.lfQuality = 0;
lf.lfPitchAndFamily = 0;
strcpy_s(lf.lfFaceName, "Times New Roman"); // font style
hFont = CreateFontIndirect(&lf);
hFontOld = (HFONT)SelectObject(hdc, hFont);
D3DXCreateText(_device, hdc, "LSZDX", 0.001f, 0.4f, &Text, 0, 0);
SelectObject(hdc, hFontOld);
DeleteObject(hFont);
DeleteDC(hdc);
//
// Lights.
//
D3DXVECTOR3 dir(0.0f, -0.5f, 1.0f);
D3DXCOLOR col = WHITE;
D3DLIGHT9 light = InitDirectionalLight(&dir, &col);
_device->SetLight(0, &light);
_device->LightEnable(0, true);
_device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
_device->SetRenderState(D3DRS_SPECULARENABLE, true);
//
// Set camera.
//
D3DXVECTOR3 pos(0.0f, 1.5f, -3.3f);
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX V;
D3DXMatrixLookAtLH(
&V,
&pos,
&target,
&up);
_device->SetTransform(D3DTS_VIEW, &V);
//
// Set projection matrix.
//
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
&proj,
D3DX_PI * 0.25f, // 45 - degree
(float)SCREEN_WIDTH / (float)SCREEN_HEIGHT,
0.01f,
1000.0f);
_device->SetTransform(D3DTS_PROJECTION, &proj);
return true;
}
//显示操作
bool DisPlay()
{
MSG msg;
::ZeroMemory(&msg, sizeof(MSG));//用0来填充消息可类比为:memset()函数
while (msg.message != WM_QUIT)
{
//PeekMessage函数是以查看的方式从系统中获取消息
//并将该消息(如果存在)放于指定的结构
if (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
//PM_REMOVE:PeekMessage处理后,消息从队列里除掉。
//TranslateMessage函数将虚拟键消息转换为字符消息。
//字符消息被寄送到调用线程的消息队列里,
//当下一次线程调用函数GetMessage或PeekMessage时被读出。
//TranslateMessage只能用于转换调用GetMessage或PeekMessage接收的消息。
::TranslateMessage(&msg);
//DispatchMessage函数
//该函数分发一个消息给窗口程序。
//通常消息从GetMessage函数获得。
//消息被分发到回调函数(过程函数),作用是消息传递给操作系统,
//然后操作系统去调用我们的回调函数,也就是说我们在窗体的过程函数中处理消息
::DispatchMessage(&msg);
}
else
{
if (_device)
{
D3DXMATRIX yRot, T;
static float y = 0.0f;
D3DXMatrixRotationY(&yRot, y);
y += 0.001f;
if (y >= 6.28f)
y = 0.0f;
D3DXMatrixTranslation(&T, -1.6f, 0.0f, 0.0f);
T = T * yRot;
_device->SetTransform(D3DTS_WORLD, &T);
//
// 绘制场景
//
_device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);
_device->BeginScene();
_device->SetMaterial(&WHITE_MTRL);
Text->DrawSubset(0);
_device->EndScene();
_device->Present(0, 0, 0, 0);
}
}
}
return true;
}
//
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
if (!InitD3D(hInstance, SCREEN_WIDTH, SCREEN_HEIGHT, true, D3DDEVTYPE_HAL, &_device))
{
MessageBox(0, "InitD3D is FAILED", 0, 0);
return 0;
}
if (!Setup())
{
MessageBox(0, "Setup() - FAILED", 0, 0);
return 0;
}
DisPlay();
_device->Release();
return 0;
}