計算機圖形學OpenGL中的glLoadIdentity、glTranslatef、glRotatef原理,用法 .(轉)


單位矩陣  對角線上都是1,其余元素皆為0的矩陣。

在矩陣的乘法中,有一種矩陣起着特殊的作用,如同數的乘法中的1,我們稱這種矩陣為單位矩陣.

它是個方陣,除左上角到右下角的對角線(稱為主對角線)上的元素均為1以外全都為0.

OpenGL中的坐標用齊次坐標表示,即(x,y,z)表示成(x',y',z',h),其中x=x'/h; y=y'/h; z=z'/h. 通常h取1. 比如空間中的點(2,3,4),在OpenGL中將表示成(2,3,4,1). 齊次坐標表示方式適合於矩陣運算,也很方便地表示了無窮遠的點,比如(1,0,0,0)就表示x軸上無窮遠的點,因為1/0是無窮大,這里約定0/0=0.

接着要說點矩陣(線性代數)的知識。OpenGL里面的平移、旋轉、縮放等變換均是線性變換,用矩陣相乘來表示。以平移變換為例,請見官方對glTranslatef函數的說明。假設有點(3,3,3),如果把該點沿x軸移動2單位,沿y軸移動3單位,沿z軸移動4單位,那么該點會是(3+2, 3+3, 4+4) = (5,6,7). 用矩陣表示是:

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /> 

左邊的矩陣稱為平移變換矩陣,若把2、3、4換成x、y、z,則用它乘以一個齊次坐標表示的向量,就可以將該向量平移(x,y,z). 旋轉變換和縮放變換都像平移變換一樣可用一個矩陣來表示。這里可以不用理會這些矩陣長什么樣,只需清楚它們乘以一個齊次坐標表示的向量,就可以使該向量發生需要的變換。

把平移變換矩陣記為T(x,y,z),旋轉變換矩陣記為R(x,y,z,s),表示繞向量(x,y,z)旋轉s角度;把向量記為X。這里只需要知道它們是 矩陣就行了,現在要把一個點X,如(3,3,3,1),移動(2,2,2)單位,再繞y軸旋轉30度角,用矩陣表示即 R(0,1,0,30)*T(2,2,2)*X,可以理解為離X最近的矩陣最先作用。理解這個順序很重要,這樣,所有變換都可以用一串矩陣的相乘來表示, 計算機里面也確實是這么做的。

介紹完基本的數學知識,下面說OpenGL的作用機制。OpenGL有個變換矩陣堆棧,堆棧就像子彈夾一樣,先進的后出。OpenGL中的每個向量,在被定義之后進入到OpenGL世界中,都必須先乘以這個變換矩陣堆棧的棧頂變換矩陣。如下圖所示:

 

 

理解完上面的知識,再來理解glLoadIdentity、glTranslatef、glRotatef這些函數干了什么就容易多了。這些函數就是對這個堆棧的操作:

  • glTranslatef:將T(x,y,z)右乘與堆棧的棧頂變換矩陣。右乘的解釋,假設目前棧頂變換矩陣為M,那么就相當於把M修改為M*T.
  • glRotatef :將R(x,y,z,s)右乘與堆棧的棧頂變換矩陣。
  • glLoadIdentity:將堆棧的棧頂變換矩陣設置成單位矩陣。
  • glPushMatrix:將堆棧的棧頂變換矩陣復制一份,然后Push到堆棧中。所謂Push,就像塞子彈一樣把一個矩陣壓入到堆棧中,此時,棧頂就是這個新的矩陣了,注意定義的向量都是和棧頂變換矩陣作用的。
  • glPopMatrix:將堆棧的棧頂變換矩陣Pop出來。

該講的講完了,下面出幾道題目練習下吧。

1、OpenGL代碼是:glLoadIdentity(); glTranslate3f(4,5,1); glRotate3f(0,1,0,90); glVertex3f(1,1,1); 請問此時棧頂變換矩陣是什么?(1,1,1)這個點到了OpenGL世界中的點是什么?

答:棧頂變換矩陣是T(4,5,1)*R(0,1,0,90),(1,1,1)到OpenGL世界中的坐標是T(4,5,1)*R(0,1,0,90)*(1,1,1).

2、解釋為什么使用glPushMatrix和glPopMatrix的組合可以隔離這兩個函數中的變換,使之不影響后面的點?

答:glPushMatrix新壓入的變換矩陣是復制了原來的棧頂變換矩陣,所以它繼承了之前的變換,此后執行glTranslatef、 glRotatef這些函數時,修改的是棧頂變換矩陣,在glPopMatrix之前的點都將受到棧頂變換矩陣的作用,之后用glPopMatrix,把 棧頂變換矩陣Pop掉,此時的棧頂變換矩陣又還原成原來的那個棧頂變換矩陣。

3、為什么有時候glTranslate3f和glRotate3f能顛倒有時候又不能?

答:矩陣A乘以矩陣B未必等於矩陣B乘以矩陣A,當它們相等時,很多只是巧合。

 

 

在opengl中,函數glTranslatef的作用就是移動坐標原點。對應的3個參數對應着3個坐標軸。
如果你調用一次glTranslatef(1.0f,0.0f,0.0f)然后畫一個小球,接着再調用次glTranslatef(0.0f,1.0f,0.0f)再畫一個小球。
此時,兩個小球中,一個在另外一個正右方。
所以,如果要使兩個小球分別處於x、y軸,則需要在第二次畫之前調用glLoadIdentity()函數,使坐標原點歸位。
另外,此處的坐標系為右手坐標系。
切記切記。


2、glPushMatrix、glPopMatrix操作其實就相當於棧里的入棧和出棧。
許多人不明白的可能是入的是什么,出的又是什么。我也是自己反復做了下測試才懂的(利用無聊的web上機時間)。

例如你當前的坐標系原點在你電腦屏幕的左上方。現在你調用glPushMatrix,然后再調用一堆平移、旋轉代碼等等,然后再畫圖。那些平移和旋轉都是基於坐上角為原點進行變化的。而且都會改變坐標的位置,經過了這些變化后,你的坐標肯定不再左上角了。
那如果想恢復怎么辦呢?這時就調用glPopMatrix從棧里取出一個“狀態”了,這個狀態就是你調用glPushMatrix之前的那個狀態。就如很多opengl的書上所講:調用glPushMatrix其實就是把當前狀態做一個副本放入堆棧之中。

 

 

glLoadIdentity()


將當前的用戶坐標系的原點移到了屏幕中心:類似於一個復位操作
1.X坐標軸從左至右,Y坐標軸從下至上,Z坐標軸從里至外。
2.OpenGL屏幕中心的坐標值是X和Y軸上的0.0f點。
3.中心左面的坐標值是負值,右面是正值。
  移向屏幕頂端是正值,移向屏幕底端是負值。
  移入屏幕深處是負值,移出屏幕則是正值。

glTranslatef(x, y, z)

沿着 X, Y 和 Z 軸移動。

注意在glTranslatef(x, y, z)中,當您移動的時候,您並不是相對屏幕中心移動,而是相對與當前所在的屏幕位置。其作用就是將你繪點坐標的原點在當前原點的基礎上平移一個(x,y,z)向量。

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

glLoadIdentity();         

glTranslatef(-1.5f,0.0f,-6.0f);
glBegin(GL_TRIANGLES);
glVertex3f(0.0f,0.0f, 0.0f);
glVertex3f(1.0f,0.0f, 0.0f);
glVertex3f(0.0f,1.0f, 0.0f);
glEnd();

glLoadIdentity();
glTranslatef(0.0f,0.0f,-6.0f);
glBegin(GL_TRIANGLES);
glVertex3f(0.0f,0.0f, 0.0f);
glVertex3f(1.0f,0.0f, 0.0f);
glVertex3f(0.0f,1.0f, 0.0f);
glEnd();

/////////////////////////////////////////////////////////////////////////////////////

程序的運行結果如下:

 

左邊的三角形是第一步繪制的,可以看到該三角形繪制的坐標系,實際上是以(-1.5f,0.0f,-6.0f)為原點的。

第二個三角形繪制的時候,由於使用glLoadIdentity()使原點重新回到屏幕中心來,因此其原點位於屏幕的中心。

glRotatef(angle, x, y, z)
與glTranslatef(x, y, z)類似,glRotatef(angle, x, y, z)也是對坐標系進行操作。
旋轉軸經過原點,方向為(x,y,z),旋轉角度為angle,方向滿足右手定則。
////////////////////////////////////////////////////////////////
glLoadIdentity();
glTranslatef(0.0f,0.0f,-6.0f);
glBegin(GL_TRIANGLES);
glVertex3f(0.0f,0.0f, 0.0f);
glVertex3f(1.0f,0.0f, 0.0f);
glVertex3f(0.0f,1.0f, 0.0f);
glEnd();
////////////////////////////////////////////////////////////////
在未旋轉的情況下如圖所示:

 

 

////////////////////////////////////////////////////////////////
glLoadIdentity();
glRotatef(45,0.0f,0.0f,1.0f);
glTranslatef(0.0f,0.0f,-6.0f);
glBegin(GL_TRIANGLES);
glVertex3f(0.0f,0.0f, 0.0f);
glVertex3f(1.0f,0.0f, 0.0f);
glVertex3f(0.0f,1.0f, 0.0f);
glEnd();
////////////////////////////////////////////////////////////////
繞Z軸正向旋轉45度角,因為Z軸正方向由屏幕內指向屏幕外,由右手定則可知方向為逆時針轉動。
由於直角頂點即為原點,因此將圍繞直角逆時針旋轉。

 

////////////////////////////////////////////////////////////////
glLoadIdentity();
glRotatef(45,0.0f,0.0f,1.0f);
glLoadIdentity();
glTranslatef(0.0f,0.0f,-6.0f);
glBegin(GL_TRIANGLES);
glVertex3f(0.0f,0.0f, 0.0f);
glVertex3f(1.0f,0.0f, 0.0f);
glVertex3f(0.0f,1.0f, 0.0f);
glEnd();


免責聲明!

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



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