1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > Qt OpenGL 位图字体

Qt OpenGL 位图字体

时间:2020-09-25 06:36:19

相关推荐

Qt OpenGL 位图字体

这次教程中,我们将创建一些基于2D图像的字体,它们可以缩放平移,但不能旋转,并且总是面向前方,但作为基本的显示来说,我想已经足够了。

或者对于这次教程,你会觉得“在屏幕上显示文字没什么难的”,但是你真正尝试过就会知道,它确实没那么容易。你当然可以把文字写在一个图片上,再把这幅图片载入你的OpenGL程序中,打开混合选项,从而在屏幕上显示出文字。但这种做法非常耗时,而且经常图像会显得模糊。另外,除非你的图像包含一个Alpha通道,否则一旦绘制在屏幕上,那些文字就会不透明(与屏幕中的其他物体混合)。

使用位图字体比起使用图形字体(贴图)看起来不止强100倍,你可以随时改变显示在屏幕上的文字,而且用不着为它们逐个制作贴图。只需要将文字定位,再调用我们即将构建的glPrint()函数就可以在屏幕上显示文字了。

程序运行时效果如下:

下面进入教程:

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

我们这次将在第01课的基础上修改代码,我会对新增代码一一解释,希望大家能掌握,首先打开myglwidget.h文件,将类声明更改如下:

1 #ifndef MYGLWIDGET_H2 #define MYGLWIDGET_H3 4 #include <QWidget>5 #include <QGLWidget>6 7 class MyGLWidget : public QGLWidget8 {9Q_OBJECT10 public:11explicit MyGLWidget(QWidget *parent = 0);12~MyGLWidget();13 14 protected:15//对3个纯虚函数的重定义16void initializeGL();17void resizeGL(int w, int h);18void paintGL();19 20void keyPressEvent(QKeyEvent *event); //处理键盘按下事件21 22 private:23void buildFont(); //创建字体24void killFont(); //删除显示列表25void glPrint(const char *fmt, ...); //输出字符串26 27 private:28bool fullscreen; //是否全屏显示29HDC m_HDC; //储存当前设备的指针30 31int m_FontSize; //控制字体的大小32GLuint m_Base; //储存绘制字体的显示列表的开始位置33GLfloat m_Cnt1; //字体移动计数器134GLfloat m_Cnt2; //字体移动计数器235 };36 37 #endif // MYGLWIDGET_H

CSDN QT技术栈大纲:Qt开发必备技术栈学习路线和资料

我们新增了几个变量,第一个变量m_HDC是用来储存当前设备绘制信息的一种windows数据结构,我们将会把我们自己创建的字体绑定到m_HDC上去,这样我们绘制文字时就自动采用绑定的字体了。后面几个变量的作用依次是控制字体大小、储存绘制字体的显示列表的开始位置、字体移动计数,具体的 会在后面讲。

另外我们增加了三个函数,分别用于创建字体、删除显示列表、输出特定的字符串,当然最后一个glPrint()函数在前面已经提到,是个很重要的函数。

接下来,我们需要打开myglwidget.cpp,加上声明#include <QTimer>、#include <QtMath>,将构造函数和析构函数修改一下,具体代码如下:

