Qt OpenGL flag effect (fluttering texture)

In this tutorial, I will teach you how to create a waving flag. The flag we are going to create is, to put it bluntly, a texture mapped image that moves in a sine wave fashion. Although it will not be difficult, the effect is really good, I hope you will like it. Of course, this tutorial is based on Lesson 06. I hope you have mastered the first 6 lessons before entering this tutorial.

The effect when the program is running is as follows:

Enter the tutorial below:

We will modify the code on the basis of Lesson 06 this time, we will only explain the added part of the code, first open the myglwidget.h file, and change the class declaration as follows:

 1 #ifndef MYGLWIDGET_H
 2 #define MYGLWIDGET_H
 4 #include <QWidget>
 5 #include <QGLWidget>
 7 class MyGLWidget : public QGLWidget
 8 {
 9     Q_OBJECT
10 public:
11     explicit MyGLWidget(QWidget *parent = 0);
12     ~MyGLWidget();
14 protected:
15     //Redefinition of 3 pure virtual functions
16     void initializeGL();
17     void resizeGL(int w, int h);
18     void paintGL();
20     void keyPressEvent(QKeyEvent *event);           //Handle keyboard press events
22 private:
23     bool fullscreen;                                //Whether to display in full screen
25     GLfloat m_xRot;                                 //The angle of rotation around the x-axis
26     GLfloat m_yRot;                                 //The angle to rotate around the y-axis
27     GLfloat m_zRot;                                 //Angle of rotation around the z-axis
28     QString m_FileName;                             //The path and file name of the image
29     GLuint m_Texture;                               //store a texture
31     float m_Points[45][45][3];                      //Array to store mesh vertices
32     int m_WiggleCount;                              //Used to control the flag wave motion animation
33 };
35 #endif // MYGLWIDGET_H

CSDN QT technology stack outline: Qt development necessary technology stack learning route and materials

We added a three-dimensional array of m_Points to store the independent x, y, and z coordinates of each vertex of the grid. Here, the grid is formed by 45×45 points, in other words, it is composed of small square grids of 44 × 44 grids. Another new variable m_WiggleCount is used to animate the textured wave motion, changing the wave shape every 2 frames looks nice.

Next, we need to open myglwidget.cpp, add the statement #include <QtMath>, and initialize the new variable data in the constructor. The specific code is as follows:

 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";         //It should be modified according to the actual storage path of the picture
10     for (int x=0; x<45; x++)                            //Initialize the array to produce a wave effect (still)
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;
21     QTimer *timer = new QTimer(this);                   //create a timer
22     //Bind the timing signal of the timer to updateGL()
23     connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));
24     timer->start(10);                                   //Take 10ms as a timing cycle
25 }

The added code is a loop, using the loop to add wave effects (just to make the flag look undulating, and it can't achieve the purpose of wave animation). It is worth noting that when we find m_Points[x][y][0] and m_Points[x][y][1], we divide x and y by 5.0f. If we divide by 5, due to the integer Rounding by division will result in a jagged effect on the screen, which is obviously not what we want. Finally, subtract 4.5f so that the calculation result falls in the interval [-4.5, 4.5], which also allows our wave to be "centered". The final value of the point m_Points[x][y][2] is the result of a sin() function calculation (because we are simulating a sine wave motion), ×8.0f is to find the corresponding angle (360 degrees equally divided to 45 points is 8 degrees per point), and finally the angle is converted to radians, so I won't explain it much.

Then in the initializeGL() function, please modify the code as follows:

 1 void MyGLWidget::initializeGL()                         //Start all the settings for OpenGL here
 2 {
 3     m_Texture = bindTexture(QPixmap(m_FileName));       //Load bitmap and convert to texture
 4     glEnable(GL_TEXTURE_2D);                            //enable texture mapping
 6     glClearColor(0.0, 0.0, 0.0, 0.0);                   //black background
 7     glShadeModel(GL_SMOOTH);                            //enable shadow smoothing
 8     glClearDepth(1.0);                                  //set depth buffer
 9     glEnable(GL_DEPTH_TEST);                            //Enable depth testing
10     glDepthFunc(GL_LEQUAL);                             //Type of depth testing done
11     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  //Tell the system to correct for perspective
13     glPolygonMode(GL_BACK, GL_FILL);                    //back surface completely filled
14     glPolygonMode(GL_FRONT, GL_LINE);                   //Front surface drawn with lines
15 }

Finally, two lines of code are added to specify the use of full fill mode to fill the back surface of the polygon area, while the front surface of the polygon is filled with contour lines. These methods depend entirely on your personal preferences. Here we are just to distinguish the front and back surfaces. That's all.

Finally, we will rewrite the entire paintGL() function, of course this is still the key point, the code is as follows:

 1 void MyGLWidget::paintGL()                              //Start all drawing from here
 2 {
 3     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clear screen and depth buffer
 4     glLoadIdentity();                                   //Reset the current model view matrix
 6     glTranslatef(0.0f, 0.0f, -15.0f);                   //Move into screen 15.0 units
 7     glRotatef(m_xRot, 1.0f, 0.0f, 0.0f);                //rotate around x
 8     glRotatef(m_yRot, 0.0f, 1.0f, 0.0f);                //rotate around y
 9     glRotatef(m_zRot, 0.0f, 0.0f, 1.0f);                //rotate around z
11     glBindTexture(GL_TEXTURE_2D, m_Texture);            //rotate texture
12     float flag_x1, flag_y1, flag_x2, flag_y2;           //Used to divide the texture into small quads for texture mapping
13     glBegin(GL_QUADS);
14     for (int x=0; x<44; x++)
15     {
16         for (int y=0; y<44; y++)
17         {
18             //split texture
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;
24             //draw a small quadrilateral
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();
37     if (m_WiggleCount == 3)                             //Used to transform the wave shape (once every 2 frames) to generate wave animation
38     {
39         //Use a cycle to move the wave value collectively to the left, and the leftmost wave value reaches the rightmost
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;                              //counter clear
50     }
51     m_WiggleCount++;                                    //Increment the counter by one
53     m_xRot += 0.3f;
54     m_yRot += 0.2f;
55     m_zRot += 0.4f;
56 }

We created four floating-point temporary variables and used loops and divisions to divide the texture into small quadrilaterals, so that we can accurately map the texture, and then draw all the quadrilaterals together to form a flag in a fluctuating state.

Then we judge whether m_WiggleCount is 2, and if so, move the wave value m_Points[x][y][2] collectively to the left (the leftmost wave value will go to the rightmost). In this way, we are equivalent to changing the fluctuating state of the flag every 2 frames, which looks like a fluttering flag, not static (you can try to comment out a certain part of the code to see what changes happen). Then the counter is cleared and plus one, so I don’t need to explain too much!

Now you can run the program to see the effect!

The benefits of this article, free to receive Qt development learning materials package, technical video, including (C++ language foundation, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QT embedded development, Quick module, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

Tags: C++ Qt5 Qt6

Posted by kingssongs on Tue, 06 Dec 2022 12:52:15 +0300