1 圖形學與圖形系統簡述
計算機圖形學是一種使用數學算法將二維或三維圖形轉化爲計算機顯示器的柵格形式的科學。它的主要研究內容就是研究如何在計算機中表示圖形、以及利用計算機進行圖形的計算、處理和顯示的相關原理與算法。圖形通常由點、線、面、體等幾何元素和灰度、色彩、線型、線寬等非幾何屬性組成。從處理技術上來看,圖形主要分爲兩類,一類是基於線條信息表示的,如工程圖、等高線地圖、曲面的線框圖等,另一類是明暗圖,也就是通常所說的真實感圖形。
計算機圖形學一個主要的目的就是要利用計算機產生令人賞心悅目的真實感圖形。爲此,必須建立圖形所描述的場景的幾何表示,再用某種光照模型,計算在假想的光源、紋理、材質屬性下的光照明效果。
計算機圖形系統主要由硬件與軟件兩大部分組成,硬件主要有顯示器,顯卡,鼠標等基本輸入輸出設備組成,負責顯示圖形,輸入輸出圖形,軟件則對圖形進行各種座標變換,光照處理,消隱處理等,使圖形輸出,並具有一定的真實感。本實驗只是在Visual C++ 2008下用MFC編寫的一個小的圖形系統的軟件部分,基本功能爲用線框顯示一個立方體和一個球體,然後用Gouraud 光照模型實現光照效果,並添加了部分額外的功能,如:旋轉物體,在多個攝像機中顯示圖形,移動和旋轉攝像機等。軟件的編寫過程按對象建模,座標變換,可見面判別,光照處理,面的繪製,物體和攝像機操作控制的步驟完成,下面將按編寫過程中的步驟分別說明。
2 對象建模
2.1 三維對象表示
一個對象最基本的特徵包括:點,線,面。要在建模座標系中表示一個物體,就要記錄該物體的點,線,面的信息,並要建立這三者之間的聯繫。這裏用指向邊表的指針來表示三維對象,三者間的關係爲:一個對象由多個多邊形面組成,每個多邊形用指向邊表的指針來表示,邊表中每條邊只存儲一次,每條邊都包括構成該邊的兩個頂點,每個頂點只在頂點表V中存儲一次。例如要建立如圖1所示簡單的正四面體,則存儲結構如下:
V={V1,V2,V3,V4}={(x1,y1,z1), (x2,y2,z2), (x3,y3,z3), (x4,y4,z4)}
E={E1,E2,E3,E4,E5,E6}={(V1,V2),(V1,V3),(V2,V3),(V1,V4),(V3,V4) ,(V2,V4)}
P={P1, P2, P3, P4}={(E1,E3,E2),(E2,E5,E4),(E1,E4,E6),(E3,E6,E5)}
因此,應該建立頂點類,邊類,面類和物體類。類聲明分別如下:
//頂點類
class CVertex
{
public:
CVertex(void);
CVertex(const CVertex &v);
CVertex(float x, float y, float z):pworld(x, y, z),position(x , y, z),pedge(NULL){};
~CVertex(void);
//世界座標
CPoint3d pworld;
//變換座標
CPoint3d position;
//光照強度
CIntens I;
//與點相關的邊表
EdgeLink *pedge;
//轉換對象的世界座標
void trfworld(const CMatrix4d &m);
//變換臨時座標
void trfpos(const CMatrix4d &m);
void operator=( CVertex const &v);
};
//邊類
class CEdge
{
public:
CEdge(const CEdge &e);
CEdge(int mh, int mt):head(mh),tail(mt),fp(-1),sp(-1){};
~CEdge(void);
//第一個節點
int head;
//第二個頂點
int tail;
//第一個面
int fp;
//第二個面
int sp;
};
//面類(三角形面)
class CPlane
{
public:
CPlane(const CPlane &p);//拷貝函數
CPlane(int m1,int m2, int m3):ft(m1),sd(m2), thd(m3),show(false){};
~CPlane(void);
int ft;//第一條邊
int sd;//第二條邊
int thd;//第三條邊
bool show;//是否顯示
CVector3d norvec;//法向量
};
//三維對象類
class CGrObj
{
public:
CGrObj(void);
~CGrObj(void);
CPoint3d position;//在世界座標系中的位置
CVector3d mcrdx;//建模座標系x軸的向量
CVector3d mcrdy;//建模座標系y 軸的向量
CVector3d mcrdz;//建模座標系z 軸的向量
vector < CVertex > V;//頂點表
vector E;//邊表
vector P;//面表
//float kd;//漫反射係數
// 調整建模座標系向量
CMatrix4d MatrMtoW;
// 把模型加載到世界座標
bool Load(void);
//指向下一個物體
CGrObj *pnextobj;
//對對象的pworld進行變換
void transformpword(CMatrix4d &m);
//對對象的position進行變換
void transformptemp(CMatrix4d &m);
//判別可見面
void visable(void);
//轉換成投影座標
void toProject(void);
//顯示
void Drow( CDC* pDC,int DrowType);
//CDC* pDC;
//更新座標,將世界座標寫入臨時座標中
void Updata(void);
//畫三角形
void Trangle(CDC * pDc, CPoint * P, CIntens * I);
//畫線
void DrowLine(CDC * pDc,int x1, int x2, CIntens & I1, CIntens & I2, int y);
protected:
// 畫橫線
void DrowLine( CDC* pDC);
//畫面
void DrowReal( CDC* pDC);
//加一個頂點
void addVertex( float x, float y, float z);
//加一條邊
void addEdge(int v1,int v2);
//加一個三角面
void addPlane(int E1,int E2,int E3);
private:
// 獲取每一頂點的顏色
void GetColor(void);
// 調整將建模座標轉換到世界座標的矩陣
void coordAdjust(void);
//求每個面的法向量
void getNorVect(void);
};
2.2 對象建立
分別建兩個CGrObj的子類,class CCube和class CSphere,在其構造函數中建立對象的數據。
建立立方體:由輸入的棱長,分別在頂點表中加入八個頂點的位置,在邊表中加入邊對應的頂點的索引,在面表中加入每個面對應的邊的索引。
建立球體:
a、徑先求南極和北極的座標,加入頂點表。
b、由半徑,經線數,緯線數,從第一條經線開始求出每條經線上各緯度的點的座標,加入頂點表。
c、由第一條經線開始,向邊表中加入本條經線與下一條經線間的所有邊。最後一條經線與第一條經線間的邊單獨處理。
d、由第一條經線開始,向面表中加入本條經線與下一條經線間的所有的面。最後一條經線與第一條經線間的面單獨處理。
3 座標變換
在第2部分的對象建模中,對象是在模型座標系中建立的,要把對象放到世界座標系中,就要將對象從模型座標系變換到世界座標系。此功能在CGrObj 類中通過調用 Load( )方法把對象加載到世界座標系中。
爲建立觀察座標系,首先需要在世界座標系中挑選一點作爲觀察座標系的原點,也稱爲觀察參考點。它就類似於相機鏡頭所在的位置。然後在該點處指定法矢量N,法矢量N即爲觀察座標系中的z軸的正向和觀察平面的方向。
爲了方便計算,將攝像頭的位置放在觀察座標系中的原點,觀察方向爲z軸負向。
要在攝像機中顯示對象,則應該把對象座標從世界座標系轉換到與攝像機對應的觀察座標系中。然後,建立起與觀察座標系中xy平面相平行的觀察平面,將觀察座標再投影到觀察平面上。
觀察座標投影到觀察平面上的過程可以經由以下幾個步驟來完成。首先,進行觀察空間的規範化變換,然後,在規範化空間內進行圖形的三維裁剪。裁剪完後即可作正投影,將裁剪後的圖形投影到觀察平面上。最後,將觀察窗口中的內容在圖形輸出設備上進行顯示。
4 可見面判別
可見面的判別可以在世界座標系中完成,也可以在規範化觀察座標系中完成,此實驗中採用後種方法。在規範化座標系中,求出每個面的法向量,法向量的z 分量大於0,則該面可見,否則不可見。判斷每一個面的可見性之後,在面類的可見屬性中修改其可見屬性。
如果用線框圖來表示物體,在將座標變換成設備座標之後,即可對邊表中的每條邊進行判斷,如果一條邊的兩個關聯面都是不可見的,則不畫該邊;否則,讀出該邊的兩個頂點座標,在屏幕上的兩點間劃直線。即可得到如下線框圖。
圖-2 線框立方體 圖-3 線框球體
5 光照處理和麪的繪製
5.1 平面處理
最簡單的光照處理方法是每個平面爲同一種顏色填充,取三個頂點處的光強的平均值作爲面的,效果圖如下:
圖-4 平面繪製的立方體 圖-5平面繪製的球體
很容易看出,圖片的光照效果並不理想,因爲各個面片的邊緣處的顏色變化太大。因此要有真實感,必須採用明暗處理方法。
5.2 明暗處理
最常見的簡單光照明暗處理算法有Gouraud明暗處理和Phong明暗處理方法,本實驗中採用雙線性光強插值的Gouraud明暗處理方法。每次物體的世界座標有變化時,都算出每個面的法向量的座標,然後由Gouraud算法算出每個頂點處的光強。
雙線性光強插值:由頂點的光強插值計算各邊的光強,然後由各邊的光強插值計算出多邊形內部點的光強。如下:
採用雙線性光強插值的Gouraud明暗處理方法後,效果圖如下:
圖-6明暗處理繪製的立方體 圖-7明暗處理繪製的球體
5.3 着色優化
當用平面處理的方法填充平面時,如果相鄰面的顏色相差比較大,就會產生較大失真效果。當用雙線性明暗處理方法填充三角面時,每個三角面都要計算逐個點的顏色,對於三個頂點顏色相差非常小,肉眼幾乎無法分辨時,這種方法非常耗時,效率很低,可以對其採用平面處理的方法填充。下面爲兩種方法下效果的對比圖:
圖-8 Gouraud明暗處理 圖-9明暗處理和平面處理相結合
從上圖可以看出,只要控制得好,可以大大提高平面繪製的效率。上圖中近40%的三角形填充採用平面填充方法,從而省去了逐點計算光強的時間。
6 物體和攝像機操作控制
6.1 物體旋轉
要實現物體的旋轉可以在系統中設置一定時器,每隔一斷時間將物體在世界座標系中的各點的座標旋轉一定的角度,然後重繪窗口。下面爲相隔一定時間後同一物體旋轉後的線框圖:
圖-10 旋轉前的立方體 圖-11旋轉後的立方體
6.2 攝像機控制
攝像機的平行移動控制可以通過改變攝像機的位置來實現,而攝像機的旋轉可以通過旋轉觀察座標系的x和y座標來實現。通過View類響應鍵盤和鼠標的消息,來控制攝像頭的移動和旋轉。
鍵盤上的方向鍵或ADSW鍵控制攝像頭的平行移動。如果要旋轉攝像頭,可點擊鼠標左鍵,此時開啓了用鼠標控制攝像頭的功能,鼠標的移動方向即爲攝像頭的視野的轉動方向。轉動完成後,再次點擊鼠標左鍵,關閉用鼠標控制攝像頭的功能。如下爲旋轉前和旋轉後的對比圖:
圖-12 攝像頭旋轉前 圖-13攝像頭旋轉後
6.3 兩個攝像頭
在系統中設置兩個攝像頭,以從不同的角度觀察同一個物體。只需在系統中再加一個CCamera 類的對象,並將其位置與x 軸方向重新設定即可。效果如下:
圖-14兩個攝像頭效果圖
(前面的圖是用Win Live Writer傳的,被壓縮過,效果太TM爛了,這個自己在網頁編輯中傳的)
7 總結
接觸圖形學三維部分已經有近半年的時間了,但半年來基本都是理論層面的學習,幾乎沒有動手寫過三維程序,此次大作業正好是一次絕好的練習機會。只有實踐時纔會真正的體會到理論與實踐的差距,平時學習圖形學中的基本原理與算法時,雖然覺得很多算法思想很精妙,但也能理解是什麼意思。可是當真正動手編程時,才知道平時有好多東西都是一知半解,例如:學習三維對象的表示時,課本上有三維對象的三種表示方法——顯式表示,指向頂點表的指針,指向邊表的指針。這幾種方法理解起來也不難,很容易就知道第三種是最合適的,但具體在編程實現數據結構時又要考慮到要能夠由點找到邊,找到面,不能每次都是在各表在進行查找,於是在每個頂點中除了應該保存位置,顏色等信息外,還應加入與之關聯的邊的指針,在每條邊中除了保存兩個頂點的指針,還應該保存與之關聯的面的指針。諸如此類的問題還有許多,在此不贅述。以後還得加強練習才能真正的學懂圖形學。