OpenGL的glPushMatrix和glPopMatrix矩陣棧頂操作函數詳解


        OpenGL中圖形繪制后,往往需要一系列的變換來達到用戶的目的,而這種變換實現的原理是又通過矩陣進行操作的。opengl中的變換一般包括視圖變換、模型變換、投影變換等,在每次變換后,opengl將會呈現一種新的狀態(這也就是我們為什么會成其為狀態機)。

        有時候在經過一些變換后我們想回到原來的狀態,就像我們談戀愛一樣,換來換去還是感覺初戀好,怎么辦?強大的opengl就幫我們提供了兩個函數:giPushMatrix()和glPopMatrix();

        首先我們要知道,對於矩陣的操作都是對於矩陣棧的棧頂來操作的。當前矩陣即為矩陣棧的棧頂元素,而對當前矩陣進行平移、旋轉等的變換操作也同樣是對棧頂矩陣的修改。所以我們在變換之前調用giPushMatrix()的話,就會把當前狀態壓入第二層,不過此時棧頂的矩陣也與第二層的相同。

       當經過一系列的變換后,棧頂矩陣被修改,此時調用glPopMatrix()時,棧頂矩陣被彈出,且又會恢復為原來的狀態。

       函數的作用過程可以用下圖描述,更為直觀。

    

 

          在opengl場景中一般存在多種矩陣變換操作,而控制這些操作的命令主要用到

          glMatrixMode(GLenum mode); 
          作用:用於指定用哪個矩陣作為當前矩陣,mode用於指定哪一種矩陣棧是其后矩陣操作的目標。mode可取: 
           GL_MODELVIEW: 把其后的矩陣操作施加於造型視圖矩陣棧。(默認) 
           GL_PROJECTION: 把其后的矩陣操作施加於投影矩陣棧。 
           GL_TEXTURE: 把其后的矩陣操作施加於紋理矩陣棧。

          注意上述三種模式分別對應了三種矩陣棧。

          所以在場景中存在多種矩陣變換時,glPushMatrix()和glPopMatrix()一般情況下也要結合glMatrixMode(GLenum mode)運用,系統才知道具體操作的是哪個矩陣棧。

 

          注意:

          攝像機矩陣和模型矩陣用的是同一個矩陣,就是GL_MODELVIEW (model是模型搜索矩陣,view是攝像機矩陣,GL_MODELVIEW里保存的是這兩個矩陣的積)。所以選擇GL_MODELVIEW之后直接用glTranslate,glRotate之類的就行。

          其實攝像機和模型矩陣本質上是一回事(這也是為什么OpenGL把這兩個矩陣放在一起保存的原因),因為比如把整個世界向y+方向移動10跟把攝像機向y-方向移動10是等價的。旋轉也是一樣。

          雖然矩陣里可以保存任何變換,但按照OpenGL的概念,model和view矩陣里只能保存平移,旋轉和縮放;project矩陣里只能保存投影矩陣,viewport矩陣里只能保存二維平移和縮放。這樣來看把model和view放在一起是合理的。他們之間的區別純粹是人為的。

 

    附上代碼例子:

 1 #include <stdlib.h>
 2 #include "include\glut.h"
 3 
 4 static int year = 0, day = 0;
 5 
 6 void init(void)
 7 {
 8     glClearColor (0.0, 0.0, 0.0, 0.0);
 9     glShadeModel (GL_FLAT);
10 }
11 
12 void display(void)
13 {
14     glClear (GL_COLOR_BUFFER_BIT);
15     glColor3f (1.0, 1.0, 1.0);
16     
17     glPushMatrix();
18     {
19         glutWireSphere(1.0, 20, 16); /* draw sun */
20         glRotatef ((GLfloat) year, 0.0, 1.0, 0.0);
21 
22         glTranslatef (2.0, 0.0, 0.0);    //把坐標原點變換位置
23 
24         glRotatef ((GLfloat) day, 0.0, 1.0, 0.0);
25         glutWireSphere(0.2, 10, 8); /* draw smaller planet */
26     }
27     glPopMatrix();
28 
29     glutSwapBuffers();
30 }
31 
32 void reshape(int w, int h)
33 {
34     glViewport (0, 0, (GLsizei) w, (GLsizei) h);
35     glMatrixMode (GL_PROJECTION);
36     glLoadIdentity ();
37     gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
38 
39     glMatrixMode(GL_MODELVIEW);
40     glLoadIdentity();
41     gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
42 }
43 
44 void keyboard(unsigned char key, int x, int y)
45 {
46     switch (key) {
47         case 'd':
48             day = (day + 10) % 360;
49             glutPostRedisplay();
50             break;
51         case 'D':
52             day = (day - 10) % 360;
53             glutPostRedisplay();
54             break;
55         case 'y':
56             year = (year + 5) % 360;
57             glutPostRedisplay();
58             break;
59         case 'Y':
60             year = (year - 5) % 360;
61             glutPostRedisplay();
62             break;
63         case 27:
64             exit(0);
65             break;
66         default:
67             break;
68     }
69 }
70 
71 int main(int argc, char** argv)
72 {
73     glutInit(&argc, argv);
74     glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
75     glutInitWindowSize (500, 500);
76     glutInitWindowPosition (100, 100);
77     glutCreateWindow (argv[0]);
78     
79     init();
80     glutDisplayFunc(display);
81     glutReshapeFunc(reshape);
82     glutKeyboardFunc(keyboard);
83     glutMainLoop();
84     return 0;
85 }

    運行結果:

    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM