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