第七章opengl文字显示

更新时间:2024-03-31 22:55:02 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

OpenGL文字显示

(一)显示英文

OpenGL并没有直接提供显示文字的功能,并且,OpenGL也没有自带专门的字库。因此,要显示文字,就必须依赖操作系统所提供的功能了。

各种流行的图形操作系统,例如Windows系统和Linux系统,都提供了一些功能,以便能够在OpenGL程序中方便的显示文字。

最常见的方法就是,我们给出一个字符,给出一个显示列表编号,然后操作系统由把绘制这个字符的OpenGL命令装到指定的显示列表中。当需要绘制字符的时候,我们只需要调用这个显示列表即可。假如我们要显示的文字全部是ASCII字符,则总共只有0到127这128种可能,因此可以预先把所有的字符分别装到对应的显示列表中,然后在需要时调用这些显示列表。

Windows系统中,可以使用wglUseFontBitmaps函数来批量的产生显示字符用的显示列表。函数有四个参数:

第一个参数是HDC,这是Windows GDI的里的东西,调用wglGetCurrentDC函数,就可以得到一个HDC了。

第二个参数表示第一个要产生的字符,因为我们要产生0到127的字符的显示列表,所以这里填0。

第三个参数表示要产生字符的总个数,因为我们要产生0到127的字符的显示列表,总共有128个字符,所以这里填128。

第四个参数表示第一个字符所对应显示列表的编号。假如这里填1000,则第一个字符的绘制命令将被装到第1000号显示列表,第二个字符的绘制命令将被装到第1001号显示列表,依次类推。我们可以先用glGenLists申请128个连续的显示列表编号,然后把第一个显示列表编号填在这里。

我们为了简化view中的代码,特建立字符显示类COpenGLFont(后面给出): #define MAX_CHAR 128

wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists); 在字符类中添加显示字符函数:

void COpenGLFont::drawString(const char* str) {

static int isFirstCall = 1; static GLuint lists;

if( isFirstCall ) { // 如果是第一次调用,执行初始化

// 为每一个ASCII字符产生一个显示列表 isFirstCall = 0;

// 申请MAX_CHAR个连续的显示列表编号

lists = glGenLists(MAX_CHAR); //编号分别是lists, lists + 1, lists + 2,

lists + MAX_CHAR -1

// 把每个字符的绘制命令都装到对应的显示列表中

wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists); //从基数

lists开始依次显示各个字符

}

// 调用每个字符对应的显示列表,绘制每个字符 for(; *str!='\\0'; ++str) }

显示列表一旦产生就一直存在(除非调用glDeleteLists销毁),所以我们只需要在第一次调用的时候初始化,以后就可以很方便的调用这些显示列表来绘制字符了。

绘制字符的时候,可以先用glColor*等指定颜色,然后用glRasterPos*指定位置,最后调用显示列表来绘制。 #include\

void CMFCOpenGLView::OnDraw(CDC* /*pDC*/) {

CMFCOpenGLDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc)

return;

glCallList(lists + *str);

// TODO: 在此处为本机数据添加绘制代码 glClearColor(0.0f,0.0f,0.7f,1.0f); //背景设置

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity();

gluLookAt(0,0,10,0,0, 0,0,1,0); glColor3f(1,0,0);

}

glRasterPos4s(-2,0,0,1); //指定位置 COpenGLFont font;

font.drawString(\glFinish();

SwapBuffers(wglGetCurrentDC());

指定字体:

在产生显示列表前,Windows允许选择字体。在COpenGLFont类中定义一个selectFont函数来实现它,大家可以看看代码。

void COpenGLFont::selectFont(int size, int charset, const char* face) { }

HFONT hFont =

CreateFontA(size, //指定字高 0, 0, 45,

//指定字宽 //指定角度(1/10度)

//指定字符的基线与x轴的角度(1/10度)

//指定字体的重量(FW_BOLD=700) //指定是否斜体 //指定是否有下划线 //指定是否是StrikeOut字体

FW_BOLD, 0, 0, 0, charset,

OUT_DEFAULT_PRECIS, //指定输出精度 CLIP_DEFAULT_PRECIS,//指定裁剪精度 DEFAULT_QUALITY, //指定输出质量

DEFAULT_PITCH | FF_SWISS, //指定字体的定位和外观 face);

HFONT hOldFont = (HFONT)SelectObject(wglGetCurrentDC(), hFont); DeleteObject(hOldFont);

在调用wglUseFontBitmaps之前使用selectFont函数即可指定字体。函数的三个参数分别表示了字体大小、字符集(英文字体ANSI_CHARSET,简体中文字体GB2312_CHARSET,繁体中文字体CHINESEBIG5_CHARSET ,对于中文的Windows系统,也可以直接用DEFAULT_CHARSET表示默认字符集)、字体名称。

(二)显示中文

原则上,显示中文和显示英文并无不同,同样是把要显示的字符做成显示列表,然后进行调用。但是有一个问题,英文字母很少,最多只有几百个,为每个字母创建一个显示列表,没有问题。但是汉字有非常多个,如果每个汉字都产生一个显示列表,这是不切实际的。我们不能在初始化时就为每个字符建立一个显示列表,那就只有在每次绘制字符时创建它了。当我们需要绘制一个字符时,创建对应的显示列表,等绘制完毕后,再将它销毁。

这里还经常涉及到中文乱码的问题,在网上流传的版本中,使用了MultiByteToWideChar这个函数的,基本上都没有出现乱码,所以我们也准备用这个函数。通常我们在C语言里面使用的字符串,如果中英文混合的话,例如“this is 中文字符.”,则英文字符只占用一个字节,而中文字符则占用两个字节。用MultiByteToWideChar函数,可以转化为所有的字符都占两个字节(同时解决了前面所说的乱码问题:))。 转化的代码如下: // 计算字符的个数

// 如果是双字节字符的(比如中文字符),两个字节才算一个字符 // 否则一个字节算一个字符 len = 0;

for(i=0; str[i]!='\\0'; ++i) {

if( IsDBCSLeadByte(str[i]) ) ++i; ++len; }

// 将混合字符转化为宽字符

wstring = (wchar_t*)malloc((len+1) * sizeof(wchar_t));

MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, wstring, len); wstring[len] = L'\\0';

