转自【翻译】NeHe OpenGL 教程

前言

声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改。对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢。

NeHe OpenGL第四十三课:FreeType库

在OpenGL中使用FreeType库

使用FreeType库可以创建非常好看的反走样的字体,记住暴雪公司就是使用这个库的,就是那个做魔兽世界的。尝试一下吧,我只告诉你了基本的使用方式,你可以走的更远。

 

在OpenGL中使用FreeType库

这里是一个快速的介绍,它告诉你如何在OpenGL中使用FreeType渲染TrueType字体。使用这个库我们可以渲染反走样的文本,它看起来更加的漂亮。

动机

这里我将给你两个例子,一个是用WGL的bitmap字体渲染得文字,另一个是用FreeType渲染得文字。

 

使用WGl渲染得文字是一些图像,当你放大它们时看起来如下:



如果你使用GNU的FreeType库(暴雪公司也在它们的游戏中使用这个库),它将看起来更漂亮,如下所示,它具有了反走样:



创建程序

第一步你需要从下面的网站上下载FreeType库:http://gnuwin32.sourceforge.net/packages/freetype.htm

接着在你使用它创建一个新的程序时,你需要链接libfreetype.lib库,并包含FreeType的头文件。

现在我们已经能创建基于FreeType的程序了,但我们还不能运行它,因为我们需要freetype-6.dll文件。

好了,现在我们可以开始编写我们的程序了,我们从13课的代码开始,我们添加两个新的文件"freetype.cpp"和"freetype.h"。我们把和FreeType相关的内容放在这两个文件里。

好了,让我们从freetype.h开始吧。

按惯例我们包含一些需要的头文件

 

#ifndef FREE_NEHE_H#define FREE_NEHE_H

//FreeType 头文件

#include <ft2build.h>

#include <freetype/freetype.h>

#include <freetype/ftglyph.h>

#include <freetype/ftoutln.h>

#include <freetype/fttrigon.h>

//OpenGL 头文件

#include <windows.h>

#include <GL/gl.h>

#include <GL/glu.h>

//STL 头文件

#include <vector>

#include <string>

//STL异常类

#include <stdexcept>

#pragma warning(disable: 4786)

我们将把每个字符需要的信息封装在一个结构中,这样就像使用WGL字体一样,我们可以分别控制每个字符的显示状态。 

  

// 把所有的操作放在名字空间freetype中,这样可以避免与其他函数的冲突namespace freetype

{

// 使用vector和string名字空间

using std::vector;

using std::string;

// 这个结构保存字体信息

struct font_data

{

float h; // 字体的高度

GLuint * textures; // 使用的纹理

GLuint list_base; // 显示列表的值

// 初始化结构

void init(const char * fname, unsigned int h);

// 清楚所有的资源

void clean();

};

最后一件事是定义我们输出字符串的原形: 

  

// 把字符输出到屏幕void print(const font_data &ft_font, float x, float y, const char *fmt, ...);

}

#endif

上面就是FreeType的头文件,下面我们看看怎样实现它 

  

#include "freetype.h"

namespace freetype {

我们使用纹理去显示字符,在OpenGL中纹理大小必须为2的次方,这个函数用来字符的大小近似到这个值。所以我们有了如下的方程: 

  

// 这个函数返回比a大的,并且是最接近a的2的次方的数inline int next_p2 (int a ){ int rval=1; //
rval<<=1 Is A Prettier Way Of Writing rval*=2;  while(rval<a)
rval<<=1; return rval;}

下面一个函数为make_dlist, 它是这个代码的核心。它包含FT_Face对象,它是FreeType用来保存字体信息的类,接着创建一个显示列表。 

  

// 为给定的字符创建一个显示列表void make_dlist ( FT_Face face, char ch, GLuint list_base, GLuint * tex_base ) {

// 载入给定字符的轮廓

if(FT_Load_Glyph( face, FT_Get_Char_Index( face, ch ), FT_LOAD_DEFAULT ))

throw std::runtime_error("FT_Load_Glyph failed");

// 保存轮廓对象

FT_Glyph glyph;

if(FT_Get_Glyph( face->glyph, &glyph ))

throw std::runtime_error("FT_Get_Glyph failed");

// 把轮廓转化为位图

FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 );

FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;

// 保存位图

FT_Bitmap& bitmap=bitmap_glyph->bitmap;

}

现在我们已经从FreeType中获得了位图,我们需要把它转化为一个满足OpenGL纹理要求的位图。你必须知道,在OpenGL中位图表示黑白的数据,而在FreeType中我们使用8位的颜色表示位图,所以FreeType的位图可以保存亮度信息。 

  

// 转化为OpenGl可以使用的大小 int width = next_p2( bitmap.width ); int height = next_p2( bitmap.rows );

// 保存位图数据

GLubyte* expanded_data = new GLubyte[ 2 * width * height];

// 这里我们使用8位表示亮度8位表示alpha值

