王 風(fēng)
(無錫職業(yè)技能鑒定指導(dǎo)中心,江蘇無錫 214000)
OpenGL的英文全稱是“Open Graphics Library”,顧名思義,OpenGL便是“開放的圖形程序接口”,是一套三維圖形(是指將用數(shù)據(jù)描述的三維空間通過計(jì)算轉(zhuǎn)換成二維圖像并顯示或打印出來的技術(shù))處理庫,也是該領(lǐng)域的工業(yè)標(biāo)準(zhǔn)??梢园袿penGL看成一條生產(chǎn)流水線,原料是場景、物體的頂點(diǎn)、表面細(xì)節(jié)等信息,產(chǎn)品是看起來有三維感覺的平面位圖。在OpenGL中是以面邊界(BREP)模型來描述物體的,或者說是使用多邊形造型系統(tǒng)。在OpenGL中每個(gè)物體都只是一組平面,這組平面記錄了該物體的表面。顯然這些平面越小畫出不斷的物體越逼真。在OpenGL中需要用戶提供圍繞平面邊緣的線段的頂點(diǎn)參數(shù)、平面內(nèi)圖案的位圖兩組信息,術(shù)語稱為Vertex(頂點(diǎn))、Texture(紋理)。
如果表示物體的平面小到一個(gè)像素,那么得到圖像將會(huì)是極為真實(shí)的,然而這意味著天文數(shù)字般的計(jì)算任務(wù)。所以O(shè)penGL三維圖形程序設(shè)計(jì)很大程度上是在質(zhì)量與速度之間折中的藝術(shù)。OpenGL流水線工作的原理是客戶端(即咱們寫的圖形應(yīng)用程序包括命令和數(shù)據(jù));服務(wù)器(流水線);幀緩沖區(qū)。
客戶在程序中所發(fā)的命令和數(shù)據(jù)并不是立即傳到流水線執(zhí)行的,流水線在收到glFlush/glFinish/SwapBuffers之類的命令之后才會(huì)真正開始執(zhí)行命令加工數(shù)據(jù),這些命令都有說明某一階段處理相關(guān)的指令數(shù)據(jù)改送已經(jīng)結(jié)束請求獲得所需結(jié)果的含義。這就是所謂的客戶/服務(wù)器模式。
幾何頂點(diǎn)數(shù)據(jù)包括模型的頂點(diǎn)集、線集、多邊形集,這些數(shù)據(jù)經(jīng)過流程圖的上部,包括運(yùn)算器、逐個(gè)頂點(diǎn)操作等;圖像數(shù)據(jù)包括象素集、影像集、位圖集等,圖像象素?cái)?shù)據(jù)的處理方式與幾何頂點(diǎn)數(shù)據(jù)的處理方式是不同的,但它們都經(jīng)過光柵化、逐個(gè)片元(Fragment)處理直至把最后的光柵數(shù)據(jù)寫入幀緩沖器。
在OpenGL中的所有數(shù)據(jù)包括幾何頂點(diǎn)數(shù)據(jù)和象素?cái)?shù)據(jù)利用顯示列表技術(shù)被存儲(chǔ)在顯示列表中或者立即可以得到處理。OpenGL要求把所有的幾何圖形單元都用頂點(diǎn)來描述,這樣運(yùn)算器和逐個(gè)頂點(diǎn)計(jì)算操作都可以針對每個(gè)頂點(diǎn)進(jìn)行計(jì)算和操作,然后進(jìn)行光柵化形成圖形碎片;對于象素?cái)?shù)據(jù),象素操作結(jié)果被存儲(chǔ)在紋理組裝用的內(nèi)存中,再像幾何頂點(diǎn)操作一樣光柵化形成圖形片元。整個(gè)流程操作的最后,圖形片元都要進(jìn)行一系列的逐個(gè)片元操作,這樣最后的象素值送入幀緩沖器實(shí)現(xiàn)圖形的顯示。
根據(jù)這個(gè)流程,可以歸納出在OpenGL中進(jìn)行主要的圖形操作直至在計(jì)算機(jī)屏幕上渲染繪制出三維圖形景觀的基本步驟:
1)根據(jù)基本圖形單元建立景物模型,并且對所建立的模型進(jìn)行數(shù)學(xué)描述(OpenGL中把:點(diǎn)、線、多邊形、圖像和位圖都作為基本圖形單元)。
2)把景物模型放在三維空間中的合適的位置,并且設(shè)置視點(diǎn)(viewpoint)以觀察所感興趣的景觀。
3)計(jì)算模型中所有物體的色彩,其中的色彩根據(jù)應(yīng)用要求來確定,同時(shí)確定光照條件、紋理粘貼方式等。
4)把景物模型的數(shù)學(xué)描述及其色彩信息轉(zhuǎn)換至計(jì)算機(jī)屏幕上的象素,這個(gè)過程也就是光柵化(rasterization)。
在這些步驟的執(zhí)行過程中,OpenGL可能執(zhí)行其他的一些操作,例如自動(dòng)消隱處理等。另外,景物光柵化之后被送入幀緩沖器之前還可以根據(jù)需要對像素?cái)?shù)據(jù)進(jìn)行操作。
OpenGl是一套3D繪圖函數(shù)庫,在三維空間繪圖是他的功能,可惜人和電腦的溝通方式還不夠發(fā)達(dá),不然藝術(shù)家就可以通過一些程序用意識(shí)憑空在空間里作畫,而現(xiàn)在仍然只能通過傳統(tǒng)的笛卡爾坐標(biāo)系這種一板一眼的方式將人的腦中的圖形量化,和數(shù)字化。在這一點(diǎn)上OpenGL的對空間事物的體現(xiàn)方式無疑是完善的,在OpenGL中,也有一個(gè)內(nèi)建的坐標(biāo)系,就如人的視野有限一樣,OpenGL同樣無法提供無限空間里的所有信息,但是可以把OpenGL提供的空間理解成一個(gè)盒子而正通過盒子唯一的開口向內(nèi)觀察,這一開口就是電腦屏幕,如果把這個(gè)盒子看成是一個(gè)(-2,-2,-2),(2,2,2)的立方體的話,屏幕就是在z=-2上從(2,2)到(-2,-2)的這片區(qū)域,事實(shí)上OpenGL也是這樣做的,它在所提供的空間中圈定的也是這樣一個(gè)坐標(biāo)系。初始情況下在屏幕上水平向左的為X軸正方向,豎直向上的為Y軸正方向,而Z軸則垂直于屏幕向外與屏幕平面的焦點(diǎn)為屏幕中心,坐標(biāo)則為(0,0,-2)。而人在屏幕上看到的圖形是顯示器上顯示的就是從z為負(fù)無窮處射來的平行光在Z=-2處的屏幕上所成的像。
理解了OpenGL的這個(gè)坐標(biāo)體系就可以開始里面作畫了,在OpenGL中畫點(diǎn)和線是由同一組函數(shù)完成的基本結(jié)構(gòu)是這樣的
這段代碼的作用是在空間中以模式mode_parameter和顏色(R G B) 在坐標(biāo)(x1,y1,0.0)和(x2,y2,z2)處畫兩個(gè)點(diǎn),當(dāng)然你最終看到的是不是點(diǎn)還要取決于你的mode_parameter。在glBegin()與glEnd()之間是通過glVertex的一系列函數(shù)來定義一組頂點(diǎn)然后根據(jù)mode_parameter將這些點(diǎn)連接畫線,或是以孤立點(diǎn)的形式畫出,mode_parameter有以下幾種:
比較常用的是GL_POINTS畫點(diǎn),也就是說程序中以glVertex定義的點(diǎn)將被畫在屏幕上,GL_LINES畫線,定義的每兩個(gè)點(diǎn)將被連接起來變成一條直線,共N/2條。
GL_LINE_STRIP //將所有的點(diǎn)連接變成一條折線
GL_LINE_LOOP //將 GL_LINE_STRIP 畫成的折線頭尾相連,形成閉合圖形
GL_TRIANGLES //定義的每三個(gè)點(diǎn)將被連接起來涂成一個(gè)三角形,共N/3個(gè)
GL_QUADS //定義的每四個(gè)點(diǎn)將被連接起來涂成一個(gè)四邊形,共N/4個(gè)
在這些模式中到底是哪幾個(gè)點(diǎn)變成一組形成直線,或多邊形取決于glvertex定義點(diǎn)的次序,至此已經(jīng)能夠畫出絕大多數(shù)直線形,但是曲線呢,數(shù)學(xué)上曲線是被定義為N個(gè)點(diǎn)連成的折線,當(dāng)N趨向無窮大的情況。而在計(jì)算機(jī)中只要畫出N足夠大的折線就可以認(rèn)為是曲線了。
在OpenGL有內(nèi)建的坐標(biāo)系,事實(shí)上OpenGL有兩套坐標(biāo)系,眼睛坐標(biāo)eye coordinate system簡稱(ECS)和物體坐標(biāo)object coordinate system(簡稱OCS),而用來繪圖的只是OCS。
兩個(gè)坐標(biāo)系中ECS可以看成是一個(gè)現(xiàn)實(shí)存在的,基本不變的全局坐標(biāo)系,而OCS則可以看成是用戶自定義的坐標(biāo)系,可以將這個(gè)坐標(biāo)系任意的平移與縮放。在初始情況下他和ECS是重合的,也可以通過glLoadIdentity()強(qiáng)制復(fù)位,這樣可以給繪圖帶來極大的方便。這里有一點(diǎn)是要值得注意的是在使用一個(gè)函數(shù)時(shí)需要弄清它是使用什么坐標(biāo)系的,剛剛用到的glVertex系列函數(shù)都是用的OCS。
在圖形的旋轉(zhuǎn)過程中整個(gè)圖形都會(huì)有一定程度的閃爍現(xiàn)象,顯得圖形的過渡極不平滑,這當(dāng)然不是所要的效果,幸好OpenGL支持一個(gè)稱為雙緩存的技術(shù),可以有效的解決這個(gè)問題。在電腦中,屏幕中顯示的東西都會(huì)被放在一個(gè)稱為顯示緩存的地方,在通常情況下只有一個(gè)這樣的緩沖區(qū),也就是單緩沖,在單緩沖中任何繪圖的過程都會(huì)被顯示在屏幕中,這也就是為什么會(huì)看到閃爍,而所謂雙緩沖就是再這個(gè)顯示的緩沖區(qū)之外再建立一個(gè)不顯示的緩沖區(qū),所有的繪圖都將在這個(gè)不顯示的緩沖區(qū)中進(jìn)行,只有當(dāng)一幀都繪制完了之后才會(huì)被拷貝到真正的現(xiàn)實(shí)緩沖區(qū)顯示出來,這樣中間過程對于最終用戶就是不可見的了,那即使是速度比較慢也只會(huì)出現(xiàn)停頓而不會(huì)有閃爍的現(xiàn)象出現(xiàn)。
在OpenGL中可以通過glut庫中的函數(shù)void glutSwapBuffers(void),來實(shí)現(xiàn)從非顯示緩沖區(qū)到顯示緩沖區(qū)的復(fù)制。當(dāng)然在使用雙緩沖之前也要在glutInitDisplayMode設(shè)定參數(shù)的時(shí)候選擇GLUT_DOUBLE,而不是之前用的GLUT_SINGLE相信這兩個(gè)參數(shù)的意思也應(yīng)該很明白了。
在解決了閃爍的問題之后,來解決另一個(gè)問題。首先在窗口中畫兩個(gè)交叉的分別位于ZOX和ZOY平面的長方形,并用不同的顏色填充它們,然后讓它們旋轉(zhuǎn)很快,發(fā)現(xiàn)有個(gè)比較嚴(yán)重的問題發(fā)生了,因?yàn)榘l(fā)現(xiàn)OpenGL顯然沒有空間的位置觀念,所以它根本不能分辨物體的前后關(guān)系,當(dāng)然也不能做出適當(dāng)?shù)娘@示,在OpenGL中使用深度緩沖區(qū)來解決這個(gè)問題,在這個(gè)緩沖區(qū)中存放著每個(gè)象素深度信息,當(dāng)有一個(gè)新的象素需要顯示的時(shí)候,可以通過一個(gè)被稱為深度測試的函數(shù)來確定它們的先后關(guān)系,要使用深度緩沖區(qū)有以下幾個(gè)步驟:
1)在函數(shù)glutInitDisplayMode(mode)中調(diào)用GLUT_DEPTH,用來表明要使用深度緩沖區(qū)。
2)使用函數(shù)glEnable(GL_DEPTH_TEST)打開深度測試函數(shù):
void glEnable(GLenum cap);
void glDisable(GLenum cap);
這兩個(gè)函數(shù)屬于基本狀態(tài)管理函數(shù)用來打開和關(guān)閉一些常用的功能,常用的參數(shù)有以下幾個(gè):
GL_BLEND GL_DEPTH_TEST GL_FOG
GL_LINE_STIPPLE GL_LIGHTING
3)是用函數(shù)void glDepthFunc(GLenum func)來設(shè)置深度測試函數(shù),比較常用的深度測試函數(shù)有GL_LESS和GL_LEQUAL兩者的區(qū)別在于當(dāng)深度相同時(shí)是顯示新的象素還是老的象素。
4)使用下面的函數(shù)來設(shè)置深度緩沖區(qū)的值void glClearDepth(GLclampd depth)
這里的參數(shù)是背景的深度,一般來說都將背景深度設(shè)成最大值1。
5)最后在使用glClear(bits)刷新背景的同時(shí),要將GL_DEPTH_BUFFER_BIT置位,表示在刷新背景顏色的同時(shí)用刷新背景深度。
OpenGL被認(rèn)為是高性能圖形和交互式視景處理的標(biāo)準(zhǔn),可以制作出順暢的3D效果,且性能也是非常出色的。所以在當(dāng)今的三維繪制領(lǐng)域,其占據(jù)了不可動(dòng)搖的位置。我們國家的三維創(chuàng)作發(fā)展起步相當(dāng)晚,必須努力鉆研迎頭趕上。相信只要堅(jiān)持學(xué)習(xí)創(chuàng)作,最終會(huì)在OpenGL領(lǐng)域獲得鮮花和果實(shí)。
[1]喬林,費(fèi)廣正.OpenGL程序設(shè)計(jì)[M].北京:清華大學(xué)出版社,2000.
[2]Mason Woo,Jackie Neider.OpenGL編程權(quán)威指南[M].吳斌,段海波,薛鳳武,譯.北京:中國電力出版社,2001.
[3]龔聲蓉,許承東.計(jì)算機(jī)圖形技術(shù)[M].北京:中國林業(yè)出版社,2006.
[4]Alan H.Watt,3D Computer Graphics[M].Addison-Wesley Educational Publishers Inc;3rd Revised edition,1999.