【Qt OpenGL教程】14:轮廓字体
来源:互联网 发布:js deviceorientation 编辑:程序博客网 时间:2024/05/16 09:20
第14课:轮廓字体 (参照NeHe)
这次教程中,我将教大家绘制3D的轮廓字体,当然肯定不是贴图方式了,它们可像一般的3D模型一样进行旋转,放缩。
创建轮廓字体的方法与13课位图的位图字体类似,但轮廓字体要酷得多!轮廓字体可以在屏幕中以3D方式旋转,而且轮廓字体还可以有一定的厚度,而不再是平面的2D字符了。使用轮廓字体,我们可以将计算机中的任何字体转换为OpenGL的3D字体,是不是听起来很诱人呢?
程序运行时效果如下:
下面进入教程:
我们这次将在第13课的基础上修改代码,由于有13课代码的基础,这节课会简单许多。我只解释新增的代码,首先打开myglwidget.h文件,将类声明更改如下:
#ifndef MYGLWIDGET_H#define MYGLWIDGET_H#include <QWidget>#include <QGLWidget>class MyGLWidget : public QGLWidget{ Q_OBJECTpublic: explicit MyGLWidget(QWidget *parent = 0); ~MyGLWidget();protected: //对3个纯虚函数的重定义 void initializeGL(); void resizeGL(int w, int h); void paintGL(); void keyPressEvent(QKeyEvent *event); //处理键盘按下事件private: void buildFont(); //创建字体 void killFont(); //删除显示列表 void glPrint(const char *fmt, ...); //输出字符串private: bool fullscreen; //是否全屏显示 HDC m_HDC; //储存当前设备的指针 GLYPHMETRICSFLOAT m_Gmf[256]; //记录256个字符的信息 GLfloat m_Deep; //移入屏幕的距离 GLuint m_Base; //储存绘制字体的显示列表的开始位置 GLfloat m_Rot; //用于旋转字体};#endif // MYGLWIDGET_H由于我们没有准备让轮廓字体移动,所以删掉两个计数器。接着增加m_Deep来控制移入屏幕的距离,其实就是来控制字体的放大缩小的。然后再增加m_Rot来控制字体的旋转。最后增加了GLYPHMETRICSFLOAT变量数组m_Gmf[256]用来保存256个轮廓字体显示列表中对应的每一个列表的位置和方向信息,我们通过m_Gmf[num]来选择字母。要注意的是,每个字符的宽度可以不相同,使用GLYPHMETRICS会大大简化我们的工作。
接下来,我们需要打开myglwidget.cpp,先修改构造函数,不多解析了,具体代码如下:
MyGLWidget::MyGLWidget(QWidget *parent) : QGLWidget(parent){ fullscreen = false; m_Deep = -10.0f; m_Rot = 0.0f; HWND hWND = (HWND)winId(); //获取当前窗口句柄 m_HDC = GetDC(hWND); //通过窗口句柄获得HDC QTimer *timer = new QTimer(this); //创建一个定时器 //将定时器的计时信号与updateGL()绑定 connect(timer, SIGNAL(timeout()), this, SLOT(updateGL())); timer->start(10); //以10ms为一个计时周期}
继续,我们需要对buildFont()、killFont()、glPrint()三个函数作一定的修改,具体代码如下:
void MyGLWidget::buildFont() //创建轮廓字体{ HFONT font; //字体句柄 m_Base = glGenLists(256); //创建256个显示列表 font = CreateFont(-18, //字体高度 0, //字体宽度 0, //字体的旋转角度 0, //字体底线的旋转角度 FW_BOLD, //字体的重量 FALSE, //是否斜体 FALSE, //是否使用下划线 FALSE, //是否使用删除线 ANSI_CHARSET, //设置字符集 OUT_TT_PRECIS, //输出精度 CLIP_DEFAULT_PRECIS, //剪裁精度 ANTIALIASED_QUALITY, //输出质量 FF_DONTCARE | DEFAULT_PITCH, //Family and Pitch的设置 LPCWSTR("Comic Sans MS")); //字体名称(电脑中已装的) SelectObject(m_HDC, font); //选择字体 wglUseFontOutlines(m_HDC, //当前HDC 0, //从ASCII码第一个字符开始 255, //字符数 m_Base, //第一个显示列表的名称 0.0f, //字体光滑度,越小越光滑 0.2f, //在z方向突出的距离(字体的厚度) WGL_FONT_POLYGONS, //使用多边形来生成字符,每个顶点具有独立法线 m_Gmf); //用于储存字形度量数据(高度,宽度等)}
void MyGLWidget::killFont() //删除显示列表{ glDeleteLists(m_Base, 256); //删除96个显示列表}
void MyGLWidget::glPrint(const char *fmt, ...) //自定义输出文字函数{ float length = 0; char text[256]; //保存字符串 va_list ap; //指向一个变量列表的指针 if (fmt == NULL) //如果无输入则返回 { return; } va_start(ap, fmt); //分析可变参数 vsprintf(text, fmt, ap); //把参数值写入字符串 va_end(ap); //结束分析 for (unsigned int i=0; i<strlen(text); i++) //计算整个字符串的长度 { length += m_Gmf[(int)text[i]].gmfCellIncX; } glTranslatef(-length / 2, 0.0f, 0.0f); //左移字符串一半的长度 glPushAttrib(GL_LIST_BIT); //把显示列表属性压入堆栈 glListBase(m_Base); //设置显示列表的基础值 glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); //调用显示列表绘制字符串 glPopAttrib(); //弹出属性堆栈}首先是buildFont()函数。首先创建字体的方法与上一课基本一致,只是把m_FontSize换成了-18。接着,将wglUseFontBitmaps()函数替换成wglUseFontOutlines()函数,这个函数包含了8个参数前4个参数大家自己看注释,第5个参数为光滑度系数,这个值越小,字体看起来会越光滑(其实看不出明显差别)。第6个参数简单点说指的是字体的厚度,有厚度的字体才有立体感嘛,如果这个值为0.0就变成2D字体了。第7个参数告诉OpenGL用多边形来生成字符,使每个顶点都会具有独立的法线,这样加上光源后会有不错的效果(光源效果我们的代码中没有,大家可以自己加加看)。最后一个参数告诉OpenGL把创建的显示列表的度量数据(高度、宽度等)放在数组m_Gmf[]中。
然后是killFont()函数。它很简单,就是调用glDeleteLists()函数从m_Base开始删除256个显示列表。
最后是glPrint()函数。我们只是在原来的基础上加了一些代码,我们首先看到我们增加了一个浮点变量length用来统计整个字符串的宽度。接着我们利用循环,在循环中,由于我们已经将每个字符的度量值储存在m_Gmf[]中,我们利用m_Gmf[text[i]].gmfCellIncX来获得每个字符的宽度,累加起来就得到字符串的总宽度。然后,我们把视图原点左移length/2的距离,这样就保证字符串总是在屏幕的中心了。最后,把glListBase(m_Base-32)换成glListBase(m_Base),这是因为我们这次是从ASCII码第一个字符开始创建的显示列表。
然后,我们修改一下paintGL()函数,与之前的代码很神似,只是更改了旋转和平移部分的代码,最后让旋转变量增加,不过多解释了,具体代码如下:
void MyGLWidget::paintGL() //从这里开始进行所以的绘制{ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存 glLoadIdentity(); //重置当前的模型观察矩阵 glTranslatef(0.0f, 0.0f, m_Deep); //移入屏幕10.0单位 glRotatef(m_Rot, 1.0f, 0.0f, 0.0f); //绕x轴旋转 glRotatef(m_Rot*1.5f, 0.0f, 1.0f, 0.0f); //绕y轴旋转 glRotatef(m_Rot*1.4f, 0.0f, 0.0f, 1.0f); //绕z轴旋转 //根据字体位置设置颜色 glColor3f(1.0f*float(cos(m_Rot/20.0f)), 1.0f*float(sin(m_Rot/25.0f)), 1.0f-0.5f*float(cos(m_Rot/17.0f))); //输出文字到屏幕上 glPrint("NeHe - %3.2f", m_Rot/50.0f); m_Rot += 0.5f; //旋转变量增加}
最后,修改键盘控制的代码,就是按PageUp和PageDown可以放大缩小字体,具体代码如下:
void MyGLWidget::keyPressEvent(QKeyEvent *event){ switch (event->key()) { case Qt::Key_F1: //F1为全屏和普通屏的切换键 fullscreen = !fullscreen; if (fullscreen) { showFullScreen(); } else { showNormal(); } updateGL(); break; case Qt::Key_Escape: //ESC为退出键 close(); break; case Qt::Key_PageUp: //PageUp按下字体缩小 m_Deep -= 0.2f; break; case Qt::Key_PageDown: //PageDown按下字体放大 m_Deep += 0.2f; break; }}现在就可以运行程序查看效果了!
全部教程中需要的资源文件点此下载
0 0
- 【Qt OpenGL教程】14:轮廓字体
- 【Qt OpenGL教程】13:位图字体
- OpenGL: 3D 轮廓字体
- 【Qt OpenGL教程】15:图形字体的纹理映射
- 基于Qt的FreeType字体轮廓解析
- 基于Qt的FreeType字体轮廓解析
- 基于Qt的FreeType字体轮廓解析
- Qt OpenGL教程
- Qt OpenGL教程
- Qt OpenGL教程 (非常详细)
- 【Qt OpenGL教程】04:旋转
- 【Qt OpenGL教程】08:混合
- 【Qt OpenGL教程】20:蒙板
- 【Qt OpenGL教程】27:阴影
- Qt OpenGL教程 (非常详细)
- Qt OpenGL教程 (非常详细)
- Qt OpenGL教程 (非常详细)
- Nehe OpenGL教程第十三课 位图字体
- 二维数组和二级指针
- brk优缺点,分析的很到位
- transform
- Android学习笔记——播放音乐
- 12345
- 【Qt OpenGL教程】14:轮廓字体
- yii2 使用ueditor教程
- 【C++】拷贝构造函数和赋值函数
- 防止设备横屏
- Python Paramiko模块安装和使用
- 南邮 OJ 1269 区间相交问题
- 简单的Android Sqlite 使用
- sizeof
- 终结android开发关于R文件的报错