for(int j=0; j <height;j++) {

for(int i=0; i < width; i++){

expanded_data[2*(i+j*width)]= expanded_data[2*(i+j*width)+1] =

(i>=bitmap.width || j>=bitmap.rows) ?

0 : bitmap.buffer[i + bitmap.width*j];

}

}

接下来我们选则字体纹理,并生成字体的贴图纹理 

  

// 设置字体纹理的纹理过滤器 glBindTexture( GL_TEXTURE_2D,
tex_base[ch]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

// 邦定纹理

glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,

GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data );

// 释放分配的内存

delete [] expanded_data;

接着创建一个显示列表,它用来绘制一个字符 

  

// 创建显示列表 glNewList(list_base+ch,GL_COMPILE);

glBindTexture(GL_TEXTURE_2D,tex_base[ch]);

//首先我们向左移动一点

glTranslatef(bitmap_glyph->left,0,0);

//接着我们向下移动一点,这只队'g','y'之类的字符有用

//它使得所有的字符都有一个基线

glPushMatrix();

glTranslatef(0,bitmap_glyph->top-bitmap.rows,0);

// 计算位图中字符图像的宽度

float x=(float)bitmap.width / (float)width,

y=(float)bitmap.rows / (float)height;

//绘制一个正方形,显示字符

glBegin(GL_QUADS);

glTexCoord2d(0,0); glVertex2f(0,bitmap.rows);

glTexCoord2d(0,y); glVertex2f(0,0);

glTexCoord2d(x,y); glVertex2f(bitmap.width,0);

glTexCoord2d(x,0); glVertex2f(bitmap.width,bitmap.rows);

glEnd();

glPopMatrix();

glTranslatef(face->glyph->advance.x >> 6 ,0,0);

//结束显示列表的绘制

glEndList();

}

下面的函数将使用make_dlist创建一个字符集的显示列表,fname为你要使用的FreeType字符文件。 

  

void font_data::init(const char * fname, unsigned int h) {  // 保存纹理ID. textures = new GLuint[128];

this->h=h;

// 创建FreeType库

FT_Library library;

if (FT_Init_FreeType( &library ))

throw std::runtime_error("FT_Init_FreeType failed");

// 在FreeType库中保存字体信息的类叫做face

FT_Face face;

// 使用你输入的Freetype字符文件初始化face类

if (FT_New_Face( library, fname, 0, &face ))

throw std::runtime_error("FT_New_Face failed (there is probably a problem with your font file)");

// 在FreeType中使用1/64作为一个像素的高度所以我们需要缩放h来满足这个要求

FT_Set_Char_Size( face, h << 6, h << 6, 96, 96);

// 创建128个显示列表

list_base=glGenLists(128);

glGenTextures( 128, textures );

make_dlist(face,i,list_base,textures);

// 释放face类

FT_Done_Face(face);

// 释放FreeType库

FT_Done_FreeType(library);

}

下面的函数完成释放资源的工作 

  

void font_data::clean() { glDeleteLists(list_base,128); glDeleteTextures(128,textures); delete [] textures;}

在print函数中要用到下面的两个方程,pushScreenCoordinateMatrix函数用来保存当前的矩阵,并设置视口与当前的窗口大小匹配。pop_projection_matrix函数用来返回pushScreenCoordinateMatrix保存的矩阵。reference
manual.  

  

// 保存当前的矩阵,并设置视口与当前的窗口大小匹配inline void pushScreenCoordinateMatrix()
{ glPushAttrib(GL_TRANSFORM_BIT); GLint  
viewport[4]; glGetIntegerv(GL_VIEWPORT,
viewport); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3]); glPopAttrib();}

//返回pushScreenCoordinateMatrix保存的矩阵

inline void pop_projection_matrix() {

glPushAttrib(GL_TRANSFORM_BIT);

glMatrixMode(GL_PROJECTION);

glPopMatrix();

glPopAttrib();

我们的print函数和13课的函数非常的像,但在实现上有一些不同。我们实际上是使用2通道的纹理而不是图像。 

  

// 输出文字void print(const font_data &ft_font, float x, float y, const
char *fmt, ...)  {          //
保存当前矩阵 pushScreenCoordinateMatrix();                                          
 GLuint font=ft_font.list_base; float
h=ft_font.h/.63f;                                                
 char text[256];          va_list ap;          

if (fmt == NULL)

*text=0;

else {

va_start(ap, fmt);

vsprintf(text, fmt, ap);

va_end(ap);

}

// 把输入的字符串按回车分割

const char *start_line=text;

vector<string> lines;

for(const char *c=text;*c;c++) {

if(*c=='\n') {

string line;

for(const char *n=start_line;n<c;n++) line.append(1,*n);

lines.push_back(line);

start_line=c+1;

}

}

if(start_line) {

string line;

for(const char *n=start_line;n<c;n++) line.append(1,*n);

lines.push_back(line);

}

glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TRANSFORM_BIT);

glMatrixMode(GL_MODELVIEW);

glDisable(GL_LIGHTING);

glEnable(GL_TEXTURE_2D);

