一杯清酒邀明月
天下本无事,庸人扰之而烦耳。

这次教程中,我将教大家如何创建一个飘动的旗帜。我们所要创建的旗帜,说白了就是一个以正弦波方式运动的纹理映射图像。虽然不会很难,但效果确实很不错,希望大家能喜欢。当然这次教程是基于第06课的,希望大家确保已经掌握了前6课再进入本次教程。

程序运行时效果如下:

下面进入教程:

我们这次将在第06课的基础上修改代码,我们只会解释增加部分的代码,首先打开myglwidget.h文件,将类声明更改如下:

 1 #ifndef MYGLWIDGET_H
 2 #define MYGLWIDGET_H
 3  
 4 #include <QWidget>
 5 #include <QGLWidget>
 6  
 7 class MyGLWidget : public QGLWidget
 8 {
 9     Q_OBJECT
10 public:
11     explicit MyGLWidget(QWidget *parent = 0);
12     ~MyGLWidget();
13  
14 protected:
15     //对3个纯虚函数的重定义
16     void initializeGL();
17     void resizeGL(int w, int h);
18     void paintGL();
19  
20     void keyPressEvent(QKeyEvent *event);           //处理键盘按下事件
21  
22 private:
23     bool fullscreen;                                //是否全屏显示
24  
25     GLfloat m_xRot;                                 //绕x轴旋转的角度
26     GLfloat m_yRot;                                 //绕y轴旋转的角度
27     GLfloat m_zRot;                                 //绕z轴旋转的角度
28     QString m_FileName;                             //图片的路径及文件名
29     GLuint m_Texture;                               //储存一个纹理
30  
31     float m_Points[45][45][3];                      //储存网格顶点的数组
32     int m_WiggleCount;                              //用于控制旗帜波浪运动动画
33 };
34  
35 #endif // MYGLWIDGET_H

我们增加了m_Points三维数组来存放网格各顶点独立的x、y、z坐标,这里网格由45×45点形成,换句话说也就是由44格×44格的小方格子组成的。另一个新增变量m_WiggleCount用来使产生纹理波浪运动动画,每2帧一次变换波动形状看起来很不错。

接下来,我们需要打开myglwidget.cpp,加上声明#include <QtMath>,在构造函数对新增变量数据进行初始化,具体代码如下:

 1 MyGLWidget::MyGLWidget(QWidget *parent) :
 2     QGLWidget(parent)
 3 {
 4     fullscreen = false;
 5     m_xRot = 0.0f;
 6     m_yRot = 0.0f;
 7     m_zRot = 0.0f;
 8     m_FileName = "D:/QtOpenGL/QtImage/Tim.bmp";         //应根据实际存放图片的路径进行修改
 9  
10     for (int x=0; x<45; x++)                            //初始化数组产生波浪效果(静止)
11     {
12         for (int y=0; y<45; y++)
13         {
14             m_Points[x][y][0] = float((x / 5.0f) - 4.5f);
15             m_Points[x][y][1] = float((y / 5.0f) - 4.5f);
16             m_Points[x][y][2] = float(sin((((x/5.0f)*40.0f)/360.0f)*3.141592654*2.0f));
17         }
18     }
19     m_WiggleCount = 0;
20  
21     QTimer *timer = new QTimer(this);                   //创建一个定时器
22     //将定时器的计时信号与updateGL()绑定
23     connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));
24     timer->start(10);                                   //以10ms为一个计时周期
25 }

增加的代码就是一个循环,利用循环来添加波浪效果(只是让旗帜看起来有起伏效果,还不能达到波动动画的目的)。值得注意的是,我们在求m_Points[x][y][0]和m_Points[x][y][1]时,都是用x、y除以5.0f,如果除以5的话,由于整数除法取整,会导致画面出现锯齿效果,这显然不是我们想要的。最后减去4.5f这样使得计算结果落在区间[-4.5, 4.5],也就让我们的波浪可以“居中”了。点m_Points[x][y][2]最后的值就是一个sin()函数计算的结果(因为我们模拟的是正弦波运动),×8.0f是求相应角度(360度平分到45个点就是8度一个点了),最后角度转换到弧度制我就不多做解释了。

