最新版本,之前的版本有些檔案沒有打包
視訊教程請關注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CM2ETZ4MTNkNWMkZDZ0UWY5IWZ0ATM3MmZjBjY5gTOy0yMzATMzEzMy8CX3AzMxAjMvwlN5YzN0UzLcd2bsJ2Lc12bj5ycn9Gbi52YuAzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
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繪制文字,筆者能力所緻,說解不到之處,有誤之處,敬請諒解,指教。
代碼下載下傳
我的QQ13697826