過去一週的網頁瀏覽次數

2012年1月14日 星期六

openGL - 何時該用glPushMatrix() 和 glPopMatrix()

這問題困擾了我一陣子,有很大的因素是被這學期的圖學作業影響...
回到正提,這問題在"openGL編程指南"中說的有點隱晦(個人認為)。

這問題是發生在我使用了gluLookAt()函式後發現的,從書中範例我們可以感受一件事,glPushMatrix()和glPopMatrix()的目的是為了讓編程過程變得簡單且容易了解,但我似乎無法清楚定義"應該在何時加入這兩個函數會讓程式變得容易理解"。


首先要先認清一件事,openGL FAQ 8.010開宗明義即說明:在openGL中的攝影機,永遠都在原點(0,0,0)的位置,openGL為了假裝出移動攝影機的樣子,必須移動場景對整個世界場景做攝影機的逆空間轉換。(OpenGL FAQ and Troubleshooting Guide - Q8),而gluLookAt()函式,是個工具函式,意思就是它包裝了一些openGL的基本函式在內,並不是一個原生函式,套用上面說的話,表示gluLookAt()中使用了一些glTranslate()或glRotate()等類型的函式,所以如果程式中寫了一行gluLookAt(0,0,3,0,0,0,0,1,0),實際上是把所有的物體都先往-Z軸推離攝影機3的距離,才繼續之後的轉換。

那麼現在來想一個簡單的程式,我希望攝影機放在(0,0,3) 且朝著(0,0,-5)看著,而有一個球可以在(0,0,-4)的位置自轉,因此球和攝影機應該要相距7的距離,程式可能如下:


glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0,0,3,0,0,-5,0,1,0);
glLoadIdentity();
glTranslatef(0,0,-4);
glRotatef(year,0,1,0);
glutSolidSphere(1,40,40);

老師說做任何事之前要先將當前矩陣(current matrix, CM)清空,確保轉換正確,所以第4行的glLoadIdentity()做了這件事。但是別忘記第3行的gluLookAt()先把東西都推離了3的距離,再把球更推離了4的距離,而這個glLoadIdentity()的存在,抹殺了gluLookAt()的目的,導致球只離攝影機4的距離,結果錯誤。

因此,你可以說:" 那我把glLoadIdentity()拿掉吧! ",會造成什麼結果? 結果就是每次球旋轉時,也相對的更遠了一點,結果還是錯。

從這邊可以發現,其實,我們並不是要一個會把CM完全清空的方法,也不是放任不管,而是我們需要一個手段可以"記錄CM的樣子",並且在事情做完後,CM可以回復到"之前記憶的樣子",而這就是glPushMatrix()和glPopMatrix()的功能!!

glPushMatrix()將當前矩陣push入一個stack,反之glPopMatrix()取出stack頂端的矩陣,因此我們就可以"記憶gluLookAt()後的CM長像",並且在所有轉換都做完之後,又再度回到"gluLookAt()後的CM長像",而且重點是這兩個函數是硬體實作且hard dependent,速度更快,因此可以用一句話形容:

glPushMatrix()是記住自己現在的位置,而glPopMatrix()是回到之前記住的位置!!


所以當你需要記住目前位置,且要再回到原位,而撰寫程式時不想要一直考慮CM有沒有跑掉的時後,就是使用這兩個函數的時機點了。


而stack的深度與硬體有關,可以用glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, *GLint *params)來查詢,或者到OpenGL capabilities report: GL_MAX_MODELVIEW_STACK_DEPTH查詢你的顯示卡/顯示晶片是否是常見的32深度。

1 則留言: