1. 程式人生 > >用DirectX實現魔方(一)

用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 ==