這篇文章首先會提到太陽系系統的概念,利用這個基礎的概念先規劃出Scene Graph,再寫出Psudocode不會詳細敘述該如何在OpenGL實現(就是Full Code啦~)如果有錯誤歡迎留言指正Vincent Chen
太陽系(Solar System)
How The System Work ?
太陽 - 地球 - 月球系統,為了簡化問題,運行軌道皆為正圓
以太陽為原點來說 (在此不考慮宇宙擴張、太陽繞著銀河系中心)
- 太陽不會動
- 地球繞著太陽公轉
- 地球自轉
- 月球繞著地球公轉
Scene Graph
接著找出他們的Transformation的關係
進而畫出Scene Graph
於是Base on 前一個Section所提到的來繼續發展
- 太陽不會動:
這會是T1,整個系統的原點(Root),接著T1會分出T2和Sun兩個節點 - 地球繞著太陽公轉
T2: 負責地球公轉的部分,有Rotate和Translate
由於這個node的pivote會在系統的原點,所以會先移動軌道半徑的距離,再旋轉
但是以OpenGL(或是Computer Graphic)的執行方式
所以步驟上會倒過來: R() -> T()
後面Pseudocode的部分會再把過程詳細寫一次 - 地球自轉
T3: 負責地球自轉的部分,當然只有Rotate囉
由於這個node的pivote已經透過T2的T()移到離中心軌道半徑距離的軌道上了
所以只需要旋轉就好 - 月球繞著地球公轉
T4: 負責月球繞著地球公轉,方式和T2地球繞太陽轉一樣
R() -> T() - 在這裡T3和T4會是T2的sub-node(or children),因為在月球公轉的時候,不能被地球自轉所影響,因此T3和T4會是分開的,但都受T2影響(T2 將pivot移到軌道上)
接著來看圖 (若有錯誤,歡迎指正!!)
Pseudocode
接下來,Base on Scene Graph,寫出Pseudocode
這樣離撰寫出完整的OpenGL code也不遠了!!
根據Scene Graph,先列出三個物件的Matrix的運算過程:
- T1[Sun]
- T1[T2[T3[Earth]]]
- T1[T2[T4[Moon]]]
於是先把它們轉換一下 (之後的Psudocode還是會先以T2和T4代替,這個步驟只是讓要寫Code會比較清楚):
- T1[Sun]
- T1[T2R[T2T[T3[Earth]]]]
- T1[T2R[T2T[T4R[T4T[Moon]]]]]
接著會用到的就是gl library 的 Pop 和 Push Matrix的兩個Functions:
- glPushMatrix()
- glPopMatrix()
為了簡化Pseudocode
會以Push()和Pop()來代替,會比較清楚易讀
01 | T1();02 | push();
03 | Sun();
04 | pop();
05 | push();
06 | T2();
07 | push();
08 | T3();
09 | Earth();
10 | pop();11 | push();12 | T4();
13 | Moon();
14 | pop();
15 | pop();
大致上是這樣,這裡我有作巢狀的測試,結果是work的
至於2~4行只有Sun的那個push-pop要不要加就見仁見智了,
嚴謹上來說,他屬於T1 node的另一個分支,加上去在code的維護上會比較清晰
在這之前有個錯誤的測試也拿出來檢討一下:
01 | T1();07 | T3();02 | push();03 | Sun();04 | pop();05 | push();06 | T2();
08 | Earth();
09 | T4();
10 | Moon();
11 | pop();
根據老師上課教的pop-push方式會變成這樣
那時候有和小柔討論這樣T3不會影響到只需要T4的Moon嗎?
測試結果是會的,有興趣想知道老師上課是怎麼說這段的
我筆記都有全部寫下來,可以跟我借去看看
Pull Back~ 回到正題
接著把Pseudocode再寫詳細一點,將T2和T4分解:
01 | T1();05 | push();02 | push();03 | Sun();04 | pop();
06 | T2R();
07 | T2T();
08 | push();
09 | T3();
10 | Earth();
11 | pop();12 | push();13 | T4R();
14 | T4T();
15 | Moon();
16 | pop();
17 | pop();
一整個就長這樣子,因為這個case是要做動畫
所以在Display List的結尾記得加glutSwapBuffers();
至於glFlush()的話在這裡就不用了,
因為glutSwapBuffers()本身就有take care glFlush()了 (Ref: glFinish and glFlush)
最後要給的建議就是,在做巢狀display list的時候
要養成習慣縮排和註解,在修改的時候會比較清楚,
而不會不小心修一修就修壞了
另外像這個case中,T2T4皆可再分解
有兩個Solution可以讓Code更加Well Organized
- 在分解後的的幾行Code頭尾用/* T2 Begin */、 /* T2 End */ 圍起來(以T2為例):
(pserdocode)
/* T2 Begin */
T2R();
T2T();
/* T2 End */ - 或是另外寫一個T2/T4的call back Function,在dispaly list裡直接call back:
(pseudocode)
void transform2(void){
R();
T();
}
到這裡就完成太陽系的動畫
在加上Enable Depth Test,當轉動到太陽後面就會被擋住
下一篇是如何切換鏡頭~
有寫錯須更正及有問題發問都歡迎在下面留言
我會很認真的回覆大家的~!


沒有留言:
張貼留言