Qt OpenGL 图形字体的纹理映射

摘要:
我们可以使用自动纹理坐标生成器,它会自动为字体上的每一个多边形生成纹理坐标。这次课中我们还将使用Wingdings字体来显示一个海盗旗的标志,为此我们需要修改buildFont()函数代码。但其实这样修改后,我们想用的Wingdings字体并不会工作。这是由于Wingdings里的字体都不是标准字符字体,我们必须告诉Windows这种字体是一种符号字体而不是一种标准字符字体,因此我们在设置字符集时,把参数改为SYMBOL_CHARSET,如此Wingdings字体就能正常工作了。

这次教程中,我们将在第14课的基础上创建带有纹理的字体,它真的很简单。也许你想知道如何才能给字体赋予纹理贴图?我们可以使用自动纹理坐标生成器,它会自动为字体上的每一个多边形生成纹理坐标。

这次课中我们还将使用Wingdings字体来显示一个海盗旗(骷髅头和十字骨头)的标志,为此我们需要修改buildFont()函数代码。如果你想显示文字的话,就不用改动第14课中buildFont()函数的代码了,当然你也可以选择另一种字体,这都不是什么大事。

程序运行时效果如下:

Qt OpenGL 图形字体的纹理映射第1张

下面进入教程:

我们这次将在第14课的基础上修改代码,由于有前两课代码的基础,这节课会更简单。我只解释新增的代码,首先打开myglwidget.h文件,将类声明更改如下:

1 #ifndef MYGLWIDGET_H
2 #define MYGLWIDGET_H
3  
4 #include <QWidget>
5 #include <QGLWidget>
6  
7 class MyGLWidget : publicQGLWidget
8 {
9 Q_OBJECT
10 public:
11     explicit MyGLWidget(QWidget *parent = 0);
12     ~MyGLWidget();
13  
14 protected:
15     //对3个纯虚函数的重定义
16     voidinitializeGL();
17     void resizeGL(int w, inth);
18     voidpaintGL();
19  
20     void keyPressEvent(QKeyEvent *event);           //处理键盘按下事件
21  
22 private:
23     void buildFont();                               //创建字体
24     void killFont();                                //删除显示列表
25     void glPrint(const char *fmt, ...);             //输出字符串
26  
27 private:
28     bool fullscreen;                                //是否全屏显示
29     HDC m_HDC;                                      //储存当前设备的指针
30  
31     GLYPHMETRICSFLOAT m_Gmf[256];                   //记录256个字符的信息
32     GLfloat m_Deep;                                 //移入屏幕的距离
33     GLuint m_Base;                                  //储存绘制字体的显示列表的开始位置
34     GLfloat m_Rot;                                  //用于旋转字体
35  
36     QString m_FileName;                             //图片的路径及文件名
37     GLuint m_Texture;                               //储存一个纹理
38 };
39  
40 #endif //MYGLWIDGET_H

注意到唯一的变化就是最后增加了两个变量,这个两个变量相信大家已经很熟悉了,m_FileName用来保存我们将用于纹理映射的图片路径名,m_Texture用于储存纹理。

接下来我们需要打开myglwidget.cpp,先修改构造函数初始化m_FileName,不多解释了,具体代码如下:

1 MyGLWidget::MyGLWidget(QWidget *parent) :
2 QGLWidget(parent)
3 {
4     fullscreen = false;
5     m_Deep = -3.0f;
6     m_Rot = 0.0f;
7     m_FileName = "D:/QtOpenGL/QtImage/Lights.bmp";        //应根据实际存放图片的路径进行修改
8  
9     HWND hWND = (HWND)winId();                          //获取当前窗口句柄
10     m_HDC = GetDC(hWND);                                //通过窗口句柄获得HDC
11  
12     QTimer *timer = new QTimer(this);                   //创建一个定时器
13     //将定时器的计时信号与updateGL()绑定
14     connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));
15     timer->start(10);                                   //以10ms为一个计时周期
16 }

