今天要講的是三維變換的內(nèi)容,課程比較枯燥。主要是因?yàn)楹芏嗪瘮?shù)在單獨(dú)使用時(shí)都不好描述其效果,我只好在最后舉一個(gè)比較綜合的例子。希望大家能一口氣看到底了。只看一次可能不夠,如果感覺(jué)到迷糊,不妨多看兩遍。有疑問(wèn)可以在下面跟帖提出。
我也使用了若干圖形,希望可以幫助理解。
在前面繪制幾何圖形的時(shí)候,大家是否覺(jué)得我們繪圖的范圍太狹隘了呢?坐標(biāo)只能從-1到1,還只能是X軸向右,Y軸向上,Z軸垂直屏幕。這些限制給我們的繪圖帶來(lái)了很多不便。
我們生活在一個(gè)三維的世界——如果要觀察一個(gè)物體,我們可以:
1、從不同的位置去觀察它。(視圖變換)
2、移動(dòng)或者旋轉(zhuǎn)它,當(dāng)然了,如果它只是計(jì)算機(jī)里面的物體,我們還可以放大或縮小它。(模型變換)
3、如果把物體畫(huà)下來(lái),我們可以選擇:是否需要一種“近大遠(yuǎn)小”的透視效果。另外,我們可能只希望看到物體的一部分,而不是全部(剪裁)。(投影變換)
4、我們可能希望把整個(gè)看到的圖形畫(huà)下來(lái),但它只占據(jù)紙張的一部分,而不是全部。(視口變換)
這些,都可以在OpenGL中實(shí)現(xiàn)。
OpenGL變換實(shí)際上是通過(guò)矩陣乘法來(lái)實(shí)現(xiàn)。無(wú)論是移動(dòng)、旋轉(zhuǎn)還是縮放大小,都是通過(guò)在當(dāng)前矩陣的基礎(chǔ)上乘以一個(gè)新的矩陣來(lái)達(dá)到目的。關(guān)于矩陣的知識(shí),這里不詳細(xì)介紹,有興趣的朋友可以看看線性代數(shù)(大學(xué)生的話多半應(yīng)該學(xué)過(guò)的)。
OpenGL可以在最底層直接操作矩陣,不過(guò)作為初學(xué),這樣做的意義并不大。這里就不做介紹了。
1、模型變換和視圖變換
從“相對(duì)移動(dòng)”的觀點(diǎn)來(lái)看,改變觀察點(diǎn)的位置與方向和改變物體本身的位置與方向具有等效性。在OpenGL中,實(shí)現(xiàn)這兩種功能甚至使用的是同樣的函數(shù)。
由于模型和視圖的變換都通過(guò)矩陣運(yùn)算來(lái)實(shí)現(xiàn),在進(jìn)行變換前,應(yīng)先設(shè)置當(dāng)前操作的矩陣為“模型視圖矩陣”。設(shè)置的方法是以GL_MODELVIEW為參數(shù)調(diào)用glMatrixMode函數(shù),像這樣:
glMatrixMode(GL_MODELVIEW);
通常,我們需要在進(jìn)行變換前把當(dāng)前矩陣設(shè)置為單位矩陣。這也只需要一行代碼:
glLoadIdentity();
然后,就可以進(jìn)行模型變換和視圖變換了。進(jìn)行模型和視圖變換,主要涉及到三個(gè)函數(shù):
glTranslate*,把當(dāng)前矩陣和一個(gè)表示移動(dòng)物體的矩陣相乘。三個(gè)參數(shù)分別表示了在三個(gè)坐標(biāo)上的位移值。
glRotate*,把當(dāng)前矩陣和一個(gè)表示旋轉(zhuǎn)物體的矩陣相乘。物體將繞著(0,0,0)到(x,y,z)的直線以逆時(shí)針旋轉(zhuǎn),參數(shù)angle表示旋轉(zhuǎn)的角度。
glScale*,把當(dāng)前矩陣和一個(gè)表示縮放物體的矩陣相乘。x,y,z分別表示在該方向上的縮放比例。
注意我都是說(shuō)“與XX相乘”,而不是直接說(shuō)“這個(gè)函數(shù)就是旋轉(zhuǎn)”或者“這個(gè)函數(shù)就是移動(dòng)”,這是有原因的,馬上就會(huì)講到。
假設(shè)當(dāng)前矩陣為單位矩陣,然后先乘以一個(gè)表示旋轉(zhuǎn)的矩陣R,再乘以一個(gè)表示移動(dòng)的矩陣T,最后得到的矩陣再乘上每一個(gè)頂點(diǎn)的坐標(biāo)矩陣v。所以,經(jīng)過(guò)變換得到的頂點(diǎn)坐標(biāo)就是((RT)v)。由于矩陣乘法的結(jié)合率,((RT)v) = (R(Tv)),換句話說(shuō),實(shí)際上是先進(jìn)行移動(dòng),然后進(jìn)行旋轉(zhuǎn)。即:實(shí)際變換的順序與代碼中寫(xiě)的順序是相反的。由于“先移動(dòng)后旋轉(zhuǎn)”和“先旋轉(zhuǎn)后移動(dòng)”得到的結(jié)果很可能不同,初學(xué)的時(shí)候需要特別注意這一點(diǎn)。
OpenGL之所以這樣設(shè)計(jì),是為了得到更高的效率。但在繪制復(fù)雜的三維圖形時(shí),如果每次都去考慮如何把變換倒過(guò)來(lái),也是很痛苦的事情。這里介紹另一種思路,可以讓代碼看起來(lái)更自然(寫(xiě)出的代碼其實(shí)完全一樣,只是考慮問(wèn)題時(shí)用的方法不同了)。
讓我們想象,坐標(biāo)并不是固定不變的。旋轉(zhuǎn)的時(shí)候,坐標(biāo)系統(tǒng)隨著物體旋轉(zhuǎn)。移動(dòng)的時(shí)候,坐標(biāo)系統(tǒng)隨著物體移動(dòng)。如此一來(lái),就不需要考慮代碼的順序反轉(zhuǎn)的問(wèn)題了。
以上都是針對(duì)改變物體的位置和方向來(lái)介紹的。如果要改變觀察點(diǎn)的位置,除了配合使用glRotate*和glTranslate*函數(shù)以外,還可以使用這個(gè)函數(shù):gluLookAt。它的參數(shù)比較多,前三個(gè)參數(shù)表示了觀察點(diǎn)的位置,中間三個(gè)參數(shù)表示了觀察目標(biāo)的位置,最后三個(gè)參數(shù)代表從(0,0,0)到 (x,y,z)的直線,它表示了觀察者認(rèn)為的“上”方向。
2、投影變換
投影變換就是定義一個(gè)可視空間,可視空間以外的物體不會(huì)被繪制到屏幕上。(注意,從現(xiàn)在起,坐標(biāo)可以不再是-1.0到1.0了!)
OpenGL支持兩種類型的投影變換,即透視投影和正投影。投影也是使用矩陣來(lái)實(shí)現(xiàn)的。如果需要操作投影矩陣,需要以GL_PROJECTION為參數(shù)調(diào)用glMatrixMode函數(shù)。
glMatrixMode(GL_PROJECTION);
通常,我們需要在進(jìn)行變換前把當(dāng)前矩陣設(shè)置為單位矩陣。
glLoadIdentity();
透視投影所產(chǎn)生的結(jié)果類似于照片,有近大遠(yuǎn)小的效果,比如在火車頭內(nèi)向前照一個(gè)鐵軌的照片,兩條鐵軌似乎在遠(yuǎn)處相交了。
使用glFrustum函數(shù)可以將當(dāng)前的可視空間設(shè)置為透視投影空間。其參數(shù)的意義如下圖:
聲明:該圖片來(lái)自www.opengl.org,該圖片是《OpenGL編程指南》一書(shū)的附圖,由于該書(shū)的舊版(第一版,1994年)已經(jīng)流傳于網(wǎng)絡(luò),我希望沒(méi)有觸及到版權(quán)問(wèn)題。
也可以使用更常用的gluPerspective函數(shù)。其參數(shù)的意義如下圖:
聲明:該圖片來(lái)自www.opengl.org,該圖片是《OpenGL編程指南》一書(shū)的附圖,由于該書(shū)的舊版(第一版,1994年)已經(jīng)流傳于網(wǎng)絡(luò),我希望沒(méi)有觸及到版權(quán)問(wèn)題。
正投影相當(dāng)于在無(wú)限遠(yuǎn)處觀察得到的結(jié)果,它只是一種理想狀態(tài)。但對(duì)于計(jì)算機(jī)來(lái)說(shuō),使用正投影有可能獲得更好的運(yùn)行速度。
使用glOrtho函數(shù)可以將當(dāng)前的可視空間設(shè)置為正投影空間。其參數(shù)的意義如下圖:
聲明:該圖片來(lái)自www.opengl.org,該圖片是《OpenGL編程指南》一書(shū)的附圖,由于該書(shū)的舊版(第一版,1994年)已經(jīng)流傳于網(wǎng)絡(luò),我希望沒(méi)有觸及到版權(quán)問(wèn)題。
如果繪制的圖形空間本身就是二維的,可以使用gluOrtho2D。他的使用類似于glOrgho。
3、視口變換
當(dāng)一切工作已經(jīng)就緒,只需要把像素繪制到屏幕上了。這時(shí)候還剩最后一個(gè)問(wèn)題:應(yīng)該把像素繪制到窗口的哪個(gè)區(qū)域呢?通常情況下,默認(rèn)是完整的填充整個(gè)窗口,但我們完全可以只填充一半。(即:把整個(gè)圖象填充到一半的窗口內(nèi))
聲明:該圖片來(lái)自www.opengl.org,該圖片是《OpenGL編程指南》一書(shū)的附圖,由于該書(shū)的舊版(第一版,1994年)已經(jīng)流傳于網(wǎng)絡(luò),我希望沒(méi)有觸及到版權(quán)問(wèn)題。
使用glViewport來(lái)定義視口。其中前兩個(gè)參數(shù)定義了視口的左下腳(0,0表示最左下方),后兩個(gè)參數(shù)分別是寬度和高度。
4、操作矩陣堆棧
介于是入門(mén)教程,先簡(jiǎn)單介紹一下堆棧。你可以把堆棧想象成一疊盤(pán)子。開(kāi)始的時(shí)候一個(gè)盤(pán)子也沒(méi)有,你可以一個(gè)一個(gè)往上放,也可以一個(gè)一個(gè)取下來(lái)。每次取下的,都是最后一次被放上去的盤(pán)子。通常,在計(jì)算機(jī)實(shí)現(xiàn)堆棧時(shí),堆棧的容量是有限的,如果盤(pán)子過(guò)多,就會(huì)出錯(cuò)。當(dāng)然,如果沒(méi)有盤(pán)子了,再要求取一個(gè)盤(pán)子,也會(huì)出錯(cuò)。
我們?cè)谶M(jìn)行矩陣操作時(shí),有可能需要先保存某個(gè)矩陣,過(guò)一段時(shí)間再恢復(fù)它。當(dāng)我們需要保存時(shí),調(diào)用glPushMatrix函數(shù),它相當(dāng)于把矩陣(相當(dāng)于盤(pán)子)放到堆棧上。當(dāng)需要恢復(fù)最近一次的保存時(shí),調(diào)用glPopMatrix函數(shù),它相當(dāng)于把矩陣從堆棧上取下。OpenGL規(guī)定堆棧的容量至少可以容納32個(gè)矩陣,某些OpenGL實(shí)現(xiàn)中,堆棧的容量實(shí)際上超過(guò)了32個(gè)。因此不必過(guò)于擔(dān)心矩陣的容量問(wèn)題。
通常,用這種先保存后恢復(fù)的措施,比先變換再逆變換要更方便,更快速。
注意:模型視圖矩陣和投影矩陣都有相應(yīng)的堆棧。使用glMatrixMode來(lái)指定當(dāng)前操作的究竟是模型視圖矩陣還是投影矩陣。
5、綜合舉例
好了,視圖變換的入門(mén)知識(shí)差不多就講完了。但我們不能就這樣結(jié)束。因?yàn)楸敬握n程的內(nèi)容實(shí)在過(guò)于枯燥,如果分別舉例,可能效果不佳。我只好綜合的講一個(gè)例子,算是給大家一個(gè)參考。至于實(shí)際的掌握,還要靠大家自己花功夫。閑話少說(shuō),現(xiàn)在進(jìn)入正題。
我們要制作的是一個(gè)三維場(chǎng)景,包括了太陽(yáng)、地球和月亮。假定一年有12個(gè)月,每個(gè)月30天。每年,地球繞著太陽(yáng)轉(zhuǎn)一圈。每個(gè)月,月亮圍著地球轉(zhuǎn)一圈。即一年有360天。現(xiàn)在給出日期的編號(hào)(0~359),要求繪制出太陽(yáng)、地球、月亮的相對(duì)位置示意圖。(這是為了編程方便才這樣設(shè)計(jì)的。如果需要制作更現(xiàn)實(shí)的情況,那也只是一些數(shù)值處理而已,與OpenGL關(guān)系不大)
首先,讓我們認(rèn)定這三個(gè)天體都是球形,且他們的運(yùn)動(dòng)軌跡處于同一水平面,建立以下坐標(biāo)系:太陽(yáng)的中心為原點(diǎn),天體軌跡所在的平面表示了X軸與Y軸決定的平面,且每年第一天,地球在X軸正方向上,月亮在地球的正X軸方向。
下一步是確立可視空間。注意:太陽(yáng)的半徑要比太陽(yáng)到地球的距離短得多。如果我們直接使用天文觀測(cè)得到的長(zhǎng)度比例,則當(dāng)整個(gè)窗口表示地球軌道大小時(shí),太陽(yáng)的大小將被忽略。因此,我們只能成倍的放大幾個(gè)天體的半徑,以適應(yīng)我們觀察的需要。(百度一下,得到太陽(yáng)、地球、月亮的大致半徑分別是:696000km, 6378km,1738km。地球到太陽(yáng)的距離約為1.5億km=150000000km,月亮到地球的距離約為380000km。)
讓我們假想一些數(shù)據(jù),將三個(gè)天體的半徑分別“修改”為:69600000(放大100倍),15945000(放大2500倍),4345000(放大5000倍)。將地球到月亮的距離“修改”為38000000(放大100倍)。地球到太陽(yáng)的距離保持不變。
為了讓地球和月亮在離我們很近時(shí),我們?nèi)匀徊恍枰儞Q觀察點(diǎn)和觀察方向就可以觀察它們,我們把觀察點(diǎn)放在這個(gè)位置:(0, -200000000, 0) ——因?yàn)榈厍蜍壍腊霃綖?/span>150000000,咱們就湊個(gè)整,取-200000000就可以了。觀察目標(biāo)設(shè)置為原點(diǎn)(即太陽(yáng)中心),選擇Z軸正方向作為 “上”方。當(dāng)然我們還可以把觀察點(diǎn)往“上”方移動(dòng)一些,得到(0, -200000000, 200000000),這樣可以得到45度角的俯視效果。
為了得到透視效果,我們使用gluPerspective來(lái)設(shè)置可視空間。假定可視角為60度(如果調(diào)試時(shí)發(fā)現(xiàn)該角度不合適,可修改之。我在最后選擇的數(shù)值是75。),高寬比為1.0。最近可視距離為1.0,最遠(yuǎn)可視距離為200000000*2=400000000。即:gluPerspective (60, 1, 1, 400000000);
5、綜合舉例
好了,視圖變換的入門(mén)知識(shí)差不多就講完了。但我們不能就這樣結(jié)束。因?yàn)楸敬握n程的內(nèi)容實(shí)在過(guò)于枯燥,如果分別舉例,可能效果不佳。我只好綜合的講一個(gè)例子,算是給大家一個(gè)參考。至于實(shí)際的掌握,還要靠大家自己花功夫。閑話少說(shuō),現(xiàn)在進(jìn)入正題。
我們要制作的是一個(gè)三維場(chǎng)景,包括了太陽(yáng)、地球和月亮。假定一年有12個(gè)月,每個(gè)月30天。每年,地球繞著太陽(yáng)轉(zhuǎn)一圈。每個(gè)月,月亮圍著地球轉(zhuǎn)一圈。即一年有360天。現(xiàn)在給出日期的編號(hào)(0~359),要求繪制出太陽(yáng)、地球、月亮的相對(duì)位置示意圖。(這是為了編程方便才這樣設(shè)計(jì)的。如果需要制作更現(xiàn)實(shí)的情況,那也只是一些數(shù)值處理而已,與OpenGL關(guān)系不大)
首先,讓我們認(rèn)定這三個(gè)天體都是球形,且他們的運(yùn)動(dòng)軌跡處于同一水平面,建立以下坐標(biāo)系:太陽(yáng)的中心為原點(diǎn),天體軌跡所在的平面表示了X軸與Y軸決定的平面,且每年第一天,地球在X軸正方向上,月亮在地球的正X軸方向。
下一步是確立可視空間。注意:太陽(yáng)的半徑要比太陽(yáng)到地球的距離短得多。如果我們直接使用天文觀測(cè)得到的長(zhǎng)度比例,則當(dāng)整個(gè)窗口表示地球軌道大小時(shí),太陽(yáng)的大小將被忽略。因此,我們只能成倍的放大幾個(gè)天體的半徑,以適應(yīng)我們觀察的需要。(百度一下,得到太陽(yáng)、地球、月亮的大致半徑分別是:696000km, 6378km,1738km。地球到太陽(yáng)的距離約為1.5億km=150000000km,月亮到地球的距離約為380000km。)
讓我們假想一些數(shù)據(jù),將三個(gè)天體的半徑分別“修改”為:69600000(放大100倍),15945000(放大2500倍),4345000(放大2500倍)。將地球到月亮的距離“修改”為38000000(放大100倍)。地球到太陽(yáng)的距離保持不變。
為了讓地球和月亮在離我們很近時(shí),我們?nèi)匀徊恍枰儞Q觀察點(diǎn)和觀察方向就可以觀察它們,我們把觀察點(diǎn)放在這個(gè)位置:(0, -200000000, 0) ——因?yàn)榈厍蜍壍腊霃綖?/span>150000000,咱們就湊個(gè)整,取-200000000就可以了。觀察目標(biāo)設(shè)置為原點(diǎn)(即太陽(yáng)中心),選擇Z軸正方向作為 “上”方。當(dāng)然我們還可以把觀察點(diǎn)往“上”方移動(dòng)一些,得到(0, -200000000, 200000000),這樣可以得到45度角的俯視效果。
為了得到透視效果,我們使用gluPerspective來(lái)設(shè)置可視空間。假定可視角為60度(如果調(diào)試時(shí)發(fā)現(xiàn)該角度不合適,可修改之。我在最后選擇的數(shù)值是75。),高寬比為1.0。最近可視距離為1.0,最遠(yuǎn)可視距離為200000000*2=400000000。即:gluPerspective (60, 1, 1, 400000000);
現(xiàn)在我們來(lái)看看如何繪制這三個(gè)天體。
為了簡(jiǎn)單起見(jiàn),我們把三個(gè)天體都想象成規(guī)則的球體。而我們所使用的glut實(shí)用工具中,正好就有一個(gè)繪制球體的現(xiàn)成函數(shù):glutSolidSphere,這個(gè)函數(shù)在“原點(diǎn)”繪制出一個(gè)球體。由于坐標(biāo)是可以通過(guò)glTranslate*和glRotate*兩個(gè)函數(shù)進(jìn)行隨意變換的,所以我們就可以在任意位置繪制球體了。函數(shù)有三個(gè)參數(shù):第一個(gè)參數(shù)表示球體的半徑,后兩個(gè)參數(shù)代表了“面”的數(shù)目,簡(jiǎn)單點(diǎn)說(shuō)就是球體的精確程度,數(shù)值越大越精確,當(dāng)然代價(jià)就是速度越緩慢。這里我們只是簡(jiǎn)單的設(shè)置后兩個(gè)參數(shù)為20。
太陽(yáng)在坐標(biāo)原點(diǎn),所以不需要經(jīng)過(guò)任何變換,直接繪制就可以了。
地球則要復(fù)雜一點(diǎn),需要變換坐標(biāo)。由于今年已經(jīng)經(jīng)過(guò)的天數(shù)已知為day,則地球轉(zhuǎn)過(guò)的角度為day/一年的天數(shù)*360度。前面已經(jīng)假定每年都是360天,因此地球轉(zhuǎn)過(guò)的角度恰好為day。所以可以通過(guò)下面的代碼來(lái)解決:
glRotatef(day, 0, 0, -1);
/* 注意地球公轉(zhuǎn)是“自西向東”的,因此是饒著Z軸負(fù)方向進(jìn)行逆時(shí)針旋轉(zhuǎn) */
glTranslatef(地球軌道半徑, 0, 0);
glutSolidSphere(地球半徑, 20, 20);
月亮是最復(fù)雜的。因?yàn)樗粌H要繞地球轉(zhuǎn),還要隨著地球繞太陽(yáng)轉(zhuǎn)。但如果我們選擇地球作為參考,則月亮進(jìn)行的運(yùn)動(dòng)就是一個(gè)簡(jiǎn)單的圓周運(yùn)動(dòng)了。如果我們先繪制地球,再繪制月亮,則只需要進(jìn)行與地球類似的變換:
glRotatef(月亮旋轉(zhuǎn)的角度, 0, 0, -1);
glTranslatef(月亮軌道半徑, 0, 0);
glutSolidSphere(月亮半徑, 20, 20);
但這個(gè)“月亮旋轉(zhuǎn)的角度”,并不能簡(jiǎn)單的理解為day/一個(gè)月的天數(shù)30*360度。因?yàn)槲覀冊(cè)诶L制地球時(shí),這個(gè)坐標(biāo)已經(jīng)是旋轉(zhuǎn)過(guò)的。現(xiàn)在的旋轉(zhuǎn)是在以前的基礎(chǔ)上進(jìn)行旋轉(zhuǎn),因此還需要處理這個(gè)“差值”。我們可以寫(xiě)成:day/30*360 - day,即減去原來(lái)已經(jīng)轉(zhuǎn)過(guò)的角度。這只是一種簡(jiǎn)單的處理,當(dāng)然也可以在繪制地球前用glPushMatrix保存矩陣,繪制地球后用glPopMatrix恢復(fù)矩陣。再設(shè)計(jì)一個(gè)跟地球位置無(wú)關(guān)的月亮位置公式,來(lái)繪制月亮。通常后一種方法比前一種要好,因?yàn)楦↑c(diǎn)的運(yùn)算是不精確的,即是說(shuō)我們計(jì)算地球本身的位置就是不精確的。拿這個(gè)不精確的數(shù)去計(jì)算月亮的位置,會(huì)導(dǎo)致 “不精確”的成分累積,過(guò)多的“不精確”會(huì)造成錯(cuò)誤。我們這個(gè)小程序沒(méi)有去考慮這個(gè),但并不是說(shuō)這個(gè)問(wèn)題不重要。
還有一個(gè)需要注意的細(xì)節(jié): OpenGL把三維坐標(biāo)中的物體繪制到二維屏幕,繪制的順序是按照代碼的順序來(lái)進(jìn)行的。因此后繪制的物體會(huì)遮住先繪制的物體,即使后繪制的物體在先繪制的物體的“后面”也是如此。使用深度測(cè)試可以解決這一問(wèn)題。使用的方法是:1、以GL_DEPTH_TEST為參數(shù)調(diào)用glEnable函數(shù),啟動(dòng)深度測(cè)試。2、在必要時(shí)(通常是每次繪制畫(huà)面開(kāi)始時(shí)),清空深度緩沖,即:glClear(GL_DEPTH_BUFFER_BIT);其中,glClear (GL_COLOR_BUFFER_BIT)與glClear(GL_DEPTH_BUFFER_BIT)可以合并寫(xiě)為:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
且后者的運(yùn)行速度可能比前者快。
到此為止,我們終于可以得到整個(gè)“太陽(yáng),地球和月亮”系統(tǒng)的完整代碼。
Code:
--------------------------------------------------------------------------------
// 太陽(yáng)、地球和月亮
// 假設(shè)每個(gè)月都是30天
// 一年12個(gè)月,共是360天
static int day = 200; // day的變化:從0到359
void myDisplay(void)
{
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(75, 1, 1, 400000000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);
// 繪制紅色的“太陽(yáng)”
glColor3f(1.0f, 0.0f, 0.0f);
glutSolidSphere(69600000, 20, 20);
// 繪制藍(lán)色的“地球”
glColor3f(0.0f, 0.0f, 1.0f);
glRotatef(day/360.0*360.0, 0.0f, 0.0f, -1.0f);
glTranslatef(150000000, 0.0f, 0.0f);
glutSolidSphere(15945000, 20, 20);
// 繪制黃色的“月亮”
glColor3f(1.0f, 1.0f, 0.0f);
glRotatef(day/30.0*360.0 - day/360.0*360.0, 0.0f, 0.0f, -1.0f);
glTranslatef(38000000, 0.0f, 0.0f);
glutSolidSphere(4345000, 20, 20);
glFlush();
}
--------------------------------------------------------------------------------
試修改day的值,看看畫(huà)面有何變化。
小結(jié):本課開(kāi)始,我們正式進(jìn)入了三維的OpenGL世界。
OpenGL通過(guò)矩陣變換來(lái)把三維物體轉(zhuǎn)變?yōu)槎S圖象,進(jìn)而在屏幕上顯示出來(lái)。為了指定當(dāng)前操作的是何種矩陣,我們使用了函數(shù)glMatrixMode。
我們可以移動(dòng)、旋轉(zhuǎn)觀察點(diǎn)或者移動(dòng)、旋轉(zhuǎn)物體,使用的函數(shù)是glTranslate*和glRotate*。
我們可以縮放物體,使用的函數(shù)是glScale*。
我們可以定義可視空間,這個(gè)空間可以是“正投影”的(使用glOrtho或gluOrtho2D),也可以是“透視投影”的(使用glFrustum或gluPerspective)。
我們可以定義繪制到窗口的范圍,使用的函數(shù)是glViewport。
矩陣有自己的“堆棧”,方便進(jìn)行保存和恢復(fù)。這在繪制復(fù)雜圖形時(shí)很有幫助。使用的函數(shù)是glPushMatrix和glPopMatrix。
好了,艱苦的一課終于完畢。我知道,本課的內(nèi)容十分枯燥,就連最后的例子也是。但我也沒(méi)有更好的辦法了,希望大家能堅(jiān)持過(guò)去。不必?fù)?dān)心,熟悉本課內(nèi)容后,以后的一段時(shí)間內(nèi),都會(huì)是比較輕松愉快的了。
===================== 第五課 完 =====================
|