然后在initializeGL()函数中,请大家修改代码如下:

 1 void MyGLWidget::initializeGL()                         //此处开始对OpenGL进行所以设置
 2 {
 3     m_Texture = bindTexture(QPixmap(m_FileName));       //载入位图并转换成纹理
 4     glEnable(GL_TEXTURE_2D);                            //启用纹理映射
 5  
 6     glClearColor(0.0, 0.0, 0.0, 0.0);                   //黑色背景
 7     glShadeModel(GL_SMOOTH);                            //启用阴影平滑
 8     glClearDepth(1.0);                                  //设置深度缓存
 9     glEnable(GL_DEPTH_TEST);                            //启用深度测试
10     glDepthFunc(GL_LEQUAL);                             //所作深度测试的类型
11     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  //告诉系统对透视进行修正
12  
13     glPolygonMode(GL_BACK, GL_FILL);                    //后表面完全填充
14     glPolygonMode(GL_FRONT, GL_LINE);                   //前表面使用线条绘制
15 }

最后加了两行代码,用来指定使用完全填充模式来填充多边形区域的后表面,而多边形的前表面则使用轮廓线填充,这些方式完全取决于你的个人喜好,这里我们只是为了区分前后表面罢了。

最后,我们将重写整个paintGL()函数,当然这依旧是重点,代码如下:

 1 void MyGLWidget::paintGL()                              //从这里开始进行所以的绘制
 2 {
 3     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存
 4     glLoadIdentity();                                   //重置当前的模型观察矩阵
 5  
 6     glTranslatef(0.0f, 0.0f, -15.0f);                   //移入屏幕15.0单位
 7     glRotatef(m_xRot, 1.0f, 0.0f, 0.0f);                //绕x旋转
 8     glRotatef(m_yRot, 0.0f, 1.0f, 0.0f);                //绕y旋转
 9     glRotatef(m_zRot, 0.0f, 0.0f, 1.0f);                //绕z旋转
10  
11     glBindTexture(GL_TEXTURE_2D, m_Texture);            //旋转纹理
12     float flag_x1, flag_y1, flag_x2, flag_y2;           //用来将纹理分割成小的四边形方便纹理映射
13     glBegin(GL_QUADS);
14     for (int x=0; x<44; x++)
15     {
16         for (int y=0; y<44; y++)
17         {
18             //分割纹理
19             flag_x1 = float(x) / 44.0f;
20             flag_y1 = float(y) / 44.0f;
21             flag_x2 = float(x+1) / 44.0f;
22             flag_y2 = float(y+1) / 44.0f;
23  
24             //绘制一个小的四边形
25             glTexCoord2f(flag_x1, flag_y1);
26             glVertex3f(m_Points[x][y][0], m_Points[x][y][1], m_Points[x][y][2]);
27             glTexCoord2f(flag_x1, flag_y2);
28             glVertex3f(m_Points[x][y+1][0], m_Points[x][y+1][1], m_Points[x][y+1][2]);
29             glTexCoord2f(flag_x2, flag_y2);
30             glVertex3f(m_Points[x+1][y+1][0], m_Points[x+1][y+1][1], m_Points[x+1][y+1][2]);
31             glTexCoord2f(flag_x2, flag_y1);
32             glVertex3f(m_Points[x+1][y][0], m_Points[x+1][y][1], m_Points[x+1][y][2]);
33         }
34     }
35     glEnd();
36  
37     if (m_WiggleCount == 3)                             //用来变换波浪形状(每2帧一次)产生波浪动画
38     {
39         //利用循环使波浪值集体左移,最左侧波浪值到了最右侧
40         for (int y=0; y<45; y++)
41         {
42             float temp = m_Points[0][y][2];
43             for (int x=0; x<44; x++)
44             {
45                 m_Points[x][y][2] = m_Points[x+1][y][2];
46             }
47             m_Points[44][y][2] = temp;
48         }
49         m_WiggleCount = 0;                              //计数器清零
50     }
51     m_WiggleCount++;                                    //计数器加一
52  
53     m_xRot += 0.3f;
54     m_yRot += 0.2f;
55     m_zRot += 0.4f;
56 }

我们创建了四个浮点临时变量并利用循环和除法,将纹理分割成小的四边形,使得我们能准确的对应进行纹理映射,然后画出全部的四边形拼到一起就是一个波动状态的旗帜了。

接着我们判断一下m_WiggleCount是否为2,如果是,就将波浪值m_Points[x][y][2]集体循环左移(最左侧波浪值会到最右侧)。这样我们相当于每2帧一次变化了旗帜的波动状态,看起来就是一个飘动的旗帜,不是静止的了(大家可以尝试着注释掉某一部分代码看看发生什么改变)。然后计数器清零加一什么的就不过多解释了!

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

posted on 2020-11-27 15:23  一杯清酒邀明月  阅读(560)  评论(0编辑  收藏  举报