天天看點

OpenGL11-繪制漢字最高效方法(使用Freetype)(代碼已更新)

最新版本,之前的版本有些檔案沒有打包 

視訊教程請關注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440

OpenGL11-繪制漢字最高效方法(使用Freetype)(代碼已更新)

OpenGL本身并沒有繪制文字的功能,他隻是一個三維繪圖的API集和,很多東西都要

自己動手才可以實作。OpenGL繪制文字,網絡上已經有很多成熟的方式方法,我這裡給

大家介紹的是我使用的方式,從繪制的效率上來說,速度上從已經達到我個人水準的最大值。

如果你有更好的方式,請聯系我。

  首先介紹下網絡上的一些繪制方式。

  一、将要繪制的文字按照每一個字生成一個小紋理的方式,然後再用将紋理貼到網格

的表面,繪制出來,例如:“部落格園-你好”,則會生成6個小紋理,然後生成網格,将紋理

貼到網格的表面。優點:每一個字的大小顔色都可選擇。缺點:文字多了以後,頻繁的切換

紋理造成效率低下。OSG中使用了這種方式,效率極差,尤其是在文字更新的情況下。

  二、直接将随繪制的文本字元串生成一個紋理資料(而不是一個文字一個紋理),這樣

做效率上比第一種要好很多,缺點就是更新的時候要重新建構一個新的紋理。速度上有很大

損失。兩種方式的原理圖如下:

OpenGL11-繪制漢字最高效方法(使用Freetype)(代碼已更新)

  三、将所繪制的文字都放到一個較大的紋理上去,然後再紋理上做索引,當繪制的時候,

去查表。在将紋理貼到網格上繪制出來,這種方式,速度很快,很多遊戲引擎都在使用這種

方式,存在的問題繪制的文字多了以後速度會變慢,占用大量的cpu時間,當然對于小的應

用已經足夠了,今天我提出的方式也是基于這樣方式的,隻是我在索引上最了一些改進,改

進之後的算法,将不再進行查表操作,而是直接索引,一定程度上降低了cpu消耗,提搞了

效率。紋理圖檔如下示意圖:

  

OpenGL11-繪制漢字最高效方法(使用Freetype)(代碼已更新)

圖檔存的資料上也很重要,結合OpenGL,采用GL_ALPHA的上存儲,這樣可以大大

降低圖檔所占用的顯存空間,進而提高效率。

下面定義一個文字所存儲的資訊,如下所示:

class   Character
{
public:
    Character()
    {
        x0          =   0;
        y0          =   0;
        x1          =   0;
        y1          =   0;
    }
    /**
    *   存儲目前字元在紋理上的坐标位置
    */
    unsigned short   x0;
    unsigned short   y0;
    unsigned short   x1;
    unsigned short   y1;
};      

 為了快速索引,減少查找的過程,我麼要結合字型本上來做一些處理,我們知道一個漢字要占用2個位元組

兩個位元組所能表示的範圍是0-65535,就是6萬多個漢字,那麼我們就聲明一個這麼大的數組來存儲字元的

資訊:

代碼如下:

Character   g_charMap[1<<16];      

當我們要繪制一個文字的時候,可以直接通過下标就可以直接定位到該字對應的資訊了,例如我們繪制'中'

就可以直接擷取其在圖檔上的内容了:

Character   getCharacter(wchar_t ch)
{
    return g_charMap[ch]; 
}      

然後這樣也存在一個問題,即一張多大的紋理才可以容納這麼多的字元呢?如果是一般的應用,我們可以忽略

這個問題,中國常用的漢字隻有3000多個,假設是一個漢字占16*16的空間,那麼一個1024*1024的紋理

所能容納的字元有 1024/16 * 1024/16 = 4096個,足夠滿足正常的需要,如果你是想做一個通用的,沒有

瑕疵的應用,我們就要修改我們的設計方式,我能一切從速度優先的方式考慮,我們在上面的Character類中

增加一個字段描述字元所在的紋理句柄,如下:

class   Character
{
public:
    Character()
    {
        x0          =   0;
        y0          =   0;
        x1          =   0;
        y1          =   0;
        texId       =   0;
    }
    unsigned short   x0;
    unsigned short   y0;
    unsigned short   x1;
    unsigned short   y1;
    //! 索引紋理,即目前字元所在的紋理
    unsigned         texId;
};      

這樣,就可以拜托之前的限制,實作大規模的繪制文字了。

說完了方案,我們還有說下實作方式,說道文字繪制,不得不提的就是FreeType字型庫了

幾乎所有的三維繪制文字的方式,都采用這個庫。下面以代碼的方式來介紹他,我們這裡

隻用到其中很少的一部分(FreeType)相關内容這裡不做介紹,有興趣的同學可以自行了解。

