許林然
(哈爾濱師范大學)
隨著移動通信技術、計算機信息技術等新技術的高速發展,平板電腦、智能手機等移動設備層出不窮,智能手機等移動設備已經成為人們日常生活的必需工具,快捷直觀的地圖查詢越來越受到人們的重視[1].Android是 Google公司于2007年11月5日發布的基于Linux內核的移動平臺,是一個真正的開源移動開發平臺[2].
OpenGL是由SGI公司開發的一套3D圖形軟件接口標準.OpenGL的體系結構簡單、使用方便,并且具有與操作平臺無關的優良特性,這使得其迅速成為一種3D圖形接口的工業標準,并陸續在各種平臺上得以實現.Android平臺下支持的高性能的3D圖像編程接口是通過OpenGL ES來實現的.OpenGL ES是專門根據手持及移動設備的特點對OpenGL標準進行裁剪定制產生的[3].OpenGL ES是 OpenGL 三維圖形 API的子集.
為了加快校園的數字化進程以及方便外界和廣大學生對校園的地理環境進行了解,開發一個基于Android的校園的三維導覽系統具有重要意義.該系統提供的移動端三維地圖的展示相對傳統的二維地圖展示,具有方便快捷、直觀形象的特點.
在OpenGL ES當中只支持點、線和三角形面這三種基本圖元的繪制,三角形面是最基礎的平面類型,同時也是OpenGL ES中唯一的平面類型,利用若干個三角形面可以構成復雜的三維物體.由于在OpenGL ES中并沒有提供構建復雜三維模型的高級命令,所以使用OpenGL ES程序來建立三維模型時過程比較繁瑣、直觀性較差、編程量較大.此外如果建模時需要對模型的數據進行修改,那么修改的效率也是很低的.
運用三維建模軟件進行建模不需要編程便可以直觀地構造外觀精細的三維模型,大大提高了軟件的開發效率.由于最終的目的是能夠在Android平臺上對三維場景進行控制和交互,于是,需要把三維建模軟件中已建立好的模型的數據導入到 Android平臺上加以利用,然后在OpenGL ES程序中對模型數據進行解析[4],運用光照、紋理和模型變換等函數,就可以把三維場景在Android平臺上渲染出來.
Android中的用戶界面視圖的顯示與交互是通過Activity組件加載用戶界面View來實現的,同樣對于三維場景的顯示也需要借助于View.Android專門定義了 GLSurfaceView類,該類是View類的子類,它為OpenGL ES提供了一個專用的渲染線程.GLSurfaceView類是 Android和OpenGL ES聯系的紐帶.
系統的設計思路如圖1所示.

