我們的項目做的是把人的照片轉為三維頭像。這其中需要將人臉輪廓提取出來,根據照片得到人頭模型,把人的照片作成貼圖,貼在模型上。
我們小組原本打算用SFS得到人臉模型,但深入研究了以後發現SFS是個很高端的領域,難度過大,於是我們決定先做出一個普通人臉模型,然後根據照片調整這個人臉模型。
由於我主要負責人臉模型的建立,在這裡我主要講講如何用OpenGL將模型讀入。
首先介紹一下OpenGL。
OpenGL全稱Open Graphics Library,既開放的圖形程式介面,它是在行業領域中最為廣泛接納的2D/3D圖形API,雖然Direct3D也是十分優秀的圖形API,但它只用於Windows系統。而OpenGL不僅用於Windows,還可以用於,Unix/Linux等其它系統,它甚至在大型電腦、各種專業電腦(如:醫學用顯示裝置)上都有應用。並且OpenGL的基本命令都做到了與硬體無關,甚至是平台無關。
更重要的是它和C語言密切結合,對於學過C語言的人來講容易上手。如果你想用電腦作圖但不知道從何下手,如果你只會C語言,建議你學OpenGL。
這裡的OBJ檔案,並不是我們編程的時候出現的,用於編譯應用程式的檔案。這裡的OBJ檔案是指Alias|Wavefront公司為它的一套基於工作站的3D建模和動畫軟體"Advanced Visualizer"開發的一種標準3D模型檔案格式。它很適合用於3D軟體模型之間的互導,用3D MAX就可以匯出OBJ檔案。
OBJ檔案是一種文字檔格式,可以直接用寫字板開啟。開啟後你會發現它非常簡潔整齊,下面是一個樣本的OBJ檔案內部。
關於它的具體檔案結構,各位可以網上尋找更詳細的內容,在這裡我們只關注頂點資料。v指幾何體頂點,vt指貼圖座標點,vn指頂點法線,f是索引號,有斜線符號(/)表示:f 頂點索引/貼圖座標點索引/法線索引。索引號指的其實是點的順序。假設有4個點,點1,點2,點3,點4。f 1 2 3 4 的串連順序是點1、2、3、4,而f 1 3 2 4 的串連順序就是點1、3、2、4。
然後我們就可以寫程式,讀入OBJ檔案了。先用fopen讀入檔案,然後我們讓程式一行一行地讀檔案。可以看到,檔案開始有兩行對我們沒用的資訊: 讀到這兩行以後,不操作。
接著是 這行串連了一個材質檔案.mtl,但由於我在OpenGL檔案中已經指定了貼圖,這行也不操做。
繼續,讀到了定義物體的地方。
上面樣本的OBJ檔案只有一個名為skin_hi的物體:
每個這樣的結構都定義了一個物體。我們在程式中也應該定義一個物體資料類型。
這是我定義的資料類型,OBJECTS體現整個OBJ檔案共有多少物體,同時能指向各個物體。OBJECT指一個物體,含有v的數量和f的數量,和儲存的v,vn,vt,f的指標,通過觀察發現vn、vt和v的數量一樣,因此不另外指出。
F資料類型用來儲存 f v/vt/vn。每個索引對應的點、貼圖座標、發現分別儲存在x[i][0]、x[i][1]、x[i][2],當s=4時,用x[0]~x[3]儲存,s=3時,用x[0]~x[2]儲存。
VETTEX,VNORMAL,VT用的都是float x,y,z,但vt由於對應的是平面圖形,只有x和y。
好了,接下去只要遇到點就讀在點的數組裡,遇到法線就讀在法線的數組裡,以此類推。注意記憶體空間的申請,就可以成功地讀檔案了。
在寫這個讀OBJ檔案的程式時,感觸最深的就是各種資料,必須時刻保持清晰的頭腦否則很容易就混了。其實我這個資料結構還可以進一步最佳化。畢竟各種資料結構相嵌套在使用的時候很不方便。在寫的過程中,我發現,它的索引值(即f後面的數字)是不斷累加的,並不是每個物體都從1開始,也就是說,可以只定義OBJECT,而不定義OBJECTS,因為我們並不關心一個檔案中究竟有多少物體,我們只要把所有物體畫出來就可以了。而不斷累加的f值使得能把所有點都放在一個數組中,而不用像我一樣,把每個物體的點分開為一個數組存放。
好了,把OBJ檔案讀進去了,接下去我們該把它繪製出來了。
首先,在初始化OpenGL環境時,加入
在繪製函數中加入
glBegin()和glEnd()是OpenGL繪製的部分,glBegin()的參數體現了繪製的圖形類型,glVertex3f()是繪製點的函數。下面一段代碼體現了OpenGL繪製三角形的方法。
glBegin(GL_TRIANGLES);
glVertex3f( 0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f, 0.0f);
glEnd();
下面貼出我們項目的範例圖片。
原始
旋轉
眼睛縮小 眼睛放大
眼睛縮小與眼睛放大,是對眼睛對應的座標點進行縮放變換。雖然效果有些生硬,但體現的是我們有能力對個別座標點進行變化。我們項目目前遇到的問題是如何把照片上的二維點轉變為空白間中的三維點,或者說,不知道怎麼把照片與模型聯絡在一起。
通過這個項目,我們學到了很多,就我個人而言,激發了我對OpenGL的興趣,同時對編程也有了進一步理解。而且一整個大工程下來,對項目的製作、團隊的配合有了更深的體會。