• Qt OpenGL 旗帜效果(飘动的纹理)


    这次教程中,我将教大家如何创建一个飘动的旗帜。我们所要创建的旗帜,说白了就是一个以正弦波方式运动的纹理映射图像。虽然不会很难,但效果确实很不错,希望大家能喜欢。当然这次教程是基于第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帧一次变化了旗帜的波动状态,看起来就是一个飘动的旗帜,不是静止的了(大家可以尝试着注释掉某一部分代码看看发生什么改变)。然后计数器清零加一什么的就不过多解释了!

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

  • 相关阅读:
    Jsの练习-将 数组中值为0 的去掉,不为0的存入一个新的数组
    Jsの数组练习-求一组数中的最大值和最小值,以及所在位置
    ES6-解构赋值
    vue初体验-ES6 基础知识补充 let 和const
    2D过渡模块的其他属性
    CSS学习笔记-05 过渡模块的基本用法
    echarts笔记
    不可思议的纯 CSS 滚动进度条效果
    十大排序算法
    关于input上传文件
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/14048211.html
Copyright © 2020-2023  润新知