圖1 系統設計思路圖
三維場景建模的首要任務就是收集建模的數據[1],系統所需的建模數據主要有校園的二維平面圖、校園的地形圖、實測圖片、三維觀測數據等.其中校園的二維平面圖是指校園的規劃設計CAD圖,它是三維場景建模的主要數據源;校園的地形是指校園的遙感衛星圖像,它可以作為三維場景的地形底圖;實測圖片是指建筑物、道路、草地等的表面紋理圖片;三維觀測數據指的是建筑物的高程數據.系統在進行三維場景建模時所采用的三維建模軟件是3DMAX,該軟件具有模型表達精細、建模工具豐富等特點.由于校園規劃設計CAD圖是三維建模的主要數據源,使用3DMAX進行三維建模可以實現與建模數據源的無縫融合[5].
在3DMAX中完成校園三維場景的建模后,由于Android平臺上的OpenGL ES無法直接讀取模型文件,所以就需要把模型文件保存為OpenGL ES可以讀取的文件,3DMAX中提供了幾種可以被OpenGL ES讀取的輸出文件格式,在此選擇了比較通用的OBJ文件格式.OBJ文件是以純文本的形式記錄三維模型的頂點、法向量、紋理坐標和材質使用信息.在JAVA程序中結合正則表達式對OBJ文件進行解析,把解析過程中所獲取的頂點坐標、紋理坐標、法向量坐標等模型信息存放到數組ArrayList當中,以便OpenGL ES進行模型的繪制與渲染時使用.其代碼如下所示(以獲取頂點坐標為例):
String[]tempsa=temps.split("[]+");//用空格分割行中的各個組成部分
if(tempsa[0].trim().equals("v"))
{//此行為頂點坐標
//若為頂點坐標行則提取出此頂點的XYZ坐標添加到原始頂點坐標列表中

坐標

坐標

坐標

三維場景的平移功能的實現需要用到平移變換函數 glTranslatef(floatx,floaty,floatz),在Renderer類中定義兩個成員變量xoffest和yoffest,分別表示沿x軸和y軸的位移,并對其進行初始化,然后傳給 glTranslatef函數.在 GLSurfaceView的屏幕觸摸事件下,如果發生ACTION_MOVE操作,則根據屏幕拖動的起點和終點計算X軸方向的位移和Y軸方向的位移,分別賦給xoffest和yoffest,然后調用GLSurfaceView的重繪方法requestRender()屏幕上即呈現出三維場景平移后的效果.
在Renderer類中定義兩個成員變量xScale和yScale,分別表示在X軸和Y軸上的縮放因子,并對其進行初始化,在onDrawFrame()函數中調用 glScale(xScale,yScale,1.0f).當發生放大或縮小操作時,根據預先設定的放大或縮小比例,修改xScale和yScale的值,然后調用GLSurfaceView的重繪方法requestRender()屏幕上即呈現出三維場景放大或縮小后的結果.能夠產生放大和縮小的操作有以下兩種.
第一種是直接點擊屏幕上的放大、縮小按鈕.ImageButton按鈕是Android SDK提供的基本的UI組件,為了能使ImageButton按鈕能夠“懸浮”在GLSurfaceView上,把ImageButton按鈕布局在另一個XML文件中,并在該XML文件中設置OnClick屬性,以便響應在主程序中定義的放大、縮小方法,然后通過 LayoutInflater把這個XML文件和程序默認的XML合并在一起.其代碼如下所示:
//設置主程序的界面布局屬性
LinearLayout GLSurfaceViewLinearLayout=new Line arLayout(this);
GLSurfaceViewLinearLayout.setLayoutParams(
new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));GLSurfaceViewLinearLayout. setOrientation(LinearLayout.VERTICAL);
//獲取XML布局的文件
LayoutInflaterinflater = (LayoutInflater)this.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);//把獲取的XML文件實例化
LinearLayout imagebuttonLinearLayout =(LinearLayout)
inflater.inflate(R.layout.zoom,GLSurface-ViewLinearLayout,false);
//合并
GLSurfaceViewLinearLayout.addView(imagebuttonLinearLayout);
//添加合并后的布局文件
addContentView(GLSurfaceViewLinearLayout,new LayoutParams
(LayoutParams.MATCH_PARENT,Layout-Params.MATCH_PARENT));
放大、縮小按鈕“懸浮”在GLSurfaceView上的運行效果如圖2所示.
第二種是觸摸屏幕進行放大和縮小.運用到了Android的多點觸控技術,當用兩個手指觸摸屏幕時,首先記錄一下最先觸摸到屏幕的手指的觸點坐標,定義一個變量oldDist,用來表示兩個觸點的相對位置變化前的觸點距離,初始化為0,當第二個手指按下的瞬間,計算兩個觸點間的距離并賦給oldDist.另外再定義一個變量newD-ist,用來記錄兩個觸點的相對位置變化后的觸點距離,當newDist>oldDist時,執行放大操作,當newDist<oldDist時,執行縮小操作,其代碼實現如下所示:
float newDi
st=Distance(e);
if(newDist> oldDist&&mRenderer.xScale <=5.0f)//放大,最大放大5 倍
{ mRenderer.xScale + = 0.1f;

圖2 放大、縮小按鈕的“懸浮”
mRenderer.yScale+=0.1f;oldDist=newDist;
requestRender();//重繪畫面 }if(newDist< oldDist&&mRenderer.xScale >=0.5f)//縮小,最小縮小 0.5 倍
{mRenderer.xScale-=0.1f;mRenderer.yScale-=0.1f;oldDist=newDist;
requestRender();//重繪畫面 }
簡單的信息瀏覽功能就是當用戶點擊三維場景中的某一地物時,會呈現出一個介紹該地物相關信息的界面.通過用戶點擊屏幕所獲取的坐標是在屏幕坐標系中的坐標,但是三維場景中地物的坐標是三維坐標,要想使屏幕上的觸點位置坐標與地物的三維坐標相對應,就需要在Open-GL ES的世界坐標系和屏幕坐標系之間進行一個坐標系間的轉換,本系統選擇是把OpenGL ES的世界坐標系轉換為了屏幕坐標系(根據投影知識,Z軸坐標經轉換后是沒有實際意義的),轉換函數如下所示:


矩陣
gl.glGetIntegerv(GL11.GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES,
bits,0);//獲取模型變換的矩陣信息
for(int i=0;i < bits.length;i++){
model[i]= Float.intBitsToFloat(bits[i]);
}gl.glGetIntegerv(GL11.GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES,bits,0);//獲取投影變換的矩陣信息
for(int i=0;i < bits.length;i++){
project[i]=Float.intBitsToFloat(bits[i]);
}GLU.gluProject(objX,objY,objZ,model,0,project,0,
new int[]{0,0,getWidth(),getHeight()},0,win,0);//該函數得到的二維屏幕坐標的原點是在屏幕左下角,Y軸方向與屏幕左邊界平行向上.
win[1]=getHeight()- win[1];//使Y軸坐標轉換為坐標原點在屏幕左上角,Y軸方向與屏幕左邊界平行向向下的坐標系中的Y軸坐標return win;}
根據地物的邊界來確定兩個邊界坐標,經過坐標轉換,可以確定一個屏幕點擊區域,只要用戶點擊位置在這個區域內,就可以瀏覽該地物相應的信息.這樣就避免了必須要點擊某個準確的點才能瀏覽信息而給用戶帶來的不便.信息的顯示是通過運用Intent組件來調用Activity的方式來實現的.被調用的Avtivity的有關信息需要在AndroidManifest.xml文件中設置.該功能的運行效果如圖3所示.以查詢校區某一教學樓為例,其代碼為:
Intent intent=new Intent(getApplicationContext(),ActivityLiOne.class);StartActivity(intent);
在Android移動平臺上實現三維地圖可視化的方法,運用Android和OpenGL ES三維開發技術實現了三維地圖的平移、放大、縮小和簡單的信息查詢功能.該系統能夠形象直觀地展現真實的校園景觀,方便了用戶對學校的了解.基于Android的校園三維導覽系統可以為數字化校園提供一個移動端的三維平臺,為數字化校園的進一步發展奠定了基礎.

圖3 簡單信息查詢圖
[1]王亞美,魯田.基于OpenGL ES的二三維地圖可視化客戶端設計與實現[J].計算機應用與軟件,2013,30(9):77-80.
[2]郝玉龍.Android程序設計基礎[M].北京:清華大學出版社;北京交通大學出版社,2011.10.
[3]歐陽零.Android核心技術與實例詳解:第2版[M].北京:電子工業出版社,2013.
[4]黃小鳳,宋瑾鈺,俞成海.基于OpenGL ES的移動平臺的三維模型繪制[J].工業控制計算機,2013,26(1):60-62.
[5]吳慶雙,王楠.安徽師范大學三維虛擬校園系統建設研究[J].重慶文理學院學報,2012,31(1):62–67.