OpenGL中矩陣堆棧的頻繁壓棧和出棧操作往往是入門時(shí)最大的門檻,也是最容易造成困惑的地方,今天我們來詳細(xì)理解一下。
要了解壓棧出棧,首先要搞清楚OpenGL的狀態(tài)機(jī)是個(gè)什么東西。OpenGL基礎(chǔ)概念中講述了狀態(tài)機(jī)的概念,本文不多做闡述,簡單來說,OpenGL會(huì)記錄下我們設(shè)置的各種狀態(tài),參數(shù),開關(guān)等等,不會(huì)默認(rèn)恢復(fù)。
(簡單舉例理解:)
我們打開燈的開關(guān),燈就一直亮著,不會(huì)自動(dòng)恢復(fù)。
對狀態(tài)機(jī)了解之后,結(jié)合繪圖的渲染過程,我們知道每次繪制時(shí),我們會(huì)使用各種不同的參數(shù),函數(shù),打開OpenGL各種功能(例如 開啟深度測試,開啟混合,開啟正背面提出)等等,而繪制完畢我們需要手動(dòng)關(guān)閉,那么同樣,存儲(chǔ)在棧中的矩陣也是如此。
當(dāng)我們使用模型視圖矩陣來記錄物體的基礎(chǔ)位置,各種變化等 時(shí),我們往往也需要手動(dòng)恢復(fù)矩陣的內(nèi)容。這就是我們經(jīng)常使用的PushMatrix和PopMatrix。
- 為什么需要恢復(fù)?
mv(模型視圖矩陣,下文簡稱mv)是全局的,每次繪制都會(huì)使用,而我們在做物體變化時(shí)都是基于單元矩陣進(jìn)行操作的(并非一定如此,大多數(shù)情況,也有可能一個(gè)永遠(yuǎn)不變的物體如地板,我們就不需要壓出棧)。每個(gè)不同的變化都有自己單獨(dú)的記錄方式,而我們實(shí)現(xiàn)效果利用不同的變化記錄值和模型視圖矩陣叉乘,如果每次不恢復(fù),那么我們需要計(jì)算基于當(dāng)前mv(此時(shí)mv中已經(jīng)結(jié)合了本次變化的結(jié)果)和變換叉乘結(jié)果來實(shí)現(xiàn)其他變化的效果。否則其他變換則會(huì)混入本次變化,即在本次變換的基礎(chǔ)上又進(jìn)行變化,就會(huì)出現(xiàn)不可預(yù)計(jì)的結(jié)果(俗稱亂套,鬼畜,尤其在多次變換時(shí))。
不懂?舉例:
我們有一個(gè)需求:物體進(jìn)行仿射變化,平移和旋轉(zhuǎn),平移和旋轉(zhuǎn) 我們有兩個(gè)float來記錄平移舉例和旋轉(zhuǎn)角度,那么
1- 繪制平移,我們用mv叉乘平移距離,繪制?;謴?fù)。
2- 繪制旋轉(zhuǎn),mv叉乘旋轉(zhuǎn)角度,繪制,恢復(fù)。
如果不恢復(fù)呢?
1- 繪制平移,mv叉乘平移距離,繪制
2- 繪制旋轉(zhuǎn),mv(第一步叉乘平移后的結(jié)果),叉乘旋轉(zhuǎn)
那么會(huì)是什么效果呢? 顯然,我們需要一次平移,一次旋轉(zhuǎn),而上述方法結(jié)果平移了兩次,旋轉(zhuǎn)了一次。
只是兩個(gè)變化是這個(gè)結(jié)果,持續(xù)變換呢?結(jié)果可想而知。
總結(jié)(引用老鐵的話):
mv只有一個(gè)是全局的大家都在用,如果你某個(gè)圖形A,他要有bcdefg變化,那么 push -> mvbcdefg -> pop mv,這時(shí)候物體B有個(gè)一個(gè)abc999-366的變化,push -> mv * abc999-366 ->pop ->mv, A和B都是要自己的變化,比如A必須要bcdefg都叉乘了才可作為A的最后的mv渲染,B也是同理,你把A的mvbcdefg abc999-366給B,那就亂套了,你要的最終變化給你就行了,別人的不要管
誰的變化給誰,push pop結(jié)合就OK了
最后簡單理解一下push和pop到底干了啥(引用自OpenGL基礎(chǔ)變化綜合練習(xí)實(shí)踐總結(jié))

可以理解為,push->記錄本次變化,pop->恢復(fù)本次變化, 通俗意思就是我這次該干的干完了,我收拾干凈,你們在弄你們的。