1.ID3DXFont绘制字体(用GDI支持中文,用FreeType得到的中文字体效果更佳)
ID3DXFont简单的设置字体
ID3DXFont是基于GDI接口的字体绘制,绘制速度比较慢,但是能够处理复杂的字体设置和格式。图形应用程序底层都是使用GDI或DirectX或者Opengl图形图像库来绘制字体,图形图像,模型,视频动画的。
关键是
D3DXFONT_DESCA结构体的设置,
D3DXCreateFontIndirect获取ID3DXFont接口对象;或者D3DXCreateFont直接创建ID3DXFont接口对象。
ID3DXFont的绘制字体函数DrawText根据传入字体或者纹理直接绘制出来。
(DrawText时候可以用ID3DXSprite m_pSprite也可以不用传NULL,如果用m_pSprite那么需要m_pSprite调用:
m_pSprite
->Begin( D3DXSPRITE_ALPHABLEND | D3DXSPRITE_SORT_TEXTURE );
DrawText绘制字体;
m_pSprite->End();)
// 用ID3DXFont绘制字体
IDirect3DDevice9* Device = 0;
const int Width = 640;
const int Height = 480;
ID3DXFont* Font = 0;
DWORD FrameCnt = 0;
float TimeElapsed = 0;
float FPS = 0;
char FPSString[32];
ID3DXSprite *m_pSprite = NULL;
//
// Framework functions
//
bool Setup()
{
D3DXFONT_DESCA fontDesc;
ZeroMemory(&fontDesc, sizeof(D3DXFONT_DESCA));
fontDesc.Height = 25;
fontDesc.Width = 12;
fontDesc.Weight = 500;
fontDesc.MipLevels = D3DX_DEFAULT;
fontDesc.Italic = FALSE;
fontDesc.CharSet = DEFAULT_CHARSET;
fontDesc.OutputPrecision = 0;
fontDesc.Quality = 0;
fontDesc.PitchAndFamily = 0;
strcpy_s(fontDesc.FaceName, "Times New Roman");
//D3DXCreateFont(Device, ...arg1 as D3DXFONT_DESCA , &Font);
if(FAILED(D3DXCreateFontIndirect(Device, &fontDesc, &Font)))
{
::MessageBox(0, "D3DXCreateFontIndirect() - FAILED", 0, 0);
::PostQuitMessage(0);
}
// m_pSprite具体作用需要更深入研究,应该是来自于freeType这样的字体库
if(FAILED(D3DXCreateSprite(Device, &m_pSprite)))
{
return false;
}
return true;
}
void Cleanup()
{
d3d::Release<ID3DXFont*>(Font);
d3d::Release<ID3DXSprite*>(m_pSprite);
}
bool Display(float timeDelta)
{
if( Device )
{
//
// Update: Compute the frames per second.
//
FrameCnt++;
TimeElapsed += timeDelta;
if(TimeElapsed >= 1.0f)
{
FPS = (float)FrameCnt / TimeElapsed;
sprintf(FPSString, "FPS:%1.2f", FPS);
FPSString[31] = '\0'; // mark end of string
TimeElapsed = 0.0f;
FrameCnt = 0;
}
//
// Render
//
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
Device->BeginScene();
RECT rect = {Width/2 - 60, Height/2 -6, Width, Height};
m_pSprite->Begin( D3DXSPRITE_ALPHABLEND | D3DXSPRITE_SORT_TEXTURE );
// m_pSprite为空直接根据D3DXFONT_DESCA描述的字体库绘制字体,
// 如果m_pSprite非空,那么使用m_pSprite指向的纹理进行绘制。
Font->DrawText(
/*NULL*/m_pSprite,
FPSString,
-1, // size of string or -1 indicates null terminating string
&rect, // rectangle text is to be formatted to in windows coords
DT_TOP | DT_LEFT, // draw in the top left corner of the viewport
0xff000000); // black text
m_pSprite->End();
Device->EndScene();
Device->Present(0, 0, 0, 0);
}
return true;
}
复杂的使用ID3DXSprite和FreeType字库字模得到字体纹理来绘制字体
使用FreeType字库,可以用:
LPD3DXSPRITE m_pSprite; // 字体精灵,可以直接绘制字体
LPDIRECT3DTEXTURE9 m_pTex;// 获取纹理,从FreeType字模得到的纹理
// 得到字模,然后根据字模填充纹理
FT_Init_FreeType(&m_FT);
FT_New_Face(pFont->m_FT, pFilename, 0, &m_Face);
FT_Set_Pixel_Sizes(m_Face, 0, dwSize);
FT_Face Face = pFace->m_Face;
FT_GlyphSlot Glyph = Face->glyph;
FT_Load_Char(Face, dwChar, FT_LOAD_RENDER);
FT_GlyphSlot Glyph = Face->glyph;
// 没有查询到填充纹理
hash_map<__int64, CChar*>::iterator it = m_Chars.find(nKey);
if(it == m_Chars.end())
{
CreateTexture(nWidth,
nHeight,
nLevels,
dwUsage,
Format,
Pool,
&m_pTex,
0);
D3DLOCKED_RECT LockedRect;
pTexture->m_pTex->LockRect(0, &LockedRect, &Rect, 0);
if(Glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
{
for(int y = 0; y < hgt; y++)
{
for(int x = 0; x < wid; x++)
{
BYTE byValue = (Glyph->bitmap.buffer[y * Glyph->bitmap.pitch + x / 8] >> (7 - (x % 8))) & 1;
((DWORD*)LockedRect.pBits)[y * LockedRect.Pitch + x] = D3DCOLOR_ARGB(
byValue * 255,
255,
255,
255);
bColor |= byValue > 0;
}
}
}
// 存放得到的字模纹理
hash_map<__int64, CChar*> m_Chars;
m_Chars[nKey] = pChar;
}
// 绘制字体
m_pSprite->Draw(
pChar->m_pTexture->m_pTex,
&rt,
NULL,
&VtPos,
Color);
2.CD3DFont自封装用D3D绘制三角形纹理方式实现简单字体的绘制(不支持中文)
用D3D创建纹理,然后用GDI字符位图填充ASCII32-126字符的纹理坐标系和纹理,将纹理坐标赋值给字体顶点,然后通过绘制三角形顶点方式来将字体绘制出来。因为绘制的字体不能支持更多的字体库和设置字体的风格样式,所以尽管运行速度比较快,但是用得还是比较少。
且不支持非ASCII字符,所以实际游戏中应用少。
m_strFontName是字体类型,例如"Times New Roman":
// 得到字体库对象
HFONT hFont = CreateFont( nHeight, 0, 0, 0, dwBold, dwItalic,
FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
VARIABLE_PITCH, m_strFontName );
// 创建纹理
hr = m_pd3dDevi
ce->CreateTexture( m_dwTexWidth, m_dwTexHeight, 1,
0, D3DFMT_A4R4G4B4,
D3DPOOL_MANAGED, &m_pTexture, 0 );
// 将字体点阵坐标填充到纹理坐标m_fTexCoords中和纹理m_pTexture中
DWORD x = 0;
DWORD y = 0;
TCHAR str[2] = _T("x");
SIZE size;
for( TCHAR c=32; c<127; c++ )
{
str[0] = c;
// str将会获得ASCII中的字体,32位到126位的字符
// 通过GetTextExtentPoint32得到size的大小,从而进行x,y的增加
GetTextExtentPoint32( hDC, str, 1, &size );
// y纹理坐标位置改变
if( (DWORD)(x+size.cx+1) > m_dwTexWidth )
{
x = 0;
y += size.cy+1;
}
ExtTextOut( hDC, x+0, y+0, ETO_OPAQUE, NULL, str, 1, NULL );
// 将GDI中纹理坐标位置赋值给m_fTexCoords,其中一维是字符,二维是每个字符的两个三角形的顶点
m_fTexCoords[c-32][0] = ((FLOAT)(x+0))/m_dwTexWidth;
m_fTexCoords[c-32][1] = ((FLOAT)(y+0))/m_dwTexHeight;
m_fTexCoords[c-32][2] = ((FLOAT)(x+0+size.cx))/m_dwTexWidth;
m_fTexCoords[c-32][3] = ((FLOAT)(y+0+size.cy))/m_dwTexHeight;
// x纹理坐标位置改变
x += size.cx+1;
}
// 将简单的坐标纹理Alpha值写人纹理中
// Lock the surface and write the alpha values for the set pixels
D3DLOCKED_RECT d3dlr;
m_pTexture->LockRect( 0, &d3dlr, 0, 0 );
BYTE* pDstRow = (BYTE*)d3dlr.pBits;
WORD* pDst16;
BYTE bAlpha; // 4-bit measure of pixel intensity
for( y=0; y < m_dwTexHeight; y++ )
{
pDst16 = (WORD*)pDstRow;
for( x=0; x < m_dwTexWidth; x++ )
{
// bAlpha 为像素中的R1R2G1G2B1B2A1A2中的A1
bAlpha = (BYTE)((pBitmapBits[m_dwTexWidth*y + x] & 0xff) >> 4);
if (bAlpha > 0)
{
// bA1pha<<12是将A1左移12位得到0102 0102 A102 0102 截取后面16位也就是得到:A102 0102
// 得到16位的alpha值
*pDst16++ = (bAlpha << 12) | 0x0fff;
}
else
{
*pDst16++ = 0x0000;
}
}
pDstRow += d3dlr.Pitch;
}
// Done updating texture, so clean up used objects
m_pTexture->UnlockRect(0);
DeleteObject( hbmBitmap );
//从m_fTexCoords写入顶点UV坐标系中,并且为DX设置纹理m_pTexture
FONT2DVERTEX* pVertices = NULL;
DWORD dwNumTriangles = 0;
m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
while( *strText )
{
TCHAR c = *strText++;
if( c == _T('\n') )
{
sx = fStartX;
sy += (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight;
}
if( (c-32) < 0 || (c-32) >= 128-32 )
continue;
FLOAT tx1 = m_fTexCoords[c-32][0];
FLOAT ty1 = m_fTexCoords[c-32][1];
FLOAT tx2 = m_fTexCoords[c-32][2];
FLOAT ty2 = m_fTexCoords[c-32][3];
FLOAT w = (tx2-tx1) * m_dwTexWidth / m_fTextScale;
FLOAT h = (ty2-ty1) * m_dwTexHeight / m_fTextScale;
if( c != _T(' ') )
{
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx1, ty2 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx1, ty1 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx2, ty2 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx2, ty1 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx2, ty2 );
*pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+0-0.5f,0.9f,1.0f), dwColor, tx1, ty1 );
dwNumTriangles += 2;
if( dwNumTriangles*3 > (MAX_NUM_VERTICES-6) )
{
// Unlock, render, and relock the vertex buffer
m_pVB->Unlock();
m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
pVertices = NULL;
m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
dwNumTriangles = 0L;
}
}
sx += w;
}
m_pd3dDevice->SetTexture( 0, m_pTexture );
// 通过绘制顶点三角形来绘制字体
m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(FONT2DVERTEX) );
if( dwNumTriangles > 0 )
m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
3.用D3DXCreateText创建网格来渲染字体(不支持中文 )
用GDI获取设备句柄:
HDC hdc = CreateCompatibleDC( 0 );
HFONT hFont;
HFONT hFontOld;
用GDI设计字体风格:
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(lf.lfFaceName, "Times New Roman"); // font style
创建和选择字体句柄:
hFont = CreateFontIndirect(&lf);
// 先保存之前的HFONT,用新的字体对象绘制完后,将对象设置回去
hFontOld = (HFONT)SelectObject(hdc, hFont);
创建字体网格:
D3DXCreateText(Device, hdc, "Why Only Eng"/*"Direct3D"*/,
0.001f, 0.4f, &Text, 0, 0);
SelectObject(hdc, hFontOld);
DeleteObject( hFont );
DeleteDC( hdc );
设置灯光:
D3DXVECTOR3 dir(0.0f, -0.5f, 1.0f);
D3DXCOLOR col = d3d::WHITE;
D3DLIGHT9 light = d3d::InitDirectionalLight(&dir, &col);
Device->SetLight(0, &light);
Device->LightEnable(0, true);
Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
Device->SetRenderState(D3DRS_SPECULARENABLE, true);
// 绘制网格
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);
Device->BeginScene();
Device->SetMaterial(&d3d::WHITE_MTRL);
Text->DrawSubset(0);
Device->EndScene();
Device->Present(0, 0, 0, 0);