楊 剛
(陜西理工學(xué)院數(shù)學(xué)與計(jì)算機(jī)科學(xué)學(xué)院,陜西漢中 723000)
目前,基于Windows系統(tǒng)的虛擬現(xiàn)實(shí)系統(tǒng)的實(shí)現(xiàn)主要采用2種技術(shù):DirectX和 OpenGL。OpenGL是一個(gè)三維的計(jì)算機(jī)圖形和模型庫,利用它可以實(shí)現(xiàn)專業(yè)級的仿真。同時(shí)相對DirectX而言,OpenGL的建模過程更簡單、更靈活。在效果處理方面,OpenGL具有alpha處理能力,生成的圖形真實(shí)感強(qiáng)。本研究選用了OpenGL進(jìn)行系統(tǒng)開發(fā)。
OpenGL程序開發(fā)框架比較眾多,本研究選擇Win32 Application。選擇Win32而不使用MFC來開發(fā)應(yīng)用程序,是因?yàn)镸FC主要是用于基于窗口和文檔的應(yīng)用軟件的編程,它集成了大量的數(shù)據(jù)和方法,將許多煩瑣的任務(wù)如應(yīng)用程序初始化文檔處理和磁盤I/O封裝起來,雖然這樣給使用者的編程帶來了巨大方便,但在編制基于圖形顯示和多媒體的應(yīng)用程序時(shí)會給編程人員帶來極大的麻煩[1]。
Win32 Application程序的入口函數(shù)是WinMain,此函數(shù)的原形是 int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)。此函數(shù)用來調(diào)用窗口創(chuàng)建例程、處理窗口消息,并監(jiān)視人機(jī)交互。系統(tǒng)中其他函數(shù)見表1。
表1 程序中的函數(shù)
OpenGL中場景的創(chuàng)建一般有3種方法:第1種方法是在3DS MAX或其他三維建模軟件中繪制好場景,然后將之讀入到OpenGL程序中[2];第2種方法是在OpenGL中建模,創(chuàng)建場景,這種方法比較繁瑣;第3種方法是讀入外部數(shù)據(jù)文件。本研究采用第3種方法讀入數(shù)據(jù)文件,這樣程序比較靈活,還可以根據(jù)需要加載不同的場景。數(shù)據(jù)文件采用文本格式,這樣效率較高。
系統(tǒng)中創(chuàng)建了一個(gè)場景文件 house.txt,文件部分內(nèi)容見表2。其中場景文件中每行的數(shù)據(jù)結(jié)構(gòu)為(x,y,z,u,v),(x,y,z)代表該頂點(diǎn)的坐標(biāo),(u,v)代表該頂點(diǎn)對應(yīng)的紋理坐標(biāo)。此文件定義的場景剖面圖如圖1所示。house.txt文件中數(shù)據(jù)的坐標(biāo)系如圖1所示,其中y軸的正向垂直xoz平面向上。
圖1 場景示意圖
三維場景中的數(shù)據(jù)信息比較豐富,而且數(shù)據(jù)量隨著場景復(fù)雜程度的增加而增加,因此在系統(tǒng)中必須將數(shù)據(jù)進(jìn)行歸類以便于處理。系統(tǒng)中定義了一系列結(jié)構(gòu)體用來組織場景中的大量數(shù)據(jù)。主要思想是把三維世界看做許多區(qū)段(區(qū)段可以是1個(gè)物體或房子等等)的集合。1個(gè)區(qū)段中包含許多三角形,1個(gè)三角形是由3個(gè)頂點(diǎn)組成,每個(gè)頂點(diǎn)含有x、y、z坐標(biāo)分量和此點(diǎn)對應(yīng)的紋理坐標(biāo)u、v。這樣程序中就需要定義以下結(jié)構(gòu):
typedef struct tagVERTEX{float x,y,z;float u,v;}VERTEX;//定義頂點(diǎn)結(jié)構(gòu)
typedef struct tagTRIANGLE{VERTEX vertex[3];}TRIANGLE; //定義三角形結(jié)構(gòu)
typedef struct tagSECTOR{int numtriangles;TRIANGLE*triangle;}SECTOR;//定義區(qū)段結(jié)構(gòu)
函數(shù)SetupHouse()用來從文件中取得數(shù)據(jù)資料。此函數(shù)在運(yùn)行時(shí)需要調(diào)用函數(shù)readstr(),從數(shù)據(jù)文件中讀入一個(gè)有意義的行數(shù)據(jù),然后再把這些數(shù)據(jù)依次賦給區(qū)段的某個(gè)三角形的某個(gè)頂點(diǎn)的相應(yīng)變量,通過這種方式將數(shù)據(jù)文件中的數(shù)據(jù)存儲在內(nèi)存中。
表2 場景文件的部分內(nèi)容
系統(tǒng)中采用位圖進(jìn)行紋理映射[3]。LoadBMP()函數(shù)用來加載位圖文件(文件后綴擴(kuò)展名為bmp)。如果文件不存在,返回NULL值告知程序無法加載位圖。函數(shù)LoadGLTextures()調(diào)用LoadBMP()函數(shù)先加載位圖文件,然后將之創(chuàng)建為紋理。系統(tǒng)中創(chuàng)建3種紋理:Nearest Filtered Texture、Linear Filtered Texture 和 MipMapped Texture,這樣在程序運(yùn)行時(shí)可以通過鍵盤選擇不同的紋理樣式。創(chuàng)建Nearest Filtered Texture的代碼如下:
在屏幕顯示三維場景的OpenGL程序中都要使用深度緩存??梢詫⑸疃染彺嬖O(shè)想為屏幕后面的層,深度緩存不斷地對進(jìn)入屏幕內(nèi)部的深度進(jìn)行跟蹤,以便對場景進(jìn)行排序,決定哪個(gè)物體先繪制,哪個(gè)物體后繪制,這樣才能正確地顯示場景。這部分程序設(shè)計(jì)在函數(shù)InitGL()中,具體代碼如下:
在OpenGL中默認(rèn)的鏡頭位置在原點(diǎn)(0,0,0)。本系統(tǒng)中漫游的功能是通過變換場景而產(chǎn)生移動或轉(zhuǎn)動鏡頭的錯覺實(shí)現(xiàn)的。具體是圍繞原點(diǎn)以與鏡頭相反的旋轉(zhuǎn)方向來旋轉(zhuǎn)場景,從而讓人產(chǎn)生鏡頭旋轉(zhuǎn)的錯覺,同時(shí)以與鏡頭平移相反的方向來平移場景。
系統(tǒng)中鏡頭(或叫相機(jī)、視點(diǎn))的移動是通過鍵盤上的幾個(gè)方向鍵控制的[4]:按“←”鍵可以使視點(diǎn)向左轉(zhuǎn)動;按“→”鍵可以使視點(diǎn)向右轉(zhuǎn)動;按“↑”鍵可以使視點(diǎn)向前移動;按“↓”鍵可以使視點(diǎn)向后移動;按“PgUp”鍵可以使視點(diǎn)向上偏移;按“PgDn”鍵可以使視點(diǎn)向下偏移。
由于鍵盤上每個(gè)鍵值都可以用0~255的1個(gè)數(shù)來代表,程序中定義了一個(gè)監(jiān)控鍵盤動作的全局?jǐn)?shù)組:bool keys[256]。當(dāng)程序運(yùn)行過程中有鍵盤被按下或釋放時(shí)就捕捉這部分消息并標(biāo)記數(shù)組中對應(yīng)相應(yīng)鍵的組成員,這部分代碼添加在窗口消息處理函數(shù)LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)中。此函數(shù)中uMsg表示消息,wParam、lParam表示附加的消息內(nèi)容。如果有鍵被按下,通過讀取wParam的信息可以找出鍵值,將鍵盤數(shù)組keys[]相應(yīng)的數(shù)組組成員的值設(shè)為TRUE,這樣就可以通過查找key[]來得知什么鍵被按下。如果有鍵被釋放則將鍵盤數(shù)組keys[]相應(yīng)的數(shù)組組成員的值設(shè)為FALSE。
捕捉到相應(yīng)鍵盤消息后,將改變視點(diǎn)的代碼添加在函數(shù)WinMain()中。如按“↑”鍵可以使視點(diǎn)向前移動的代碼如下:
程序運(yùn)行后,系統(tǒng)提示用戶選擇窗口和全屏2種模式。全屏顯示下,漫游效果更為逼真、沉浸性更好。用戶可以用鍵盤中4個(gè)方向鍵進(jìn)行前進(jìn)、后退、左轉(zhuǎn)和右轉(zhuǎn)進(jìn)行漫游,用“PgUp”、“PgDn”兩個(gè)鍵進(jìn)行仰視和俯視的操作,還可以按下“F”鍵觀察同一位圖的3種不同紋理樣式生成不同場景的效果(按3次分別對應(yīng)3種變化)。
如果不考慮碰撞檢測問題,在漫游過程中觀察者有時(shí)可能穿墻而過,故應(yīng)在系統(tǒng)中對這種情況進(jìn)行正確的處理。本系統(tǒng)處理方法[5]:記錄第1次動作的視點(diǎn)位置,當(dāng)視點(diǎn)位置變換到一個(gè)新位置時(shí),判斷是否有墻面穿越新舊視點(diǎn)的連線,如果有,則這個(gè)視點(diǎn)位置變動不能進(jìn)行,仍留在原位置,否則視點(diǎn)可以移動到新的位置。如以按“↑”鍵由室內(nèi)向外穿過A1墻壁(見圖1所示,此墻在地面的投影為一線段,其左端點(diǎn)的坐標(biāo)為(-2,0,-2)、右端點(diǎn)的坐標(biāo)為(-0.5,0,-2))為例,需要在WinMain()函數(shù)中的檢測“↑”鍵被按下的代碼中添加新的內(nèi)容:
運(yùn)行表明以上代碼起到較好的作用,漫游時(shí)不會使視點(diǎn)穿過A1墻體。
運(yùn)用圖形程序庫和Visual C++6.0開發(fā)了一個(gè)三維場景漫游系統(tǒng)。系統(tǒng)實(shí)現(xiàn)中采用了光照、紋理映射、視點(diǎn)變換、碰撞檢測、全屏顯示等技術(shù),增強(qiáng)了虛擬場景的真實(shí)感和沉浸式交互性。
[1]樊雅萍,黃生學(xué),溫佩芝,等.基于OpenGL的機(jī)器人虛擬漫游系統(tǒng)開發(fā)[J].系統(tǒng)仿真學(xué)報(bào),2005,17(10):119-121.
[2]和平鴿工作室.OpenGL高級編程與可視化系統(tǒng)開發(fā)——高級編程篇[M].2版.北京:中國水利水電出版社,2006.
[3]劉興龍,王斌,張赭,等.農(nóng)業(yè)中三維地形可視化及其漫游技術(shù)的研究[J].農(nóng)機(jī)化研究,2008(9):179-181.
[4]郭蓬松,殷宏,晏峰,等.基于OpenGL的飛機(jī)虛擬場景漫游系統(tǒng)的實(shí)現(xiàn)[J].計(jì)算機(jī)工程與設(shè)計(jì),2005.26(7):1938-1941.
[5]李熳,張曙光,劉英.電力系統(tǒng)中基于多邊形與圖像繪制相結(jié)合的實(shí)時(shí)漫游[J].計(jì)算機(jī)工程與應(yīng)用,2003(11):222-224.