继续,我们需要略微修改一下buildFont()函数,修改后代码如下:

1 void MyGLWidget::buildFont()                            //创建位图字体
2 {
3     HFONT font;                                         //字体句柄
4     m_Base = glGenLists(256);                           //创建256个显示列表
5     font = CreateFont(-18,                              //字体高度
6                       0,                                //字体宽度
7                       0,                                //字体的旋转角度
8                       0,                                //字体底线的旋转角度
9                       FW_BOLD,                          //字体的重量
10                       FALSE,                            //是否斜体
11                       FALSE,                            //是否使用下划线
12                       FALSE,                            //是否使用删除线
13                       SYMBOL_CHARSET,                   //设置字符集
14                       OUT_TT_PRECIS,                    //输出精度
15                       CLIP_DEFAULT_PRECIS,              //剪裁精度
16                       ANTIALIASED_QUALITY,              //输出质量
17                       FF_DONTCARE | DEFAULT_PITCH,      //Family and Pitch的设置
18                       LPCWSTR("Wingdings"));            //字体名称(电脑中已装的)
19  
20     SelectObject(m_HDC, font);                          //选择字体
21  
22     wglUseFontOutlines(m_HDC,                           //当前HDC
23                        0,                               //从ASCII码第一个字符开始
24                        255,                             //字符数
25                        m_Base,                          //第一个显示列表的名称
26                        0.1f,                            //字体光滑度,越小越光滑
27                        0.2f,                            //在z方向突出的距离(字体的厚度)
28                        WGL_FONT_POLYGONS,               //使用多边形来生成字符,每个顶点具有独立法线
29                        m_Gmf);                            //用于储存字形度量数据(高度,宽度等)
30 }

注意到我们只是修改了CreateFont()函数的第9个参数(设置字符集)和最后一个参数(字体名称),修改最后一个参数好理解,因为我们要使用字体Wingdings嘛。但其实这样修改后,我们想用的Wingdings字体并不会工作。这是由于Wingdings里的字体都不是标准字符字体,我们必须告诉Windows这种字体是一种符号字体而不是一种标准字符字体,因此我们在设置字符集时,把参数改为SYMBOL_CHARSET,如此Wingdings字体就能正常工作了。

然后我们需要来修改initializeGL()函数,这是本节课的重点部分,具体代码如下:

1 void MyGLWidget::initializeGL()                         //此处开始对OpenGL进行所以设置
2 {
3     //自动生成纹理
4 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
5 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
6 glEnable(GL_TEXTURE_GEN_S);
7 glEnable(GL_TEXTURE_GEN_T);
8  
9     m_Texture = bindTexture(QPixmap(m_FileName));       //载入位图并转换成纹理
10     glEnable(GL_TEXTURE_2D);                            //启用纹理映射
11  
12     glClearColor(0.0, 0.0, 0.0, 0.0);                   //黑色背景
13     glShadeModel(GL_SMOOTH);                            //启用阴影平滑
14     glClearDepth(1.0);                                  //设置深度缓存
15     glEnable(GL_DEPTH_TEST);                            //启用深度测试
16     glDepthFunc(GL_LEQUAL);                             //所作深度测试的类型
17     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  //告诉系统对透视进行修正
18  
19     buildFont();                                        //创建字体
20 }

注意到,我们一开始增加了四行新代码,这四行代码将为我们绘制在屏幕上的任何物体自动生成纹理坐标。函数glTexGen非常强大,而且复杂,在这里我们没法完全讲清楚。我们只需要知道GL_S和GL_T是纹理坐标就可以了。默认状态下,它被设置为提取物体此刻在屏幕上的x坐标和y坐标,并把它们装换为顶点坐标。我们运行程序时会发现到物体在z平面没有纹理,只显示一些斑纹,而正面和反面都被赋予了纹理,这些都是由glTexGen函数产生的。

