視頻教程請關注 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繪制文字,筆者能力所致,說解不到之處,有誤之處,敬請諒解,指教。