1 MyGLWidget::MyGLWidget(QWidget *parent) :2QGLWidget(parent)3 {4fullscreen = false;5m_FontSize = -18;6m_Cnt1 = 0.0f;7m_Cnt2 = 0.0f;8 9HWND hWND = (HWND)winId();//获取当前窗口句柄10m_HDC = GetDC(hWND); //通过窗口句柄获得HDC11 12QTimer *timer = new QTimer(this); //创建一个定时器13//将定时器的计时信号与updateGL()绑定14connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));15timer->start(10);//以10ms为一个计时周期16 }

1 MyGLWidget::~MyGLWidget()2 {3killFont(); //删除显示列表4 }

几个普通变量的初始化我不作解释了,我们重点看m_HDC的初始化。我们要如何获得当前窗口的HDC呢?方法是我们先得到当前窗口的句柄(HWND),通过调用函数GetCD(HWND)可以获得HDC。那如何获得HWND呢?Qt中有一个winId()函数可以返回当前窗口的Id(类型为WId),我们把它强制转换为HWND类型就可以了,这样我们就可以初始化关键的m_HDC。

注意一下析构函数,在退出程序之前,我们应该确保我们分配的用于存放显示列表的空间被释放,所以我们在析构函数处调用killFont()函数删除显示列表(具体实现看下面)。

继续,我们要来定义我们新增的三个函数了,这可是重头戏,具体代码如下:

1 void MyGLWidget::buildFont() //创建位图字体2 {3HFONT font; //字体句柄4m_Base = glGenLists(96); //创建96个显示列表56font = CreateFont(m_FontSize, //字体高度7 0, //字体宽度8 0, //字体的旋转角度9 0, //字体底线的旋转角度10 FW_BOLD,//字体的重量11 FALSE, //是否斜体12 FALSE, //是否使用下划线13 FALSE, //是否使用删除线14 ANSI_CHARSET, //设置字符集15 OUT_TT_PRECIS,//输出精度16 CLIP_DEFAULT_PRECIS, //剪裁精度17 ANTIALIASED_QUALITY, //输出质量18 FF_DONTCARE | DEFAULT_PITCH,//Family and Pitch的设置19 LPCWSTR("Courier New"));//字体名称(电脑中已装的)20 21wglUseFontBitmaps(m_HDC, 32, 96, m_Base); //创建96个显示列表,绘制ASCII码为32-128的字符22SelectObject(m_HDC, font);//选择字体23 }

1 void MyGLWidget::killFont() //删除显示列表2 {3glDeleteLists(m_Base, 96);//删除96个显示列表4 }

1 void MyGLWidget::glPrint(const char *fmt, ...)//自定义输出文字函数2 {3char text[256]; //保存字符串4va_list ap; //指向一个变量列表的指针5 6if (fmt == NULL)//如果无输入则返回7{8 return;9}10 11va_start(ap, fmt); //分析可变参数12 vsprintf(text, fmt, ap); //把参数值写入字符串13va_end(ap); //结束分析14 15glPushAttrib(GL_LIST_BIT);//把显示列表属性压入属性堆栈16glListBase(m_Base - 32); //设置显示列表的基础值17glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); //调用显示列表绘制字符串18glPopAttrib(); //弹出属性堆栈19 }

首先是buildFont()函数 。我们先定义了字体句柄变量(HFONT),用来存放我们将要创建和使用的字体。接着我们在定义m_Base的同时使用glGenLists(96)创 建了一组共96个显示列表。然后我们调用Windows的API函数CreateFont()来创建我们自己的字体,前13个参数的意义大家请参考注释,我觉得没必要一个个解释了(有兴趣了解CreateFont各个参数 请点击此处 ),最后一个参数是字体类型,我们可以使用我们电脑已安装的任何字体,在Windows\Fonts目录可查看电脑已安装的字体。

然后我们从ASCII码第32个字符(空格)开始建立96个显示列表。如果你愿意,也可以建立所有256个字符,只要确保使用glGenLists建立256个显示列表就可以了。最后我们将font对象指针选入HDC,如此就完成了字体的创建及绑定。

然后是killFont()函数。它很简单,就是调用glDeleteLists()函数从m_Base开始删除96个显示列表。

最后是glPrint()函数。首先第一行我们创建一个大小为256个字符的字符数组,将用来保存我们要输出的字符串。第二行我们创建了一个指向一个变量列表的指针,我们在传递字符串的同时也传递了这个变量列表。然后是排除字符串为空的情况。接着的三行代码将文字中的所有符号转换为它们的字符编号,最终文字和转换的符号被储存在字符串text中。然后我们将GL_LIST_BIT压入属性堆栈,它会防止glListBase影响到我们的程序中的其它显示列表。

glListBase(m_Base-32)是告诉OpenGL去哪找对应字符的显示列表,由于每个字符对应一个显示列表,通过m_Base设置一个起点,OpenGL就知道到哪去找到正确的显示列表。减去32是因为我们没有构造前32个显示列表,那么久跳过它们就好了。于是,我们不得不通过从m_Base的值减去32来让OpenGL知道这一点。

现在OpenGL知道字母的存放位置了,我们就可以让它在屏幕上显示文字了。glCallLists()函数能同时将多个显示列表的内容显示在屏幕上,第一个参数是要显示在屏幕上的字符串长度,第二个参数告诉OpenGL将字符串当作一个无符号数组处理,它们的值都介于0到255之间,第三个参数通过传递text来告诉OpenGL显示的具体内容。最后,我们将GL_LIST_BIT属性弹出堆栈,恢复到我们使用glListBase(m_Base-32)之前的状态。

也许你想知道为什么字符不会彼此重叠堆积在一起。那是因为每个字符的显示列表都知道字符的右边缘在哪里,在写完一个字符后,OpenGL自动移动到刚刚写过的字符的右边,再写下一个字或画下一个物体时就会从最后的位置开始,也就是最后一个字符的右边。

然后我们修改一下initializeGL()函数,不作解释,代码如下:

1 void MyGLWidget::initializeGL()//此处开始对OpenGL进行所以设置2 {3glClearColor(0.0, 0.0, 0.0, 0.0); //黑色背景4glShadeModel(GL_SMOOTH); //启用阴影平滑5glClearDepth(1.0); //设置深度缓存6glEnable(GL_DEPTH_TEST); //启用深度测试7glDepthFunc(GL_LEQUAL); //所作深度测试的类型8glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //告诉系统对透视进行修正9 10buildFont();//创建字体11 }

还有,我们该进入paintGL()函数了,很简单,难的都过去了,具体代码如下:

1 void MyGLWidget::paintGL()//从这里开始进行所以的绘制2 {3glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存4glLoadIdentity();//重置当前的模型观察矩阵5 6glTranslatef(0.0f, 0.0f, -10.0f); //移入屏幕10.0单位7//根据字体位置设置颜色8glColor3f(1.0f*float(cos(m_Cnt1)), 1.0f*float(sin(m_Cnt2)),91.0f-0.5f*float(cos(m_Cnt1+m_Cnt2)));10//设置光栅化位置,即字体位置11glRasterPos2f(-4.5f+0.5f*float(cos(m_Cnt1)), 1.92f*float(sin(m_Cnt2)));12//输出文字到屏幕上13glPrint("Active OpenGL Text With NeHe - %7.2f", m_Cnt1);14m_Cnt1 += 0.051f;//增加两个计数器的值15m_Cnt2 += 0.005f;16 }

值得注意的是,深入屏幕并不能缩小字体,只会给字体变化移动范围(这一点大家自己改改数据就知道了)。然后字体颜色设置和位置设置我觉得没必要解释了,都是数学的东西,我们主要是为了得到一个变化的效果,并不在乎它是怎么实现的。然后就是调用glPrint()函数输出文字,最后增加两个计数器的值就OK了。

最后就是键盘控制的代码了,大家自己看吧,很简单,具体代码如下:

1 void MyGLWidget::keyPressEvent(QKeyEvent *event)2 {3switch (event->key())4{5case Qt::Key_F1://F1为全屏和普通屏的切换键6 fullscreen = !fullscreen;7 if (fullscreen)8 {9 showFullScreen();10 }11 else12 {13 showNormal();14 }15 updateGL();16 break;17case Qt::Key_Escape: //ESC为退出键18 close();19 break;20case Qt::Key_PageUp: //PageUp按下字体缩小21 m_FontSize -= 1;22 if (m_FontSize < -75)23 {24 m_FontSize = -75;25 }26 buildFont();27 break;28case Qt::Key_PageDown://PageDown按下字体放大29 m_FontSize += 1;30 if (m_FontSize > -5)31 {32 m_FontSize = -5;33 }34 buildFont();35 break;36}37 }

现在就可以运行程序查看效果了!

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。