文/徐鳳雪
(山東師范大學(xué) 山東省濟(jì)南市 250358)
在人們?nèi)粘I畹默F(xiàn)實(shí)世界中,所有的物體都具有三維特性,但是計(jì)算機(jī)只能處理離散的數(shù)據(jù),在屏幕上顯示二維圖形。所以,將三維圖形顯示在二維平面上是一個(gè)復(fù)雜的問題,而唯一能在三維物體和二維數(shù)據(jù)之間建立起關(guān)聯(lián)的就是坐標(biāo)系統(tǒng)。因此,在整個(gè)三維圖形顯示系統(tǒng)中,可以定義不同的坐標(biāo)系統(tǒng),通過一系列三維變換,實(shí)現(xiàn)將三維圖形顯示在二維平面上。為了便于理解,引入照相機(jī)成像的模擬過程,形象地展示出從三維物體到二維數(shù)據(jù)之間所經(jīng)過的各種變換。OpenGL作為一種三維圖形編程接口,為用戶提供了多種簡明的圖形繪制功能,它可以理解為一種狀態(tài)機(jī),通過矩陣堆棧的形式來獲取用戶的輸入并修改自己當(dāng)前的狀態(tài),從而實(shí)現(xiàn)相應(yīng)的變換和輸出。
前面說到,要通過不同的坐標(biāo)系統(tǒng)來將三維物體與二維平面的顯示聯(lián)系起來,需要設(shè)立不同的坐標(biāo)系統(tǒng),將物體的三維空間特性映射到二維平面上,同時(shí)還能保留其三維特點(diǎn),OpenGL為此設(shè)立了包括模型坐標(biāo)系、世界坐標(biāo)系、觀察坐標(biāo)系和成像面坐標(biāo)系、視口坐標(biāo)系在內(nèi)的坐標(biāo)系統(tǒng),用以完成整個(gè)三維圖形的顯示、變換過程。如圖1所示。
(1)世界坐標(biāo)系(X,Y,Z)是要顯示物體所在空間的一個(gè)總的基準(zhǔn)坐標(biāo)系,用于定義其他的坐標(biāo)點(diǎn)及坐標(biāo)系,作為其他坐標(biāo)系之間轉(zhuǎn)換的橋梁,世界坐標(biāo)系是始終保持不變的。
(2)模型坐標(biāo)系(Xm,Ym,Zm)也稱局部坐標(biāo)系,其原點(diǎn)位于三維物體的中心,主要用于描述物體的幾何特性。
(3)觀察坐標(biāo)系(Xv,Yv,Zv)是為了觀察物體投影在二維平面上的成像而設(shè)立的坐標(biāo)系,其坐標(biāo)原點(diǎn)就是視點(diǎn)的位置,相當(dāng)于人們的眼睛,當(dāng)從不同的角度和距離來觀察物體,就會在成像面上形成不同的圖像。觀察坐標(biāo)系作為模型坐標(biāo)系和世界坐標(biāo)系之間轉(zhuǎn)換的橋梁,可以簡化三維物體在投影面成像的數(shù)學(xué)推導(dǎo)和計(jì)算過程。
(4)成像面坐標(biāo)系就是在投影平面上所建立的一個(gè)二維坐標(biāo)系,用于顯示三維物體經(jīng)過投影變換之后的二維圖像信息。
(5)視口坐標(biāo)系也即設(shè)備坐標(biāo)系,是物體最終顯示的屏幕坐標(biāo)系統(tǒng),不同的設(shè)備有不同的顯示。
此外,在設(shè)備屏幕顯示最終圖像之前,還需要進(jìn)行一步規(guī)范化設(shè)備坐標(biāo)系的變換,規(guī)范化設(shè)備坐標(biāo)系也是一個(gè)二維坐標(biāo)系,主要用于規(guī)范不同分辨率的設(shè)備顯示,它獨(dú)立于具體規(guī)格的設(shè)備,坐標(biāo)范圍為0-1。
整個(gè)三維物體的顯示過程,可以用照相機(jī)拍照的形式來模擬生成。OpenGL就是在其內(nèi)部設(shè)定了一個(gè)虛擬攝像機(jī),來模擬物體的顯示過程。
如圖2所示,照相機(jī)拍照與計(jì)算機(jī)生成圖像的步驟如下:
(1)將相機(jī)固定在三腳架上,令其對準(zhǔn)要拍攝的物體,相機(jī)的位置即視點(diǎn)位置——觀察坐標(biāo)系的原點(diǎn),從被攝物體到相機(jī)的方向?yàn)閆軸,手持相機(jī)的正上方為Y軸,最終通過右手定則確定X軸
圖1:三維圖形坐標(biāo)系統(tǒng)
的方向。調(diào)整相機(jī)的位置就是調(diào)整視點(diǎn)的位置,這個(gè)過程對應(yīng)于OpenGL中的視圖變換。
(2)相機(jī)的位置確定好之后,有時(shí)還需要改變物體的位置,這個(gè)過程對應(yīng)于從模型坐標(biāo)系到世界坐標(biāo)系的轉(zhuǎn)化,對應(yīng)于OpenGL中的模型變換。
(3)要想將具體的景物定格在相機(jī)內(nèi)部,需要調(diào)整焦距和相關(guān)內(nèi)參數(shù),然后按下快門,這個(gè)過程對應(yīng)于OpenGL中利用觀察坐標(biāo)系進(jìn)行的投影變換。
(4)照片拍完之后需要沖洗底片,也就是確定照片的尺寸,即分辨率。這個(gè)過程在OpenGL中稱為視口變換,用于設(shè)定最終顯示圖形的窗口在二維屏幕上的大小和位置。
OpenGL中三維物體呈現(xiàn)在二維平面上的過程變換可以分為如圖3的幾步。
OpenGL中各種變換函數(shù)的功能是通過矩陣相乘的形式來實(shí)現(xiàn)的,每當(dāng)OpenGL調(diào)用一個(gè)變換函數(shù),就會生成一個(gè)4×4階的矩陣,該變換矩陣與矩陣堆棧中現(xiàn)存矩陣相乘,形成一個(gè)新的現(xiàn)存矩陣,當(dāng)該新的現(xiàn)存矩陣與所繪制的某一頂點(diǎn)相乘時(shí),該矩陣所對應(yīng)的變換功能,就會作用在要繪制的頂點(diǎn)上,從而實(shí)現(xiàn)相應(yīng)的變換。
模型變換對應(yīng)于拍照過程中設(shè)定物體的位置,也就是將物體放在世界坐標(biāo)系中的某一合適坐標(biāo)點(diǎn)。OpenGL提供了三個(gè)接口函數(shù)來實(shí)現(xiàn)此變換操作,它們?yōu)間lTranslatef、glRotatef和glScalef,分別對應(yīng)于平移、旋轉(zhuǎn)和縮放變換。這三個(gè)變換函數(shù)調(diào)用后,便會生成一個(gè)變換矩陣,該矩陣與當(dāng)前矩陣相乘,作用于要繪制的圖形,使得圖形在世界坐標(biāo)系中的位置或形狀發(fā)生改變。
圖2:照相機(jī)模擬成像過程
需要注意的是,在OpenGL中,若未指定物體的具體位置,即不做任何變換時(shí),物體本身的模型坐標(biāo)系和世界坐標(biāo)系是完全重合的。因此,當(dāng)我們對三維物體施加幾何變換操作時(shí),可以有兩種理解方式。第一種理解是,可以看做整個(gè)場景中不存在模型坐標(biāo)系,繪制圖形時(shí)設(shè)定的頂點(diǎn)坐標(biāo)直接在世界坐標(biāo)系中,三種模型變換也是直接作用在物體頂點(diǎn)上,從而實(shí)現(xiàn)物體在整個(gè)場景(世界坐標(biāo)系)中的位置變換。第二種理解是,繪制模型時(shí)給出的頂點(diǎn)坐標(biāo)都是針對物體本身的模型坐標(biāo)系而言,而上述三種變換是作用于模型坐標(biāo)系與世界坐標(biāo)系之間的變換,即對整個(gè)模型坐標(biāo)系在世界坐標(biāo)系中進(jìn)行了平移、旋轉(zhuǎn)和縮放,而頂點(diǎn)坐標(biāo)始終在模型坐標(biāo)系中。為了更好地將三維圖形的顯示過程理解清楚,大多數(shù)情況下采用第二種理解方式。
視圖變換對應(yīng)于拍照過程中設(shè)置相機(jī)的擺放位置及拍攝方向,即建立觀察坐標(biāo)系。在OpenGL中設(shè)立觀察坐標(biāo)系的函數(shù)是gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble atx,GLdouble aty,GLdouble atz,GLdouble upx,GLdouble upy,GLdoubleupz)。其中,(eyex,eyey,eyez)確定視點(diǎn)的位置,(atx,aty,atz)確定拍攝物體的位置,從點(diǎn)(atx,aty,atz)到點(diǎn)(eyex,eyey,eyez)為觀察坐標(biāo)系z軸的方向,(upx,upy,upz)為觀察坐標(biāo)系y軸的三個(gè)分量,最終通過右手定則確定x軸的方向。
根據(jù)物理學(xué)基本原理:運(yùn)動(dòng)是相對的,可以得到在整個(gè)拍攝過程中,三維物體和視點(diǎn)的位置也是一個(gè)相對的關(guān)系,對于同樣的顯示效果,既可以通過模型變換來實(shí)現(xiàn),也可以通過視圖變換來實(shí)現(xiàn)。比如,要想讓物體與視點(diǎn)的距離減少3個(gè)單位,可以保持物體位置不變,使用gluLookat(0,0,-3,0,0,0,0,1,0)函數(shù)使得相機(jī)靠近物體,也可以保持相機(jī)不動(dòng),調(diào)用gltranslate(0,0,3)函數(shù)使得物體向相機(jī)移動(dòng)三個(gè)單位距離,最終的顯示效果是一樣的。然而,為了物理意義上的清晰以及程序的可讀性,OpenGL中將模型變換和視圖變換區(qū)分開來,當(dāng)需要改變物體的幾何屬性時(shí),使用模型變換,當(dāng)需要指定相機(jī)的位置和方向時(shí),則采用視圖變換。此外,需要注意的是,OpenGL在其內(nèi)部把上述兩種變換合并為一個(gè):模型視圖變換,且使用一個(gè)模型視圖矩陣來完成。在圖形繪制過程中,無論是進(jìn)行模型變換還是視圖變換,都要先調(diào)用glMatrixMode(GL_MODEVIEW)函數(shù),將OpenGL當(dāng)前的矩陣堆棧設(shè)置為模型視圖狀態(tài),再將相對應(yīng)的變化函數(shù)乘到當(dāng)前的模型視圖矩陣上。還應(yīng)注意的是,OpenGL的矩陣相乘都是采用右乘的形式,因此實(shí)際呈現(xiàn)的變換順序與代碼中寫的變換順序是相反的。因此,在編寫程序時(shí),要考慮好各種變換之間的前后順序與最終呈現(xiàn)結(jié)果之間的關(guān)系,以避免相關(guān)問題的混淆。
圖3:三維圖形顯示流程圖
投影變換的主要作用就是在人眼和三維物體之間建立一個(gè)視截體,將位于視截體內(nèi)部的物體投影到成像面上,而舍棄視截體外的部分。視截體中離視點(diǎn)近的面稱為近裁剪面,遠(yuǎn)的為遠(yuǎn)裁剪面。通常來講,投影方式分為平行投影和透視投影兩種。
3.3.1 平行投影
在OpenGL中一般使用 glOrtho()函數(shù)來設(shè)定三維平行投影模式。該函數(shù)調(diào)用后,會創(chuàng)建一個(gè)長方體形狀的視截體,其中近裁剪面和遠(yuǎn)裁剪面都是一個(gè)矩形,各個(gè)函數(shù)參數(shù)定義了整個(gè)視截體的位置和形狀。調(diào)用該函數(shù)后,OpenGL就會自動(dòng)創(chuàng)建一個(gè)平行投影矩陣,該矩陣與當(dāng)前的投影矩陣相乘,作用于要繪制的物體坐標(biāo),產(chǎn)生相應(yīng)的投影效果。
3.3.2 透視投影
OpenGL提供了兩個(gè)透視變換函數(shù)。第一個(gè)為glFrustum (GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble near,GLdouble far)。該函數(shù)創(chuàng)建了一個(gè)近裁剪面小于遠(yuǎn)裁剪面的四棱臺形狀的視截體,其前四個(gè)參數(shù)只確定了近裁剪面的大小,near確定了近裁剪面離視點(diǎn)的距離,OpenGL內(nèi)部通過透視原理和最后一個(gè)參數(shù)far自動(dòng)計(jì)算出遠(yuǎn)裁剪面的具體位置和形狀。同理,該函數(shù)調(diào)用后,會自動(dòng)生成一個(gè)投影變換矩陣用于三維物體的透視投影。
OpenGL還提供了另外一個(gè)透視投影函數(shù)gluPerspective (GLdouble fovy,GLdouble aspect,GLdouble near,GLdouble far)。此函數(shù)也是創(chuàng)建了一個(gè)四棱臺形狀的視截體,但其中的參數(shù)與上一個(gè)函數(shù)不同。fovy確定了Y-Z平面的觀察角度,aspect為投影平面的寬高比,near和far則表示近、遠(yuǎn)裁剪面到視點(diǎn)的距離,它們的值總是大于0。該函數(shù)完全定義了整個(gè)視截體的形狀和位置,比起glFrustum函數(shù)使用起來更加理想。與模型視圖變換類似,在調(diào)用具體的投影變換函數(shù)之前,也要通過glMatrixMode(GL_PROJECTION)函數(shù)將當(dāng)前的矩陣堆棧指定為投影變換狀態(tài)。
視口變換直接決定了所繪制的圖形在屏幕上最終顯示的大小和位置。實(shí)際上,視口就是最終人們在二維屏幕上所觀察到的圖形顯示窗口,被觀察物體經(jīng)過前面的一系列變換后,其各頂點(diǎn)坐標(biāo)已經(jīng)轉(zhuǎn)換到規(guī)范化設(shè)備坐標(biāo)系,這一步由OpenGL自動(dòng)完成,后面再由規(guī)范化設(shè)備坐標(biāo)轉(zhuǎn)換到視口坐標(biāo)系中的過程就是視口變換。OpenGL提供了視口變換函數(shù)glViewport,其前兩個(gè)參數(shù)定義了二維顯示窗口的左下角在屏幕上坐標(biāo)位置,后兩個(gè)參數(shù)則定義了窗口的寬和高。此函數(shù)調(diào)用后,便可確定最終顯示圖形的大小和位置。在視口變換中需要注意的是,設(shè)置視口的寬度和高度時(shí),應(yīng)當(dāng)使其縱橫比和投影變換中的縱橫比保持一致,這樣才能保證顯示出來的圖像不會壓扁或者拉伸。并且,在窗口的大小發(fā)生改變時(shí),也應(yīng)當(dāng)及時(shí)調(diào)整視口的大小,否則繪制的圖形會隨著窗口形狀的變化而變化。
三維圖形的顯示過程是整個(gè)圖形渲染管線的核心,其中的各種變換實(shí)際上包含著很多復(fù)雜的數(shù)學(xué)原理和推導(dǎo)計(jì)算過程,編程開發(fā)人員只有正確理解整個(gè)三維變換的原理和過程,才能更加熟練和高效的使用OpenGL提供的各個(gè)接口函數(shù)。