// 用完后记得释放内存 free(wstring);

加上前面所讲到的wglUseFontBitmaps函数,即可显示中文字符了。在字体类中添加显示中文字符函数:

void COpenGLFont::drawCNString(const char* str) { int len, i;

wchar_t* wstring;

HDC hDC = wglGetCurrentDC(); GLuint list = glGenLists(1);

// 计算字符的个数

// 如果是双字节字符的(比如中文字符),两个字节才算一个字符 // 否则一个字节算一个字符 len = 0;

for(i=0; str[i]!='\\0'; ++i) { if( IsDBCSLeadByte(str[i]) ) ++i; ++len; }

// 将混合字符转化为宽字符

wstring = (wchar_t*)malloc((len+1) * sizeof(wchar_t));

MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, wstring, len); wstring[len] = L'\\0'; // 逐个输出字符 for(i=0; i

// 回收所有临时资源 free(wstring);

glDeleteLists(list, 1); }

注意我用了wglUseFontBitmapsW函数,而不是wglUseFontBitmaps。wglUseFontBitmapsW是wglUseFontBitmaps函数的宽字符版本,它认为字符都占两个字节。因为这里使用了MultiByteToWideChar,每个字符其实是占两个字节的,所以应该用wglUseFontBitmapsW。 void CMFCOpenGLView::OnDraw(CDC* /*pDC*/) {

CMFCOpenGLDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc)

return;

// TODO: 在此处为本机数据添加绘制代码 glClearColor(0.0f,0.0f,0.7f,1.0f); //背景设置

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity();

gluLookAt(0,0,10,0,0, 0,0,1,0); glColor3f(1,0,0);

glRasterPos4s(-5,0,0,1); //指定位置 COpenGLFont font;

font.selectFont(48,DEFAULT_CHARSET, \黑体\ }

font.drawCNString(\汉字显示测试!chinese character show test!12345\glFinish();

SwapBuffers(wglGetCurrentDC());

(三)三维汉字的显示

在OpenGL中输出文本有两个函数:wglUseFontBitmaps和wglUseFontOutlines,前者用来输出2维文字,后者用来输出3维文字, 项目需要采用unicode编码方式. void COpenGLFont::draw3DString(_TCHAR* str) {

GLYPHMETRICSFLOAT pgmf[1]; DWORD dwChar; int list;

HDC hDC=wglGetCurrentDC();

}

for(size_t i=0;i<_tcslen(str);i++) { dwChar=str[i]; list=glGenLists(1); //取出一个字符的显示列表 wglUseFontOutlines(hDC,//设备环境句柄 dwChar,//要转换为显示列表的第一个字符 1, //要转换为显示列表的字符数 list,//显示列表的基数 0.0,//指定与实际轮廓的最大偏移量,显示精度 0.5,//在Z轴负方向的值,即厚度 0,//填充模式,为填充,为线框 NULL/*pgmf*/); //接受字符的地址 //绘制该字符的显示列表 glCallList(list); glDeleteLists(list, 1); }

void CMFCOpenGLView::OnDraw(CDC* /*pDC*/) {

CMFCOpenGLDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return;

// TODO: 在此处为本机数据添加绘制代码 glClearColor(0.0f,0.0f,0.7f,1.0f); //背景设置

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity();

gluLookAt(0,0,10,0,0,0,0,1,0); mydraw();//完成图形绘制 glColor3f(1,0,0);

glTranslatef(-3,2,-2); glScalef(1,2,1);

COpenGLFont font;

font.selectFont(10,DEFAULT_CHARSET, \宋体\ font.draw3DString(L\江西农业大学软件学院!\ glFinish();

SwapBuffers(wglGetCurrentDC()); }

本文来源:https://www.bwwdw.com/article/v1mr.html

Top