GL_TEXTURE_GEN_MODE允许我们选择我们想在S和T纹理坐标上使用的纹理映射模式,我们有三种选择:GL_EYE_LINEAR - 会使纹理固定在屏幕上,它不会移动,物体将被赋予处于它通过的地区的那一块纹理;GL_OBJECT_LINEAR - 纹理被固定于屏幕上运动的物体上;GL_SPHERE_MAP - 创建一种有金属质感的物体(大家可以变化着试试,效果都很不错)。

当然下面增加的两行用于生成纹理和启用纹理映射,和第06课的代码一样的,不解释了。

最后,我们来修改一下paintGL()函数,具体代码如下:

1 void MyGLWidget::paintGL()                              //从这里开始进行所以的绘制
2 {
3     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存
4     glLoadIdentity();                                   //重置当前的模型观察矩阵
5 glBindTexture(GL_TEXTURE_2D, m_Texture);
6  
7     glTranslatef(1.1f*float(cos(m_Rot/16.0f)),
8                  0.8f*float(sin(m_Rot/20.0f)), m_Deep); //物体移动及控制大小
9     glRotatef(m_Rot, 1.0f, 0.0f, 0.0f);                 //绕x轴旋转
10     glRotatef(m_Rot*1.2f, 0.0f, 1.0f, 0.0f);            //绕y轴旋转
11     glRotatef(m_Rot*1.4f, 0.0f, 0.0f, 1.0f);            //绕z轴旋转
12     //输出文字到屏幕上
13     glPrint("N");
14     m_Rot += 0.1f;                                      //旋转变量增加
15 }

首先是绑定我们已经生产的纹理,接着由于我们这次纹理不需要融合颜色,于是去掉了选择颜色的代码。然后是物体移动和旋转的代码,我也不解释了,这只是其中一种变换方式,使得能产生动画,大家完全可以自己设计平移和旋转部分的代码(如加上键盘控制等)。然后就需要来输出我们的“海盗旗”了,如果你不知道我是如何从字母“N”中得到海盗旗符号的,那就打开写字板,在字体出选择Wingdings字体。输入大写字母“N”,就会显示出海盗旗符号了。

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

免责声明:文章转载自《Qt OpenGL 图形字体的纹理映射》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Java高性能并发编程——线程池设置ubantu的软件源地址下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

OpenGL编程逐步深入(四)Shaders

OpenGl 中的 Shader在一些中文书籍或资料中都被翻译为“着色器”, 单从字面意思也看不出Shader到底是什么,Shader实际上就是一段代码,用于完成特定功能的一个模块。Shader分为Vertex Shader(顶点着色器)和Pixel Shader(像素着色器)两种,其中Pixel Shader在本文中又被称为Fragment Shade...

OpenGL代码学习(22)--绘制通道

注意:需要在配置好OpenGL的编程环境中运行下列代码,环境配置文章可参考: OpenGL在Mac项目上的配置下面的代码,直接放置在main.cpp文件中即可: #include "GLTools.h" #include "GLShaderManager.h" #include "GLFrustum.h" #include "GLBatch.h" #inc...

基于OpenGL编写一个简易的2D渲染框架-04 绘制图片

阅读文章前需要了解的知识,纹理:https://learnopengl-cn.github.io/01%20Getting%20started/06%20Textures/ 过程简述:利用 FreeImage 库加载图像数据,再创建 OpenGL 纹理,通过 Canvas2D 画布绘制,最后又 Renderer 渲染器渲染 本来想用 soil 库加载图像数...

RenderMonkey 练习 第三天 【OpenGL renderToTexture】

渲染到纹理: 1. 新建一个OpenGL 空effect; 2. 添加渲染目标纹理, Add Texture-> Add Render Texture 3. 添加一个渲染pass 4. 将pass0 渲染到纹理上, add Render Target->renderTexture; 5. 在pass1中,引用纹理 Add Tex...