zoukankan      html  css  js  c++  java
  • OpenGL11-绘制汉字最高效方法(使用Freetype)(代码已更新)

    最新版本,之前的版本有些文件没有打包 

    视频教程请关注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440

     

    OpenGL本身并没有绘制文字的功能,他只是一个三维绘图的API集和,很多东西都要

    自己动手才可以实现。OpenGL绘制文字,网络上已经有很多成熟的方式方法,我这里给

    大家介绍的是我使用的方式,从绘制的效率上来说,速度上从已经达到我个人水平的最大值。

    如果你有更好的方式,请联系我。

      首先介绍下网络上的一些绘制方式。

      一、将要绘制的文字按照每一个字生成一个小纹理的方式,然后再用将纹理贴到网格

    的表面,绘制出来,例如:“博客园-你好”,则会生成6个小纹理,然后生成网格,将纹理

    贴到网格的表面。优点:每一个字的大小颜色都可选择。缺点:文字多了以后,频繁的切换

    纹理造成效率低下。OSG中使用了这种方式,效率极差,尤其是在文字更新的情况下。

      二、直接将随绘制的文本字符串生成一个纹理数据(而不是一个文字一个纹理),这样

    做效率上比第一种要好很多,缺点就是更新的时候要重新构建一个新的纹理。速度上有很大

    损失。两种方式的原理图如下:

      三、将所绘制的文字都放到一个较大的纹理上去,然后再纹理上做索引,当绘制的时候,

    去查表。在将纹理贴到网格上绘制出来,这种方式,速度很快,很多游戏引擎都在使用这种

    方式,存在的问题绘制的文字多了以后速度会变慢,占用大量的cpu时间,当然对于小的应

    用已经足够了,今天我提出的方式也是基于这样方式的,只是我在索引上最了一些改进,改

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

    效率。纹理图片如下示意图:

      

    图片存的数据上也很重要,结合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绘制文字,笔者能力所致,说解不到之处,有误之处,敬请谅解,指教。

    代码下载 

     

  • 相关阅读:
    Linq to OBJECT延时标准查询操作符
    LINQ to XML
    动态Linq(结合反射)
    HDU 1242 dFS 找目标最短路
    HDu1241 DFS搜索
    hdu 1224 最长路
    BOJ 2773 第K个与m互质的数
    ZOJ 2562 反素数
    2016 ccpc 杭州赛区的总结
    bfs UESTC 381 Knight and Rook
  • 原文地址:https://www.cnblogs.com/zhanglitong/p/3206497.html
Copyright © 2011-2022 走看看