第一步:初始化字型庫

FT_Init_FreeType( &_library );      

第二步:加載字型

FT_New_Face(_library,_fontFace,0,&_face);      

第三步:設定字型大小

FT_Set_Char_Size( _face, fontSize << 6, fontSize << 6, 72, 72);      

第四步:擷取字型的資訊

FT_Load_Glyph( face, FT_Get_Char_Index( face, code ), FT_LOAD_DEFAULT );
       
   
    FT_Glyph glyph;
    FT_Get_Glyph( face->glyph, &glyph );

    //Convert the glyph to a bitmap.
    FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 );
    FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;

    //This reference will make accessing the bitmap easier
    FT_Bitmap& bitmap   =   bitmap_glyph->bitmap;      

第五步:将bmp資料寫到紋理上

glTexSubImage2D(GL_TEXTURE_2D,0,x,y,width,height,GL_ALPHA,GL_UNSIGNED_BYTE,data);      

第六步:生成網格,貼上紋理繪制文字

RESULT  IGraphyDeviceImpl::draw2DText(const wchar_t* text,float x,float y,const Rgba4Byte& color,RectF* pOut)
{
    
    typedef float   TextVertex[5];
    TextVertex  vert[2048 * 6];
    
    float       texWidth    =   (float)_fontDefault._textures._width;
    float       texHeight   =   (float)_fontDefault._textures._height;
    float       xStart      =   x;
    float       yStart      =   y;
    float       zStart      =   0;
    unsigned    index       =   0;
    unsigned    nSize       =   wcslen(text);
    float       fHeight     =   0;
   
    for (unsigned i = 0 ;i <  nSize; ++ i )
    {
        Character*  ch  =   _fontDefault.getCharacter(text[i]);
       
        int         h   =   ch->y1 - ch->y0;
        int         w   =   ch->x1 - ch->x0;
        /**
        *   第一個點
        */
        vert[index + 0][0]  =   xStart;
        vert[index + 0][1]  =   yStart;
        vert[index + 0][2]  =   zStart;
        vert[index + 0][3]  =   ch->x0/texWidth;
        vert[index + 0][4]  =   ch->y0/texHeight;
        /**
        *   第二個點
        */
        vert[index + 1][0]  =   xStart;
        vert[index + 1][1]  =   yStart + h;
        vert[index + 1][2]  =   zStart;
        vert[index + 1][3]  =   ch->x0/texWidth;
        vert[index + 1][4]  =   ch->y1/texHeight;
        /**
        *   第二個點
        */
        vert[index + 2][0]  =   xStart + w;
        vert[index + 2][1]  =   yStart + h;
        vert[index + 2][2]  =   zStart;
        vert[index + 2][3]  =   ch->x1/texWidth;
        vert[index + 2][4]  =   ch->y1/texHeight;


        /**
        *   第二個三角形
        */
        vert[index + 3][0]  =   xStart;
        vert[index + 3][1]  =   yStart;
        vert[index + 3][2]  =   zStart;
        vert[index + 3][3]  =   ch->x0/texWidth;
        vert[index + 3][4]  =   ch->y0/texHeight;

        /**
        *   第二個點
        */
        vert[index + 4][0]  =   xStart + w;
        vert[index + 4][1]  =   yStart + h;
        vert[index + 4][2]  =   zStart;
        vert[index + 4][3]  =   ch->x1/texWidth;
        vert[index + 4][4]  =   ch->y1/texHeight;

        /**
        *   第二個點
        */
        vert[index + 5][0]  =   xStart + w;
        vert[index + 5][1]  =   yStart;
        vert[index + 5][2]  =   zStart;
        vert[index + 5][3]  =   ch->x1/texWidth;
        vert[index + 5][4]  =   ch->y0/texHeight;

        index   +=  6;
        xStart  +=  w;
    }
    glColor4ub(color._r,color._g,color._b,color._a);
    /**
    *   對字型使用到的紋理和定點坐标進行排序
    */
    bindTexture2D(_fontDefault._textures._texture);

    CELL::Graphy::VertexDeclaration fontDesc[]    =   
    {
        0,  CELL::Graphy::TYPE_VERTEX,   CELL::Graphy::FORMAT_FLOAT,     3,  0, sizeof(TextVertex),
        0,  CELL::Graphy::TYPE_TEXCOORD0,CELL::Graphy::FORMAT_FLOAT,     2,  12, sizeof(TextVertex)
    };

    drawPrimitiveDirect(fontDesc,2,PT_TRIANGLELIST,vert,0,index);      

上面簡單的介紹了如何使用FreeType與OpenGL繪制文字,筆者能力所緻,說解不到之處,有誤之處,敬請諒解,指教。

代碼下載下傳 

我的QQ13697826

繼續閱讀