D3D绘制字体

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);

你可能感兴趣的:(D3D绘制字体)