glDisable(GL_DEPTH_TEST);

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glListBase(font);

float modelview_matrix[16];      glGetFloatv(GL_MODELVIEW_MATRIX, modelview_matrix);

// 下面的代码完成具体的绘制过程

for(int i=0;i<lines.size();i++) {

glPushMatrix();

glLoadIdentity();

glTranslatef(x,y-h*i,0);

glMultMatrixf(modelview_matrix);

//调用显示列表绘制

glCallLists(lines[i].length(), GL_UNSIGNED_BYTE, lines[i].c_str());

glPopMatrix();

}

glPopAttrib();

pop_projection_matrix();

}

}

}

FreeType库我们就写好了,现我们在13课的代码上来做一些修改,当然首先我们需要包含freetype.h的头文件 

  

#include "freetype.h"

  

现在我们就可以调用freetype库绘制字符串了

// 保存我们创建的字体的信息freetype::font_data our_font;

  

接下来使用test.ttf文件初始化字体 

  

our_font.init("Test.ttf", 16);

  

在程序结束时记得释放内存资源 

  

our_font.clean();

  

下面是我们具体的绘制函数 

  

int DrawGLScene(GLvoid)          { glClear(GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT);      glLoadIdentity();          glTranslatef(0.0f,0.0f,-1.0f);       

// 蓝色文字

glColor3ub(0,0,0xff);

// 绘制WGL文字

glRasterPos2f(-0.40f, 0.35f);

glPrint("Active WGL Bitmap Text With NeHe - %7.2f", cnt1);

// 红色文字

glColor3ub(0xff,0,0);

glPushMatrix();

glLoadIdentity();

glRotatef(cnt1,0,0,1);

glScalef(1,.8+.3*cos(cnt1/5),1);

glTranslatef(-180,0,0);

//绘制freetype文字

freetype::print(our_font, 320, 200, "Active FreeType Text - %7.2f", cnt1);

glPopMatrix();

cnt1+=0.051f;

cnt2+=0.005f;

return TRUE; // 成功返回

}

原文及其个版本源代码下载:

http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=43

 
 

NeHe OpenGL教程 第四十三课:FreeType库的更多相关文章

  1. NeHe OpenGL教程 第四十七课:CG顶点脚本

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  2. NeHe OpenGL教程 第四十课:绳子的模拟

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  3. NeHe OpenGL教程 第三十三课:TGA文件

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  4. NeHe OpenGL教程 第四十八课:轨迹球

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  5. NeHe OpenGL教程 第四十五课:顶点缓存

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  6. NeHe OpenGL教程 第四十六课:全屏反走样

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  7. NeHe OpenGL教程 第四十四课:3D光晕

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. NeHe OpenGL教程 第四十二课:多重视口

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  9. NeHe OpenGL教程 第四课:旋转

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

随机推荐

  1. JSBinding + SharpKit / JavaScript 加载流程

    首先,现在的方案是游戏启动就加载全部的 JavaScript 代码. 先看下 StreamingAssets/JavaScript/ 文件夹下的目录结构:

  2. Unity摄像机

    把相机做为人物的子对象,就可以制作: 1.第1人称摄像机:把摄像机摆在眼睛前面 2.第3人称摄像机:把摄像机摆在人后上面 Clear Flags: http://www.haogongju.net/a ...

  3. 递归遍历多维数组(树数据结构)的超级简单方式,并且可以递归超过200层,摘自<<PHP精粹:编写高效PHP代码>>

    <?php $array = array( "Hello", // Level 1 array( "World" // Level 2 ), array( ...

  4. 内存泄漏,当您使用的 GetDC 方法和 ReleaseDC 方法 CWnd 类版本

    重现行为的步骤 是从 CWnd 派生的类的一个方法中插入下面的代码在您的应用程序中: CDC *pDC; RECT rect; GetClientRect (&rect); for (int ...

  5. unity3d中Find的用法

    在unity3d中用Find可以直接查找组件 例子一: 该脚本时绑在main Camera上的,"/Scene/player"这是在Hierarchy目录下直接找寻Scene   ...

  6. 轮播图切换 纯html+js+css

    如图所示. 该图片切换特效实现很简单,而且兼容性很好. html页面如下 复制代码代码如下: <div class="wrapper"> <div id=&quo ...

  7. Xcode 7 ImageNamed 方法加载jpg图片失败

    更新XCode7后 原来的Image.xcassets文件夹变成了Assets.xcassets 把01.jpg,02.jpg,03.png拖入这个文件夹中 UIImage* test1=[UIIma ...

  8. Top JavaScript Frameworks, Libraries & Tools and When to Use Them

    It seems almost every other week there is a new JavaScript library taking the web community by storm ...

  9. Why we made vorlon.js and how to use it to debug your JavaScript remotely

    Vorlon.js is powered by node.JS, socket.io, and late-night coffee. I would like to share with you wh ...

  10. IE7中line-height垂直居中问题

    line-height:24px; *+line-height:24px; //针对ie7 height:24px