用DirectX實現魔方(一)
關於魔方
魔方英文名字叫做Rubik's Cube,是由匈牙利建築學教授和雕塑家Ernő Rubik於1974年發明,最初叫做Magic Cube(這大概也是中文名字的來歷吧),1980年Ideal Toys公司開始銷售此玩具,並將名字改為Rubik's Cube。
魔方在80年代最為風靡,至今未衰。截至2009年1月,魔方在全世界已經售出了3億五千多萬個。最常見的魔方是三階魔方,由27個小方塊構成,共三層,每層9個小方塊。我的Demo實現的就是三階魔方。其他的魔方種類有二階,四階及更高階,也有鑽石魔方,五邊形魔方,三角魔方等。
三階魔方所有可能的排列數是43252003274489856000,這個數實在是太大了,用中文不知道該如何表達。可以打一個比方,如果有這麼多個三階魔方,那麼可以覆蓋地球表面275次!
Demo來歷
這是我以前學習DirectX的時候寫的一個Demo,大概是2008年左右,當時寫完以後高興了好幾天,現在拿出來看看,彼時的情景歷歷在目。隨著年齡的增長,已經不能像以前那麼拼命的寫程式了,現在想安靜下來乾點事都是奢望呀,不過對於DirectX的熱情倒是有增無減,一有時間還是會抽空寫點程式碼。對於強大的DX來說,這個Demo簡直是小兒科了,不過麻雀雖小,五臟俱全。再小的東西也有值得學習和總結的地方,本著這個目的,我將這個Demo從新整理了一下,簡化了一些程式碼,並改進了一些演算法,拿出來和大家分享。說實話,這個Demo有很多地方我不是很滿意,發出來也是為了能收集一下大家的意見,繼續改進,歡迎大家多多指教。我打算分幾個部分詳細介紹一下這個Demo的編寫原理。
- 概述(此篇),講一下整個程式的結構及流程。
- 構造魔方,模型構造,貼圖,光照,渲染等。
- 旋轉視角,主要介紹一下如何用Arcball來實現旋轉。
- 旋轉魔方,如何旋轉某一層,這是程式最核心的部分,佔了整個程式50%左右的程式碼。
- 雜項,一些不好分類的都放在這裡,並不是不重要,比如D3D程式框架,D3D裝置的管理,全屏及視窗的切換等。
知識準備
程式採用C/C++語言+DirectX 9.0編寫,用的還是固定管線API,因為我對shader不太熟悉,稍後有空學習一下可以出個shader版本。也可能移植到DirectX 11上,就算是練練手吧。這個Demo涉及的技術有以下幾個方面。
- C/C++語言
- DirectX,Vertex, Index。光照,紋理對映等,都是入門級的東西。
- Windows程式設計,視窗管理,訊息處理等。
- 計算機圖形學,這個就不用多說了,必須的。
- 數學,線性代數,空間解析幾何,Arcball及相交檢測都會涉及到一點數學知識。
效果圖
俗話說得好,有圖有真相!先來個透視照(線框圖)
然後來個素顏照(實體未貼圖)
再來個有貼圖的(穿上衣服後,好看多了),魔方的顏色採用國際標準配色。
- 前面-白色
- 後面-黃色
- 左面-紅色
- 右面-橙色
- 頂面-綠色
- 底面-藍色
旋轉某一層
打亂順序
實現原理
構造魔方
起初,模型採用的是DirectX的.x檔案格式,現在.x格式已經被微軟拋棄了,儘管你仍然可以使用它,但是在DirectX 11中,已經沒有支援.x檔案的介面了。要使用.x檔案,只能使用舊版本的DirectX SDK或者用第三方庫。由於魔方對應的幾何模型比較簡單,就是立方體,所以就乾脆不用.x檔案了,直接畫,一個完整的魔方由27個小的立方體構成,所以如果能繪製一個小立方體,那麼就可以繪製27個,拼成一個完整的魔方。
關於貼圖,開始用的是紋理圖片,後來簡化了一下,直接在記憶體中生成紋理,因為單色的,而且只有六種顏色,並不麻煩。動態生成的一個好處是釋出程式的時候也不同釋出紋理圖片了,只有一個可執行檔案。
旋轉魔方
旋轉魔方是通過滑鼠拖拽來完成的,分為如下兩個部分:
- 旋轉整個魔方(右鍵拖拽)
- 旋轉某一層(左鍵拖拽)
前者通過變換視角來完成,實現採用Arcball技術,Arcball有很多優點,相比尤拉角來說,Arcball更加平滑,而且沒有抖動現象(這個本質是因為Arcball裡面採用的是Quaternion)。變換視角而不是通過旋轉魔方本身的好處是
- 實現更方便,更高效。
- 不改變模型的座標,維持模型的座標不便對於旋轉魔方的某一層十分重要。
後者通過旋轉模型本身來實現,因為變換視角會影響場景中的所有模型,而旋轉某一層要保證其他層不動,所以只能旋轉模型本身,因為將魔方拆成了27個小的cube。這對於只操作某些部分而維持其他部分不變是十分方便的。旋轉某一層的方法如下
- 通過滑鼠點選生成拾取射線,判斷射線是否擊中魔方,如擊中則執行後續步驟,否則不做任何操作。
- 通過滑鼠移動判斷該旋轉哪一層
- 根據旋轉層選定該層包含的小立方體
- 計算旋轉軸和旋轉角度
- 旋轉這些立方體
- 滑鼠鬆開時完成剩下的旋轉(保證每次旋轉都是90度的倍數,否則魔方無法對齊)
鍵位介紹
- 滑鼠左鍵(拖拽)-旋轉某一層
- 滑鼠右鍵(拖拽)-旋轉整個魔方
- 滾輪-縮放
- F - 全屏及視窗切換
- S - 打亂順序
- R - 還原
- Esc 退出程式
程式結構
主要有如下幾個類及檔案
- Arcball,軌跡球,模型旋轉的根基。
- Camera,攝像機類,負責顯示場景,變換視角。會用到Arcball類。
- Cube,構成魔方的小立方體類,包括構造,繪製,貼圖,更新變換矩陣等主要介面。
- D3D9,這個類是後來加入的,把大部分與D3D9相關的操作全部歸到這裡了。
- RubikCube,魔方類,總控,協調其他類完成魔方的所有功能。會用到Cube類。
- Math,數學相關,主要有三角形,矩形,射線的實現,以及射線和三角形的相交檢測。
- Main,程式入口,負責建立視窗和執行程式。
程式下載
先出個不太成熟的版本,bug一定不少,歡迎大家提出寶貴意見。
== Happy Coding ==