首页
/
每日頭條
/
科技
/
opengl線性變換和仿射變換
opengl線性變換和仿射變換
更新时间:2024-07-28 03:15:28

  八、OpenGL變換

  OpenGL變換是本篇的重點内容,它包括計算機圖形學中最基本的三維變換,即幾何變換、投影變換、裁剪變換、視口變換,以及針對OpenGL的特殊變換概念理解和用法,如相機模拟、矩陣堆棧等。學好了這章,才開始真正走進三維世界。

  OpenGL基礎圖形編程(二)OpenGL概念建立 8.1、從三維空間到二維平面

  8.1.1 相機模拟

  在真實世界裡,所有的物體都是三維的。但是,這些三維物體在計算機世界中卻必須以二維平面物體的形式表現出來。那麼,這些物體是怎樣從三維變換到二維的呢?下面我們采用相機( Camera )模拟的方式來講述這個概念,如圖8-1所示。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(1)

  實際上,從三維空間到二維平面,就如同用相機拍照一樣,通常都要經曆以下幾個步驟 (括号内表示的是相應的圖形學概念):

  第一步,将相機置于三角架上,讓它對準三維景物(視點變換,Viewing Transformation)。

  第二步,将三維物體放在适當的位置(模型變換,Modeling Transformation)。

  第三步,選擇相機鏡頭并調焦,使三維物體投影在二維膠片上(投影變換,Projection Transformation)。

  第四步,決定二維像片的大小(視口變換,Viewport Transformation)。

  這樣,一個三維空間裡的物體就可以用相應的二維平面物體表示了,也就能在二維的電腦屏幕上正确顯示了。

  8.1.2 三維圖形顯示流程

  運用相機模拟的方式比較通俗地講解了三維圖形顯示的基本過程,但在具體應用OpenGL函數庫編程時,還必須了解三維圖形世界中的幾個特殊坐标系的概念,以及用這些概念表達的三維圖形顯示流程。

  計算機本身隻能處理數字,圖形在計算機内也是以數字的形式進行加工和處理的。大家都知道,坐标建立了圖形和數字之間的聯系。為了使被顯示的物體數字化,要在被顯示的物體所在的空間中定義一個坐标系。這個坐标系的長度單位和坐标軸的方向要适合對被顯示物體的描述,這個坐标系稱為世界坐标系。

  計算機對數字化的顯示物體作了加工處理後,要在圖形顯示器上顯示,這就要在圖形顯示器屏幕上定義一個二維直角坐标系,這個坐标系稱為屏幕坐标系。這個坐标系坐标軸的方向通常取成平行于屏幕的邊緣,坐标原點取在左下角,長度單位常取成一個象素的長度,大小可以是整型數。

  為了使顯示的物體能以合适的位置、大小和方向顯示出來,必須要通過投影。投影的方法有兩種,即正射投影和透視投影。

  有時為了突出圖形的一部分,隻把圖形的某一部分顯示出來,這時可以定義一個三維視景體(Viewing Volume)。正射投影時一般是一個長方體的視景體,透視投影時一般是一個棱台似的視景體。隻有視景體内的物體能被投影在顯示平面上,其他部分則不能。在屏幕窗口内可以定義一個矩形,稱為視口(Viewport),視景體投影後的圖形就在視口内顯示。

  為了适應物理設備坐标和視口所在坐标的差别,還要作一适應物理坐标的變換。這個坐标系稱為物理設備坐标系。根據上面所述,三維圖形的顯示流程應如圖8-2所示。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(2)

  8.1.3 基本變換簡單分析  下面舉一個簡單的變換例子,cube.c:

  例8-4 簡單變換例程(cube.c)

  #include glos.h #include GL/gl.h #include GL/glu.h #include GL/glaux.h void myinit(void); void CALLBACK myReshape(GLsizei w, GLsizei h); void CALLBACK display(void); void CALLBACK display (void) { glClear(GL_COLOR_BUFFER_BIT); glColor3f (1.0, 1.0, 1.0); glLoadIdentity (); /* clear the matrix */ glTranslatef (0.0, 0.0, -5.0); /* viewing transformation */ glScalef (1.0, 2.0, 1.0); /* modeling transformation */ auxWireCube(1.0); /* draw the cube */ glFlush(); } void myinit (void) { glShadeModel (GL_FLAT); } void CALLBACK myReshape(GLsizei w, GLsizei h) { glMatrixMode (GL_PROJECTION); /* prepare for and then */ glLoadIdentity (); /* define the projection */ glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0); /* transformation */ glMatrixMode (GL_MODELVIEW); /* back to modelview matrix */ glViewport (0, 0, w, h); /* define the viewport */ } void main(void) { auxInitDisplayMode (AUX_SINGLE | AUX_RGBA); auxInitPosition (0, 0, 500, 500); auxInitWindow (Perspective 3-D Cube myinit (); auxReshapeFunc (myReshape); auxMainLoop(display); }

   以上程序運行結果就是繪制一個三維的正面透視立方體。其中已經用到了相機模拟中提到的四種基本變換,即視點變換、模型變換、投影變換和視口變換。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(3)

  下面簡單分析一下整個程序過程:

  1)視點變換。視點變換是在視點坐标系中進行的。視點坐标系于一般的物體所在的世界坐标系不同,它遵循左手法則,即左手大拇指指向Z正軸,與之垂直的四個手指指向X正軸,四指彎曲90度的方向是Y正軸。而世界坐标系遵循右手法則的。如圖8-4所示。當矩陣初始化glLoadIdentity()後,調用glTranslatef()作視點變換。函數參數(x, y, z)表示視點或相機在視點坐标系中移動的位置,這裡z=-5.0,意思是将相機沿Z負軸移動5個單位。

  通常相機位置缺省值同場景中的物體一樣,都在原點處,而且相機初始方向都指向Z負軸。

  這裡相機移走後,仍然對準立方體。如果相機需要指向另一方向,則調用glRotatef()可以改變。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(4)

  2) 模型變換 。模型變換是在世界坐标系中進行的。在這個坐标系中,可以對物體實施平移 glTranslatef()、旋轉glRotatef()和放大縮小glScalef()。例子裡隻對物體進行比例變換,glScalef(sx, sy, sz)的三個參數分别是X、Y、Z軸向的比例變換因子。缺省時都為1.0,即物體沒變化。程序中物體Y軸比例為2.0,其餘都為1.0,就是說将立方體變成長方體。

  3) 投影變換 。投影變換類似于選擇相機的鏡頭。本例中調用了一個透視投影函數 glFrustum(),在調用它之前先要用glMatrixMode()說明當前矩陣方式是投影GL_PROJECTION。這個投影函數一共有六個參數,由它們可以定義一個棱台似的視景體。即視景體内的部分可見,視景體外的部分不可見,這也就包含了三維裁剪變換。

  4) 視口變換 。視口變換就是将視景體内投影的物體顯示在二維的視口平面上。通常,都調用函數glViewport()來定義一個視口,這個過程類似于将照片放大或縮小。

  總而言之,一旦所有必要的變換矩陣被指定後,場景中物體的每一個頂點都要按照被指定的變換矩陣序列逐一進行變換。注意:OpenGL 中的物體坐标一律采用齊次坐标,即(x, y, z, w),故所有變換矩陣都采用4X4矩陣。一般說來,每個頂點先要經過視點變換和模型變換,然後進行指定的投影,如果它位于視景體外,則被裁剪掉。最後,餘下的已經變換過的頂點x、y、z坐标值都用比例因子w除,即x/w、y/w、z/w,再映射到視口區域内,這樣才能顯示在屏幕上。

  8.2、幾何變換

  實際上,上述所說的視點變換和模型變換本質上都是一回事,即圖形學中的幾何變換。

  隻是視點變換一般隻有平移和旋轉,沒有比例變換。當視點進行平移或旋轉時,視點坐标系中的物體就相當于在世界坐标系中作反方向的平移或旋轉。因此,從某種意義上講,二者可以統一,隻是各自出發點不一樣而已。讀者可以根據具體情況,選擇其中一個角度去考慮,這樣便于理解。

  8.2.1 兩個矩陣函數解釋

  這裡先解釋兩個基本OpenGL矩陣操作函數,便于以後章節的講述。函數解釋如下:

  void glLoadMatrix{fd}(const TYPE *m)

  設置當前矩陣中的元素值。函數參數*m是一個指向16個元素(m0, m1, ..., m15)的指針,這16個元素就是當前矩陣M中的元素,其排列方式如下:

  M = | m0 m4 m8 m12 | | m1 m5 m9 m13 | | m2 m6 m10 m14 | | m3 m7 m11 M15 | void glMultMatrix{fd}(const TYPE *m)

   用當前矩陣去乘m所指定的矩陣,并将結果存放于m中。當前矩陣可以是用glLoadMatrix() 指定的矩陣,也可以是其它矩陣變換函數的綜合結果。

  當幾何變換時,調用OpenGL的三個變換函數glTranslate()、glRotate()和glScale*(),實質上相當于産生了一個近似的平移、旋轉和比例矩陣,然後調用glMultMatrix()與當前矩陣相乘。但是直接調用這三個函數程序運行得快一些,因OpenGL自動能計算矩陣。

  8.2.2 平移

  平移變換函數如下:  

  void glTranslate{fd}(TYPE x,TYPE y,TYPE z)

  三個函數參數就是目标分别沿三個軸向平移的偏移量。這個函數表示用這三個偏移量生成的矩陣乘以當前矩陣。當參數是(0.0,0.0,0.0)時,表示對函數glTranslate*()的操作是單位矩陣,也就是對物體沒有影響。平移示意如圖8-5所示。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(5)

  8.2.3 旋轉

  旋轉變換函數如下:

  void glRotate{fd}(TYPE angle,TYPE x,TYPE y,TYPE z)

  函數中第一個參數是表示目标沿從點(x, y, z)到原點的方向逆時針旋轉的角度,後三個參數是旋轉的方向點坐标。這個函數表示用這四個參數生成的矩陣乘以當前矩陣。當角度參數是0.0時,表示對物體沒有影響。旋轉示意如圖8-6所示。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(6)

  8.2.3 縮放和反射

  縮放和反射變換函數如下: 

  void glScale{fd}(TYPE x,TYPE y,TYPE z)

  三個函數參數值就是目标分别沿三個軸向縮放的比例因子。這個函數表示用這三個比例因子生成的矩陣乘以當前矩陣。這個函數能完成沿相應的軸對目标進行拉伸、壓縮和反射三項功能。當參數是(1.0, 1.0, 1.0)時,表示對函數glScale*()操作是單位矩陣,也就是對物體沒有影響。當其中某個參數為負值時,表示将對目标進行相應軸的反射變換,且這個參數不為1.0,則還要進行相應軸的縮放變換。最好不要令三個參數值都為零,這将導緻目标沿三軸都縮為零。縮放和反射示意如圖8-7所示。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(7)

  8.2.5 幾何變換舉例  以上介紹了三個基本幾何變換函數,下面舉一個簡單的例子進一步說明它們的用法。程序如下:

  例 8-5 幾何變換例程(geomtrsf.c)

  #include glos.h #include GL/gl.h #include GL/glu.h #include GL/glaux.h void myinit(void); void draw_triangle(void); void CALLBACK display(void); void CALLBACK myReshape(GLsizei w, GLsizei h); void draw_triangle(void) { glBegin(GL_LINE_LOOP); glVertex2f(0.0, 25.0); glVertex2f(25.0, -25.0); glVertex2f(-25.0, -25.0); glEnd(); } void CALLBACK display(void) { glClearColor (0.0, 0.0, 0.0, 1.0); glClear (GL_COLOR_BUFFER_BIT); /* draw an original triangle */ glLoadIdentity (); glColor3f (1.0, 1.0, 1.0);/* white */ draw_triangle (); /* translating a triangle along X_axis */ glLoadIdentity (); glTranslatef (-20.0, 0.0, 0.0); glColor3f(1.0,0.0,0.0); /* red */ draw_triangle (); /* scaling a triangle along X_axis by 1.5 and along Y_axis by 0.5 */ glLoadIdentity(); glScalef (1.5, 0.5, 1.0); glColor3f(0.0,1.0,0.0); /* green */ draw_triangle (); /* rotating a triangle in a counterclockwise direction about Z_axis */ glLoadIdentity (); glRotatef (90.0, 0.0, 0.0, 1.0); glColor3f(0.0,0.0,1.0); /* blue */ draw_triangle (); /* scaling a triangle along Y_axis and reflecting it about Y_axis */ glLoadIdentity(); glScalef (1.0, -0.5, 1.0); glColor3f(1.0,1.0,0.0); /* yellow */ draw_triangle (); glFlush(); } void myinit (void) { glShadeModel (GL_FLAT); } void CALLBACK myReshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w = h) glOrtho(-50.0, 50.0, -50.0*(GLfloat)h/(GLfloat)w, 50.0*(GLfloat)h/(GLfloat)w,-1.0,1.0); else glOrtho(-50.0*(GLfloat)w/(GLfloat)h, 50.0*(GLfloat)w/(GLfloat)h, -50.0, 50.0,-1.0,1.0); glMatrixMode(GL_MODELVIEW); } void main(void) { auxInitDisplayMode (AUX_SINGLE | AUX_RGBA); auxInitPosition (0, 0, 500, 500); auxInitWindow (Geometric Transformations myinit (); auxReshapeFunc (myReshape); auxMainLoop(display); }

  以上程序運行結果:第一個白色三角形是原始三角形,第二個紅色三角形是白三角沿X 負軸平移後的三角形,第三個綠色三角形是白三角分别沿X軸和Y軸比例變換後的三角形,第四個藍色三角形是白三角繞Z正軸逆時針轉90度後的三角形,第五個黃色三角形是白三角沿Y軸方向縮小一倍且相對于X軸作反射後形成的三角形。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(8)

  8.3、投影變換

  投影變換是一種很關鍵的圖形變換,OpenGL中隻提供了兩種投影方式,一種是正射投影,另一種是透視投影。不管是調用哪種投影函數,為了避免不必要的變換,其前面必須加上以下兩句:

  glMAtrixMode(GL_PROJECTION); glLoadIdentity();

  事實上,投影變換的目的就是定義一個視景體,使得視景體外多餘的部分裁剪掉,最終圖像隻是視景體内的有關部分。本指南将詳細講述投影變換的概念以及用法。

    8.3.1 正射投影(Orthographic Projection)

  正射投影,又叫平行投影。這種投影的視景體是一個矩形的平行管道,也就是一個長方體,如圖8-9所示。正射投影的最大一個特點是無論物體距離相機多遠,投影後的物體大小尺寸不變。這種投影通常用在建築藍圖繪制和計算機輔助設計等方面,這些行業要求投影後的物體尺寸及相互間的角度不變,以便施工或制造時物體比例大小正确。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(9)

  OpenGL正射投影函數共有兩個,這在前面幾個例子中已用過。一個函數是: 

  void glOrtho(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,  GLdouble near,GLdouble far)

  它創建一個平行視景體。實際上這個函數的操作是創建一個正射投影矩陣,并且用這個矩陣乘以當前矩陣。其中近裁剪平面是一個矩形,矩形左下角點三維空間坐标是(left,bottom,-near),右上角點是(right,top,-near);遠裁剪平面也是一個矩形,左下角點空間坐标是(left,bottom,-far),右上角點是(right,top,-far)。所有的near和far值同時為正或同時為負。如果沒有其他變換,正射投影的方向平行于Z軸,且視點朝向Z負軸。

  這意味着物體在視點前面時far和near都為負值,物體在視點後面時far和near都為正值。另一個函數是:

  void gluOrtho2D(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top)

  它是一個特殊的正射投影函數,主要用于二維圖像到二維屏幕上的投影。它的near和far缺省值分别為-1.0和1.0,所有二維物體的Z坐标都為0.0。因此它的裁剪面是一個左下角點為(left,bottom)、右上角點為(right,top)的矩形。

  8.3.2 透視投影(Perspective Projection)

  透視投影符合人們心理習慣,即離視點近的物體大,離視點遠的物體小,遠到極點即為消失,成為滅點。它的視景體類似于一個頂部和底部都被切除掉的棱椎,也就是棱台。這個投影通常用于動畫、視覺仿真以及其它許多具有真實性反映的方面。

  OpenGL透視投影函數也有兩個,其中函數glFrustum()在8.1.3節中提到過,它所形成的視景體如圖8-10所示。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(10)

  這個函數原型為:

  void glFrustum(GLdouble left,GLdouble Right,GLdouble bottom,GLdouble top,GLdouble near,GLdouble far);

  它創建一個透視視景體。其操作是創建一個透視投影矩陣,并且用這個矩陣乘以當前矩陣。這個函數的參數隻定義近裁剪平面的左下角點和右上角點的三維空間坐标,即(left,bottom,-near)和(right,top,-near);最後一個參數far是遠裁剪平面的Z負值,其左下角點和右上角點空間坐标由函數根據透視投影原理自動生成。near和far表示離視點的遠近,它們總為正值。

  另一個函數是:  

  void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear, GLdouble zFar);

  它也創建一個對稱透視視景體,但它的參數定義于前面的不同,如圖8-11所示。其操作是創建一個對稱的透視投影矩陣,并且用這個矩陣乘以當前矩陣。參數 fovy定義視野在X-Z平面的角度,範圍是[0.0, 180.0];參數aspect是投影平面寬度與高度的比率;參數zNear和Far分别是遠近裁剪面沿Z負軸到視點的距離,它們總為正值。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(11)

  以上兩個函數缺省時,視點都在原點,視線沿Z軸指向負方向。二者的應用實例将在後續章節中介紹。

  8.4、裁剪變換

  在OpenGL中,空間物體的三維裁剪變換包括兩個部分:視景體裁剪和附加平面裁剪。視景體裁剪已經包含在投影變換裡,前面已述,這裡不再重複。下面簡單講一下平面裁剪函數的用法。

  除了視景體定義的六個裁剪平面(上、下、左、右、前、後)外,用戶還可自己再定義一個或多個附加裁剪平面,以去掉場景中無關的目标,如圖8-12所示。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(12)

  附加平面裁剪函數為:

   void glClipPlane(GLenum plane,Const GLdouble *equation);

  函數定義一個附加的裁剪平面。其中參數equation指向一個擁有四個系數值的數組,這四個系數分别是裁剪平面Ax By Cz D=0的A、B、 C、D值。因此,由這四個系數就能确定一個裁剪平面。參數plane是GL_CLIP_PLANEi(i=0,1,...),指定裁剪面号。

  在調用附加裁剪函數之前,必須先啟動glEnable(GL_CLIP_PLANEi),使得當前所定義的裁剪平面有效;當不再調用某個附加裁剪平面時,可用glDisable(GL_CLIP_PLANEi)關閉相應的附加裁剪功能。

  下面這個例子不僅說明了附加裁剪函數的用法,而且調用了gluPerspective()透視投影函數,讀者可以細細體會其中的用法。例程如下:

  例8-6 裁剪變換例程 ( clipball.c)

  #include glos.h #include GL/gl.h #include GL/glu.h #include GL/glaux.h void myinit(void); void CALLBACK myReshape(GLsizei w, GLsizei h); void CALLBACK display(void); void CALLBACK display(void) { GLdouble eqn[4] = {1.0, 0.0, 0.0, 0.0}; glClear(GL_COLOR_BUFFER_BIT); glColor3f (1.0, 0.0, 1.0); glPushMatrix(); glTranslatef (0.0, 0.0, -5.0); /* clip the left part of wire_sphere : x0 */ glClipPlane (GL_CLIP_PLANE0, eqn); glEnable (GL_CLIP_PLANE0); glRotatef (-90.0, 1.0, 0.0, 0.0); auxWireSphere(1.0); glPopMatrix(); glFlush(); } void myinit (void) { glShadeModel (GL_FLAT); } void CALLBACK myReshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); } void main(void) { auxInitDisplayMode (AUX_SINGLE | AUX_RGB); auxInitPosition (0, 0, 500, 500); auxInitWindow (Arbitrary Clipping Planes myinit (); auxReshapeFunc (myReshape); auxMainLoop(display); }

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(13)

  8.5、視口變換

  在前面幾節内容中已相繼提到過視口變換,這一節将針對OpenGL來講述視口變換的原理及其相關函數的用法。運用相機模拟方式,我們很容易理解視口變換就是類似于照片的放大與縮小。在計算機圖形學中,它的定義是将經過幾何變換、投影變換和裁剪變換後的物體顯示于屏幕窗口内指定的區域内,這個區域通常為矩形,稱為視口。OpenGL中相關函數是:

  glViewport(GLint x,GLint y,GLsizei width, GLsizei height);

  這個函數定義一個視口。函數參數(x, y)是視口在屏幕窗口坐标系中的左下角點坐标,參數width和height分别是視口的寬度和高度。缺省時,參數值即(0, 0, winWidth, winHeight) 指的是屏幕窗口的實際尺寸大小。所有這些值都是以象素為單位,全為整型數。

  注意 :在實際應用中,視口的長寬比率總是等于視景體裁剪面的長寬比率。如果兩個比率不相等,那麼投影後的圖像顯示于視口内時會發生變形,如圖8-14所示。另外,屏幕窗口的改變一般不明顯影響視口的大小。因此,在調用這個函數時,最好實時檢測窗口尺寸,及時修正視口的大小,保證視口内的圖像能随窗口的變化而變化,且不變形。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(14)

  8.6 矩陣堆棧

  學過計算機的人也許都知道這個使用頻率極高的名詞 — “堆棧”。顧名思義,堆棧指的是一個頂部打開底部封閉的柱狀物體,通常用來存放常用的東西。這些東西從頂部依次放入,但取出時也隻能從頂部取出,即“先進後出,後進先出”。在計算機中,它常指在内存中開辟的一塊存放某些變量的連續區域。因此,OpenGL的矩陣堆棧指的就是内存中專門用來存放矩陣數據的某塊特殊區域。

  實際上,在創建、裝入、相乘模型變換和投影變換矩陣時,都已用到堆棧操作。一般說來,矩陣堆棧常用于構造具有繼承性的模型,即由一些簡單目标構成的複雜模型。例如,一輛自行車就是由兩個輪子、一個三角架及其它一些零部件構成的。它的繼承性表現在當自行車往前走時,首先是前輪旋轉,然後整個車身向前平移,接着是後輪旋轉,然後整個車身向前平移,如此進行下去,這樣自行車就往前走了。

  矩陣堆棧對複雜模型運動過程中的多個變換操作之間的聯系與獨立十分有利。因為所有矩陣操作函數如glLoadMatrix()、glMultMatrix()、 glLoadIdentity()等隻處理當前矩陣或堆棧頂部矩陣,這樣堆棧中下面的其它矩陣就不受影響。堆棧操作函數有以下兩個:

  void glPushMatrix(void); void glPopMatrix(void);

   第一個函數表示将所有矩陣依次壓入堆棧中,頂部矩陣是第二個矩陣的備份;壓入的矩陣數不能太多,否則出錯。第二個函數表示彈出堆棧頂部的矩陣,令原第二個矩陣成為頂部矩陣,接受當前操作,故原頂部矩陣被破壞;當堆棧中僅存一個矩陣時,不能進行彈出操作,否則出錯。由此看出,矩陣堆棧操作與壓入矩陣的順序剛好相反,編程時要特别注意矩陣操作的順序。

  為了更好地理解這兩個函數,我們可以形象地認為glPushMatrix()就是“記住自己在哪”,glPopMatrix()就是“返回自己原來所在地”。請看下面一例:

  例8-7 堆棧操作例程 ( arm.c )  

  #include glos.h #include GL/gl.h #include GL/glu.h #include GL/glaux.h void myinit(void); void drawPlane(void); void CALLBACK elbowAdd (void); void CALLBACK elbowSubtract (void); void CALLBACK shoulderAdd (void); void CALLBACK shoulderSubtract (void); void CALLBACK display(void); void CALLBACK myReshape(GLsizei w, GLsizei h); static int shoulder = 0, elbow = 0; void CALLBACK elbowAdd (void) { elbow = (elbow 5) % 360; } void CALLBACK elbowSubtract (void) { elbow = (elbow - 5) % 360; } void CALLBACK shoulderAdd (void) { shoulder = (shoulder 5) % 360; } void CALLBACK shoulderSubtract (void) { shoulder = (shoulder - 5) % 360; } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT); glColor3f(0.0, 1.0, 1.0); glPushMatrix(); glTranslatef (-0.5, 0.0, 0.0); glRotatef ((GLfloat) shoulder, 0.0, 0.0, 1.0); glTranslatef (1.0, 0.0, 0.0); auxWireBox(2.0, 0.2, 0.5); glTranslatef (1.0, 0.0, 0.0); glRotatef ((GLfloat) elbow, 0.0, 0.0, 1.0); glTranslatef (0.8, 0.0, 0.0); auxWireBox(1.6, 0.2, 0.5); glPopMatrix(); glFlush(); } void myinit (void) { glShadeModel (GL_FLAT); } void CALLBACK myReshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef (0.0, 0.0, -5.0); /* viewing transform */ } void main(void) { auxInitDisplayMode (AUX_SINGLE | AUX_RGBA); auxInitPosition (0, 0, 400, 400); auxInitWindow (Composite Modeling Transformations myinit (); auxKeyFunc (AUX_LEFT, shoulderSubtract); auxKeyFunc (AUX_RIGHT, shoulderAdd); auxKeyFunc (AUX_UP, elbowAdd); auxKeyFunc (AUX_DOWN, elbowSubtract); auxReshapeFunc (myReshape); auxMainLoop(display); }

   從以上例程可以看出,複雜的機械手臂是由兩個簡單的長方體依據一定的繼承關系構成的,而這個繼承關系是由矩陣堆棧的順序決定的。

  opengl線性變換和仿射變換(OpenGL基礎圖形編程八)(15)

  原文鍊接:htt

Comments
Welcome to tft每日頭條 comments! Please keep conversations courteous and on-topic. To fosterproductive and respectful conversations, you may see comments from our Community Managers.
Sign up to post
Sort by
Show More Comments
Copyright 2023-2024 - www.tftnews.com All Rights Reserved