1. 程式人生 > 實用技巧 >Qt OpenGL 旗幟效果(飄動的紋理)

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幀一次變化了旗幟的波動狀態,看起來就是一個飄動的旗幟,不是靜止的了(大家可以嘗試著註釋掉某一部分程式碼看看發生什麼改變)。然後計數器清零加一什麼的就不過多解釋了!

現在就可以執行程式檢視效果了!