張清揚(yáng) 王爽 黃今慧
北京工商大學(xué)計算機(jī)與信息工程學(xué)院,中國·北京 100048
為滿足目前對虛擬場景再現(xiàn)的真實(shí)性需求,同時優(yōu)化觀察者的視覺感受,利用OpenGL 對整個三維模型進(jìn)行渲染,生成真彩的三維虛擬場景。由繪制簡單的3D 物體開始,逐步完善,到最后生成動態(tài)場景的過程中,將信息完整、準(zhǔn)確地轉(zhuǎn)化為虛擬場景,使參觀者能身臨其境感受整體布局及各個細(xì)微項。同時OpenGL 與C++相結(jié)合,增加了虛擬場景的交互性,增強(qiáng)體驗(yàn)感,讓參觀者可以從不同角度來觀察和操控場景,而有效的碰撞檢測和處理讓虛擬場景更加真實(shí)。
OpenGL;glut;天空盒模型;碰撞檢測和處理;包圍盒算法
OpenGL,是一套底層圖形庫中的三維圖形處理庫,即開放性圖形庫Open Graphics Library,針對三維圖形可視化進(jìn)行展開分析,是解決三維模型繪制、顯示和交互問題的圖形接口技術(shù)。它功能強(qiáng)大、調(diào)用方便,是一個跨編程語言、跨平臺的編程接口。OpenGL 最初由SGI 公司開發(fā),作為圖形工作站的一個強(qiáng)大的3d 圖形機(jī)制或圖形標(biāo)準(zhǔn),當(dāng)初SGI 公司為其圖形工作站開發(fā)的IRIS GL 是OpenGL 的起源,隨著跨平臺移植,最終發(fā)展成為了OpenGL。
OpenGL 應(yīng)用廣泛,在教學(xué)應(yīng)用方面,它是高校交互式實(shí)驗(yàn)教學(xué)系統(tǒng)設(shè)計的基礎(chǔ)。從最初的模型繪制,到最終的交互技術(shù),都體現(xiàn)在了系統(tǒng)的各個模塊中。模型認(rèn)知模塊主要應(yīng)用了模型繪制和模型觀察功能,建筑漫游模塊則應(yīng)用了光照、材質(zhì)、紋理映射等功能,依托 OpenGL 完備的功能特性,才能構(gòu)建起交互式試驗(yàn)系統(tǒng)。
而作為一種三維圖形的開發(fā)標(biāo)準(zhǔn),OpenGL 實(shí)現(xiàn)了高性能的三維圖形開發(fā)。由OpenGL 得到的圖形質(zhì)量好,性能高,穩(wěn)定性好,著色方便;Open GL 可以完成對虛擬場景中對象的上色和渲染工作,讓繪制的模型接近現(xiàn)實(shí)生活,具有更豐富的細(xì)節(jié),得到尤為逼真的視覺效果。OpenGL 最基礎(chǔ)的功能是模型繪制,通過基本繪圖函數(shù),進(jìn)行二次封裝,得到新的繪制函數(shù),再由OpenGL 的矩陣變換等操作,產(chǎn)生三維模型供實(shí)際應(yīng)用,簡單的如立方體、棱錐等;模型建立完成后,需要對模型進(jìn)行觀察,如設(shè)置視點(diǎn)、變換坐標(biāo)、旋轉(zhuǎn)模型等操作;接下來要為模型指定顏色,OpenGL 有RGBA 模式和顏色索引(Color index)兩種物體著色方式。不同的顏色模式,其組合顏色的方式有所區(qū)別?;咀儞Q和投影變換則是實(shí)現(xiàn)變換的兩種主要方式。將OpenGL 與virtual studio 相結(jié)合,其變換的物體都是基于前一物體,在加上OpenGL 提供的一系列圖形轉(zhuǎn)換函數(shù),實(shí)現(xiàn)了基本變換。使用這種方法有利于提高三維圖形的顯示速度,與此同時在降低運(yùn)行時間方面有一定的幫助。模型要產(chǎn)生陰影等效果,就用到了投影變換,此時,將借助光照的應(yīng)用,OpenGL 的光照模型包括疊加各個獨(dú)立的部分,即鏡面光、環(huán)境光以及輻射光等光源,疊加之后,光照部分將有較快的速度,同時將看到更為明顯的效果。而根據(jù)不同的光源,可以模擬多樣化的光照環(huán)境;反走樣和霧化可以解決圖像的鋸齒現(xiàn)象和遠(yuǎn)近層次關(guān)系,使之貼近真實(shí);模型的細(xì)節(jié)層次,可以借助紋理映射技術(shù)來完善,通過鏈接真實(shí)圖片,使三維景觀更加逼真;從而動態(tài)流暢地顯示三維模型。即虛擬場景中的模型繪制是實(shí)現(xiàn)場景的基礎(chǔ)。
基于以上所述Open GL 豐富全面的功能,我們希望能將其應(yīng)用于樓盤虛擬場景的實(shí)現(xiàn)中[1]。目前房地產(chǎn)行業(yè)快速發(fā)展,而大多房地產(chǎn)商使用的平面效果圖和沙盤形勢單一,少數(shù)使用直升機(jī)參觀又費(fèi)時耗力成本大,為實(shí)現(xiàn)既能為參觀者提供漫步于場景之中身臨其境的真實(shí)感,又能全方位準(zhǔn)確地觀看樓房、小區(qū)設(shè)施及周圍環(huán)境,讓參觀者經(jīng)濟(jì)有效快速地采集所需信息的效果,針對這些問題,本研究通過利用Open GL構(gòu)筑虛擬場景,進(jìn)行再現(xiàn)樓盤虛擬場景的探討。
實(shí)驗(yàn)開始前,先對實(shí)驗(yàn)環(huán)境進(jìn)行配置。實(shí)驗(yàn)設(shè)備使用visual studio 2017,首先新建一個visual C++空項目,然后在項目中選擇管理Nuget 程序包,再瀏覽glut 并選擇安裝nupengl.core 包來完成配置。專為構(gòu)建中小型OpenGL 程序的應(yīng)用工具包:glut,是一個和窗口系統(tǒng)無關(guān)的軟件包,英文全稱為OpenGL Utility Toolkits,由Mark Kilgard 任職于SGI 公司時編寫的。本次實(shí)驗(yàn)中,采用glut 對虛擬場景進(jìn)行繪制。
首先,需要對樓市沙盤的信息進(jìn)行采集。樓市的沙盤模型通常是按照一定的比例(如1000:1 比例尺)對住宅小區(qū)中的建筑模型進(jìn)行縮放,包括地形、綠植、河流湖泊、樓棟等信息。通過聯(lián)系相應(yīng)的房地產(chǎn)開發(fā)商,或?qū)鞘猩潮P進(jìn)行直接拍照取樣,獲得相關(guān)的樓盤信息,即各個建筑模型的長寬高比例、模型與模型之間的相對位置關(guān)系等。對于本次實(shí)驗(yàn),我們還希望能讓用戶可以進(jìn)入樓棟內(nèi)部,觀察樓棟的內(nèi)部大小和結(jié)構(gòu)以及設(shè)計,因此將對樓盤室內(nèi)信息進(jìn)行采集。有了以上樓盤信息,我們就可以借助OpenGL 來繪制樓盤的虛擬場景,接下來是針對具體實(shí)現(xiàn)虛擬場景部分的闡述。
OpenGL 提供了點(diǎn)、線、面的基本繪制函數(shù),而在虛擬場景繪制中需要用到很多復(fù)雜的幾何體的繪制函數(shù),于是通過基本繪圖函數(shù),進(jìn)行二次封裝,得到新的繪制函數(shù),再利用OpenGL 提供的矩陣變換等一系列功能操作,產(chǎn)生具有一定幾何外觀的三維模型以供實(shí)際應(yīng)用。現(xiàn)實(shí)中的建筑物大都可以看作長方體的堆疊,因而在進(jìn)行場景構(gòu)建前先對長方體進(jìn)行封裝,函數(shù)原型如下:
在函數(shù)體中調(diào)用填充四邊形函數(shù)glBegin(GL_QUADS);glEnd();,并使用glVertex3f();繪點(diǎn)函數(shù)來順序指定長方體的各個點(diǎn)坐標(biāo),同時可以用glColor3f();函數(shù)對該物體進(jìn)行著色。考慮到對文件容量和運(yùn)行效率的控制,對于較為復(fù)雜的模型,需要減少繪制不必要的面,所以需要對模型構(gòu)建和函數(shù)實(shí)現(xiàn)兩方面進(jìn)行優(yōu)化。
在進(jìn)行場景構(gòu)建前,一方面,應(yīng)考慮到虛擬場景中除主要繪制物體以外的環(huán)境,即場景中除模型以外的要素,如天空、場景外部、地形等。其中,地形模型對虛擬場景的真實(shí)性有重要的影響,而本次實(shí)驗(yàn)所選取的樓盤實(shí)景多為平坦的地形,較為簡單,所以在實(shí)驗(yàn)中將地形平面化,用尺寸略大于場景的矩形平面代替地形,并進(jìn)行紋理填充。另一方面,在實(shí)驗(yàn)中使用天空盒來增加虛擬場景的真實(shí)性,天空盒的實(shí)現(xiàn)思路是在場景中創(chuàng)建一個采用立方體貼圖(cubemap)進(jìn)行紋理采樣的立方體,并始終讓該立方體置于場景的外部,使觀測者對天空產(chǎn)生觸不可及的感覺。其中過程為:首先創(chuàng)建cubemap,立方體貼圖包含6 個2D 紋理,每一個2D 紋理對應(yīng)立方體的一個面。接著通過SOIL 創(chuàng)建一個cubemap,將其傳入shader,在著色器接收到紋理值后,再將紋理繪制出即可。
創(chuàng)建cubemap:
虛擬場景中的模型繪制是實(shí)現(xiàn)場景的基礎(chǔ),也是系統(tǒng)中相對復(fù)雜的部分,模型的選取將直接影響到虛擬場景的真實(shí)性。針對樓盤中的模型繪制,因?yàn)樵跇潜P中的物體主要是各種建筑設(shè)施,很少有形狀復(fù)雜的物體,模型繪制相對簡單,只需把樓盤小區(qū)中的物體抽象成基本幾何體的組合,并按照一定的比例對尺寸進(jìn)行縮放。最后再使用自定義的三維繪制函數(shù)在場景的指定位置進(jìn)行繪制,就能完成對虛擬場景模型的繪制[2]。
針對樓棟內(nèi)部場景的繪制,要獨(dú)立于之前所述的室外場景的繪制,將樓棟內(nèi)部的場景位置設(shè)定在天空盒外部,讓用戶無法在室外場景中通過鏡頭移動到達(dá)內(nèi)部場景。只有當(dāng)用戶與樓棟模型進(jìn)行交互時,再調(diào)用渲染內(nèi)部場景的函數(shù),以達(dá)到減少程序渲染規(guī)模的效果。在調(diào)用內(nèi)部渲染場景的函數(shù)時,會進(jìn)行鏡頭參數(shù)更改,將鏡頭置于室內(nèi)場景坐標(biāo)內(nèi),同樣當(dāng)離開內(nèi)部場景時釋放資源。在渲染內(nèi)部場景時,同樣按照內(nèi)部比例進(jìn)行縮放,按照天空盒的思路,將鏡頭放在房間的內(nèi)部,用平面的拼接的方法實(shí)現(xiàn)墻面繪制,再適當(dāng)?shù)睦L制一下小物體,如家具和各種擺件。不同于普通的OpenGL,glut 庫中提供了許多特殊模型的繪制函數(shù),我們可以用其來繪制復(fù)雜的小物體。
有了這些函數(shù),室內(nèi)場景中的物件更加豐富,從而給使用者提供更為全面的室內(nèi)參考。
在系統(tǒng)中合理添加光照,也可以豐富虛擬場景。處理光照時,材質(zhì)、光源和光照模型是OpenGL 光照系統(tǒng)的三大部分。材質(zhì)、光源和光照模式都有各自不同特質(zhì)和屬性,而這些可以通過函數(shù)來設(shè)置。可以利用glLight 來設(shè)置的光源屬性,包括光源的類型(環(huán)境光、漫反射光、鏡面光)、光源的位置等。同時glMaterial 還可以設(shè)置材質(zhì)的屬性。OpenGL 用材質(zhì)對光源的rgb 反射率來定義顏色[3]。若光源的顏色為(lr,lg,lb),材質(zhì)的顏色為(mr,mg,mb),則在忽略反射的情況下,真實(shí)呈現(xiàn)的顏色為(lr*mr,lg*mg,lb*mb)。glLightModel 可以設(shè)置光照模型,其中全局環(huán)境光、鏡面反射顏色、近視點(diǎn)或遠(yuǎn)視點(diǎn)、雙面光照的設(shè)置,以及是否和環(huán)境顏色、散射顏色分開都屬于光照模型的設(shè)置。在對光源進(jìn)行操作時,可以用glEnable 開啟光源,glDisable 關(guān)閉光源。
交互是虛擬場景中重要的一環(huán),虛擬場景的交互性即體現(xiàn)為用戶對場景內(nèi)物體的可操控程度和用戶從場景中得到響應(yīng)的自然程度,一個具有交互功能的虛擬場景可以讓使用者可以從不同角度來觀察和操控場景。
OpenGL 為虛擬場景漫游提供了相關(guān)函數(shù),在實(shí)現(xiàn)時主要使用gluLookAt 函數(shù)對鏡頭進(jìn)行操控,該函數(shù)的函數(shù)原型如下:
該函數(shù)定義了一個視圖矩陣,并與當(dāng)前矩陣相乘,其中的第一組參數(shù)eyex,eyey,eyez 代表著鏡頭在世界坐標(biāo)的位置,第二組參數(shù)centerx,centery,centerz 代表著鏡頭對準(zhǔn)的物體在世界坐標(biāo)中的位置,第三組參數(shù)upx,upy,upz 代表著鏡頭向上的方向在世界坐標(biāo)中的位置。如果沒有調(diào)用過gluLookAt 函數(shù),則默認(rèn)情況下鏡頭的位置為世界坐標(biāo)的原點(diǎn),且指向z 軸的負(fù)方向,朝上向量為(0,1,0)。通過使用gluLookAt 函數(shù),我們可以模擬人眼在虛擬場景中的情況,借由函數(shù)參數(shù)的改變,實(shí)現(xiàn)了場景中的物體相對于觀察者發(fā)生的變化,從而實(shí)現(xiàn)了虛擬場景中的移動。
鏡頭的移動和轉(zhuǎn)向應(yīng)由使用者的操控而發(fā)生響應(yīng),在實(shí)驗(yàn)中采用鍵盤交互的方式來實(shí)現(xiàn)。通過使用鍵盤移動鏡頭,用向左向右鍵來控制鏡頭在xoz 坐標(biāo)平面圍繞y 軸進(jìn)行旋轉(zhuǎn),向上向下鍵來控制鏡頭在當(dāng)前朝向下的前進(jìn)和后退,具體的實(shí)現(xiàn)方法如下[4]。首先需要聲明一些全局變量用來保存鏡頭的參數(shù),其中包括鏡頭的位置和鏡頭指向目標(biāo)方向的向量,同時還需要保存鏡頭的角度:
接著處理箭頭鍵,當(dāng)使用者按下左右鍵時,角度變量angle 也會隨之改變。隨著角度值的改變,程序?qū)⒅匦掠嬎阋暰€向量的lx 和lz 相應(yīng)的值。由于鏡頭的位置只在xoz 坐標(biāo)平面上移動,所以不需要改變視覺向量ly 的值,計算角度值和視線向量的公式如下:
此時當(dāng)更新lx 和lz 時,鏡頭的位置不會發(fā)生變化,變化的只有鏡頭指向物體的坐標(biāo)。當(dāng)移動鏡頭時,下一次的鏡頭位置需要沿著視線向量。為了達(dá)到這樣的效果,需要在按下向上鍵或向下鍵時,加一或減一個粒度的視線向量到當(dāng)前位置,例如在移動鏡頭向前時的計算公式如下:
此處的粒度可以理解為鏡頭移動的步幅,如果粒度值保持在一個常量,速率就會維持在一個常量范圍內(nèi),增加粒度的值可以使速率更快,即每一幀移動的更遠(yuǎn)。
GLUT 庫可以讓程序自動監(jiān)測到鍵盤輸入,包括普通按鍵和特殊按鍵,并且提供兩個函數(shù)來為鍵盤事件注冊回調(diào)函數(shù),實(shí)驗(yàn)中操控鏡頭時使用的箭頭鍵屬于特殊按鍵,所以應(yīng)調(diào)用函數(shù)glutSpecialFunc進(jìn)行注冊。鍵盤事件的處理函數(shù)如下:
其中的deltaMove 表示移動的增量,deltaAngle 表示角度變化的增量。
當(dāng)用戶想要以俯視的角度觀察場景時,可以通過按下鍵盤1 鍵來讓鏡頭處于場景上方并俯視場景。實(shí)現(xiàn)的方法是通過改變gluLookAt 函數(shù)參數(shù)來實(shí)現(xiàn),將gluLookAt 函數(shù)中的eyey 和centery 兩個參數(shù)用全局變量代替,當(dāng)鍵盤1 鍵按下事件發(fā)生時,通過改變?nèi)肿兞康闹?,將鏡頭y 坐標(biāo)增加,使鏡頭置于場景上空,并將鏡頭指向物體的y 坐標(biāo)減小,從而達(dá)到俯視的效果。在俯視場景時把移動增量deltaMove 的值設(shè)置為0,阻止使用者在俯視時移動,防止在落回地面時鏡頭位置處于繪制模型的內(nèi)部[5]。
用戶也可以通過與樓棟模型交互來進(jìn)入室內(nèi)場景,實(shí)現(xiàn)的方法是添加新的鍵盤監(jiān)聽事件,同時判定條件。當(dāng)同時滿足鏡頭坐標(biāo)位于樓棟門口的矩形區(qū)域范圍內(nèi)時,調(diào)用渲染內(nèi)部場景的函數(shù),同時會將鏡頭坐標(biāo)通過gluLookAt 函數(shù)改變參數(shù)實(shí)現(xiàn)位移,用戶在室內(nèi)場景中同樣可以通過上述的鏡頭移動算法來移動。
通過以上的方法實(shí)現(xiàn)了基本的虛擬場景漫游,但缺少對物理事件的模擬。當(dāng)鏡頭位置與場景中模型所占據(jù)的虛擬空間重合時就會發(fā)生碰撞,為了增加虛擬場景的真實(shí)感,就要對碰撞進(jìn)行檢測并對檢測結(jié)果做出處理。
虛擬場景對碰撞的解決通常需要使用包圍盒算法。所謂包圍盒算法,即是用體積稍大且特性簡單的幾何體對象來近似地代替復(fù)雜的幾何對象。同時,作為一種求解離散點(diǎn)集的最優(yōu)包圍空間算法,包圍盒算法也是進(jìn)行虛擬場景中碰撞干涉初步檢測的重要方法。常見的包圍盒算法有AABB 包圍盒(軸對齊包圍盒)、包圍球、方向包圍盒OBB 以及固定方向凸包FDH,考慮到虛擬場景為小區(qū)樓宇,模型多為與坐標(biāo)軸對齊的建筑物,所以采用AABB 包圍盒算法[6]。在實(shí)驗(yàn)中,忽略觀察者作為模型與場景中其他模型發(fā)生碰撞,將觀察者看作一個點(diǎn),只考慮其視點(diǎn)與其他模型的包圍盒相碰撞的情況。碰撞的發(fā)生是由于視點(diǎn)進(jìn)入了包圍盒與實(shí)際模型之間的區(qū)域才產(chǎn)生的,當(dāng)觀察者前進(jìn)時進(jìn)入包圍盒或后退時進(jìn)入包圍盒,即視線與包圍盒面線段相交且視點(diǎn)坐標(biāo)與包圍盒重合時,判定為有碰撞發(fā)生。將包圍盒封裝為結(jié)構(gòu)體,成員包括不同方向的范圍值,由于實(shí)驗(yàn)中的移動只在xoz 坐標(biāo)平面上,所以成員只有上下左右四個方向的最大值和最小值。
碰撞檢測函數(shù)如下:
在程序中要對場景中的每一個模型的包圍盒遍歷檢測函數(shù),所有的物體無論與視點(diǎn)相距多遠(yuǎn)都要進(jìn)行計算,這樣會對性能產(chǎn)生較大的影響。更優(yōu)解法是對模型進(jìn)行排查,去掉當(dāng)前不可能發(fā)生碰撞的模型,后續(xù)不進(jìn)行相關(guān)的計算。在這里需要借助于一些用于場景管理的數(shù)據(jù)結(jié)構(gòu),例如八叉樹、四叉樹等,其思路是用遞歸來劃分空間,最終確定模型所在的空間。以上是實(shí)驗(yàn)碰撞檢測的改進(jìn)方案。
在碰撞發(fā)生之后,就要做出相應(yīng)的碰撞響應(yīng)。當(dāng)碰撞發(fā)生時,將視點(diǎn)恢復(fù)到碰撞發(fā)生前一瞬間的位置,在視點(diǎn)與邊緣相交時恢復(fù)到恰好不相交的位置,而在過程中保持角度不發(fā)生改變。由于主函數(shù)中的渲染函數(shù)glutDisplayFunc()為循環(huán)調(diào)用,所以需要在每一幀鏡頭移動后進(jìn)行碰撞檢測,并對發(fā)生碰撞的分量取上一步的值。碰撞處理的流程圖如下。
圖1 碰撞流程處理圖
有了碰撞檢測后,虛擬場景中的交互更加真實(shí),用戶的沉浸感也隨之增加。
OpenGL憑借其強(qiáng)大的2D、3D圖形渲染能力,在圖形仿真、虛擬實(shí)現(xiàn)等領(lǐng)域上起著重要作用。本次實(shí)驗(yàn)利用OpenGL 完成了對樓盤虛擬場景的搭建,并結(jié)合C++代碼增加了場景的交互性。在實(shí)驗(yàn)中克服了室內(nèi)搭建、光照、碰撞等難點(diǎn),最終實(shí)現(xiàn)了樓盤的虛擬場景,使用戶可以對樓盤的不同角度進(jìn)行觀察,從室內(nèi)到樓棟外部,形成了一個全面的觀察范圍。在虛擬樓盤中漫游可做為用戶購房的參考,實(shí)現(xiàn)了實(shí)驗(yàn)的預(yù)期目標(biāo)。