在前面繪製幾何圖形的時候,你們是否以爲咱們繪圖的範圍太狹隘了呢?座標只能從-1到1,還只能是X軸向右,Y軸向上,Z軸垂直屏幕。這些限制給咱們的繪圖帶來了不少不便。
咱們生活在一個三維的世界——若是要觀察一個物體,咱們能夠:
一、從不一樣的位置去觀察它。(視圖變換)
二、移動或者旋轉它,固然了,若是它只是計算機裏面的物體,咱們還能夠放大或縮小它。(模型變換)
三、若是把物體畫下來,咱們能夠選擇:是否須要一種「近大遠小」的透視效果。另外,咱們可能只但願看到物體的一部分,而不是所有(剪裁)。(投影變換)
四、咱們可能但願把整個看到的圖形畫下來,但它只佔據紙張的一部分,而不是所有。(視口變換)
這些,均可以在OpenGL中實現。
OpenGL變換其實是經過矩陣乘法來實現。不管是移動、旋轉仍是縮放大小,都是經過在當前矩陣的基礎上乘以一個新的矩陣來達到目的。關於矩陣的知識,這裏不詳細介紹,有興趣的朋友能夠看看線性代數(大學生的話多半應該學過的)。
OpenGL能夠在最底層直接操做矩陣,不過做爲初學,這樣作的意義並不大。這裏就不作介紹了。
一、模型變換和視圖變換
從「相對移動」的觀點來看,改變觀察點的位置與方向和改變物體自己的位置與方向具備等效性。在OpenGL中,實現這兩種功能甚至使用的是一樣的函數。
因爲模型和視圖的變換都經過矩陣運算來實現,在進行變換前,應先設置當前操做的矩陣爲「模型視圖矩陣」。設置的方法是以GL_MODELVIEW爲參數調用glMatrixMode函數,像這樣:
glMatrixMode(GL_MODELVIEW);
一般,咱們須要在進行變換前把當前矩陣設置爲單位矩陣。這也只須要一行代碼:
glLoadIdentity();
而後,就能夠進行模型變換和視圖變換了。進行模型和視圖變換,主要涉及到三個函數:
glTranslate*,把當前矩陣和一個表示移動物體的矩陣相乘。三個參數分別表示了在三個座標上的位移值。
glRotate*,把當前矩陣和一個表示旋轉物體的矩陣相乘。物體將繞着(0,0,0)到(x,y,z)的直線以逆時針旋轉,參數angle表示旋轉的角度。
glScale*,把當前矩陣和一個表示縮放物體的矩陣相乘。x,y,z分別表示在該方向上的縮放比例。
注意我都是說「與XX相乘」,而不是直接說「這個函數就是旋轉」或者「這個函數就是移動」,這是有緣由的,立刻就會講到。
假 設當前矩陣爲單位矩陣,而後先乘以一個表示旋轉的矩陣R,再乘以一個表示移動的矩陣T,最後獲得的矩陣再乘上每個頂點的座標矩陣v。因此,通過變換獲得 的頂點座標就是((RT)v)。因爲矩陣乘法的結合率,((RT)v) = (R(Tv)),換句話說,其實是先進行移動,而後進行旋轉。即:實際變換的順序與代碼中寫的順序是相反的。因爲「先移動後旋轉」和「先旋轉後移動」獲得的結果極可能不一樣,初學的時候須要特別注意這一點。
OpenGL之因此這樣設計,是爲了獲得更高的效率。但在繪製複雜的三維圖形時,若是每次都去考慮如何把變換倒過來,也是很痛苦的事情。這裏介紹另外一種思路,可讓代碼看起來更天然(寫出的代碼其實徹底同樣,只是考慮問題時用的方法不一樣了)。
讓咱們想象,座標並非固定不變的。旋轉的時候,座標系統隨着物體旋轉。移動的時候,座標系統隨着物體移動。如此一來,就不須要考慮代碼的順序反轉的問題了。
以 上都是針對改變物體的位置和方向來介紹的。若是要改變觀察點的位置,除了配合使用glRotate*和glTranslate*函數之外,還可使用這個 函數:gluLookAt。它的參數比較多,前三個參數表示了觀察點的位置,中間三個參數表示了觀察目標的位置,最後三個參數表明從(0,0,0)到 (x,y,z)的直線,它表示了觀察者認爲的「上」方向。
二、投影變換
投影變換就是定義一個可視空間,可視空間之外的物體不會被繪製到屏幕上。(注意,從如今起,座標能夠再也不是-1.0到1.0了!)
OpenGL支持兩種類型的投影變換,即透視投影和正投影。投影也是使用矩陣來實現的。若是須要操做投影矩陣,須要以GL_PROJECTION爲參數調用glMatrixMode函數。
glMatrixMode(GL_PROJECTION);
一般,咱們須要在進行變換前把當前矩陣設置爲單位矩陣。
glLoadIdentity();
透視投影所產生的結果相似於照片,有近大遠小的效果,好比在火車頭內向前照一個鐵軌的照片,兩條鐵軌彷佛在遠處相交了。
使用glFrustum函數能夠將當前的可視空間設置爲透視投影空間。具體解釋以下:html
void glFrustum(
|
GLdouble
|
left,
|
GLdouble
|
right,
|
|
GLdouble
|
bottom,
|
|
GLdouble
|
top,
|
|
GLdouble
|
nearVal,
|
|
GLdouble
|
farVal);
|
1 // 太陽、地球和月亮 2 // 假設每月都是30天 3 // 一年12個月,共是360天 4 static int day = 200; // day的變化:從0到359 5 void myDisplay(void) 6 { 7 glEnable(GL_DEPTH_TEST); 8 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 9 10 glMatrixMode(GL_PROJECTION); 11 glLoadIdentity(); 12 gluPerspective(75, 1, 1, 4000000); 13 glMatrixMode(GL_MODELVIEW); 14 glLoadIdentity(); 15 gluLookAt(0, -2000000, 2000000, 0, 0, 0, 0, 0, 1); 16 17 // 繪製紅色的「太陽」 18 glColor3f(1.0f, 0.0f, 0.0f); 19 glutSolidSphere(696000, 20, 20); 20 // 繪製藍色的「地球」 21 glColor3f(0.0f, 0.0f, 1.0f); 22 glRotatef(day / 360.0*360.0, 0.0f, 0.0f, -1.0f); 23 glTranslatef(1500000, 0.0f, 0.0f); 24 glutSolidSphere(159450, 20, 20); 25 // 繪製黃色的「月亮」 26 glColor3f(1.0f, 1.0f, 0.0f); 27 glRotatef(day / 30.0*360.0 - day / 360.0*360.0, 0.0f, 0.0f, -1.0f); 28 glTranslatef(380000, 0.0f, 0.0f); 29 glutSolidSphere(43450, 20, 20); 30 31 glFlush(); 32 33 }
注:本來代碼顯示參數寫的太大,即參照物距離放大了,致使代碼運行看不到結果,上面代碼進行縮小100倍,即大數減去兩個0,,,編程
效果以下:小程序
試修改day的值,看看畫面有何變化。
小結:本課開始,咱們正式進入了三維的OpenGL世界。
OpenGL經過矩陣變換來把三維物體轉變爲二維圖象,進而在屏幕上顯示出來。爲了指定當前操做的是何種矩陣,咱們使用了函數glMatrixMode。
咱們能夠移動、旋轉觀察點或者移動、旋轉物體,使用的函數是glTranslate*和glRotate*。
咱們能夠縮放物體,使用的函數是glScale*。
咱們能夠定義可視空間,這個空間能夠是「正投影」的(使用glOrtho或gluOrtho2D),也能夠是「透視投影」的(使用glFrustum或gluPerspective)。
咱們能夠定義繪製到窗口的範圍,使用的函數是glViewport。
矩陣有本身的「堆棧」,方便進行保存和恢復。這在繪製複雜圖形時頗有幫助。使用的函數是glPushMatrix和glPopMatrix。
好了,艱苦的一課終於完畢。我知道,本課的內容十分枯燥,就連最後的例子也是。但我也沒有更好的辦法了,但願你們能堅持過去。沒必要擔憂,熟悉本課內容後,之後的一段時間內,都會是比較輕鬆愉快的了。
===================== 第五課 完 =====================
=====================TO BE CONTINUED=====================ide