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