Android OpenGL ES (二) 繪製三維/空間座標系

OpenGL ES 繪製三維/空間座標系

本程序的源代碼包在http://download.csdn.net/detail/zhangjikuan/6992735

普通的三維畫圖大多數就是畫個立方體,顏色花哨一點,但是因爲項目需求,要畫一個三維座標系,並在上面顯示三維向量,不是要平面的那種座標系,而是做成像是在紙上畫的那種立體長方體並顯示出向量。

剛開始還是小鬱悶了一會,但是忽略了其實OpenGL ES的面都是三角形組成的,面肯定就是線組成的,既然能夠畫三維面肯定就可以畫三維線,越簡單的東西越容易被忽略,原理請看上一帖,直接上程序了,程序中有詳細講解。

最後的效果圖


/****************************************************************

下面是jiasudu.java

/*******************************************************************

 * 此文件是關於3D座標軸的繪製,用jiasu.java和jiasu.xml實現了用戶界面
 * 關於3D處理的所有程序都在此文件中
 *zjk 2014/03/04
 */
public class Jiasudu implements Renderer
{

float x=-0.5f,y=-0.5f,z=-0.5f;
private float r=0;
Handler handler,handler2;
private Timer timer = new Timer();
private TimerTask task;

// 定義Open GL ES繪製所需要的Buffer對象
FloatBuffer lineVerticesBuffer;
FloatBuffer xyzVerticesBuffer;
ByteBuffer lineFacetsBuffer;
ByteBuffer xiangliangFacetsBuffer;
ByteBuffer XFacetsBuffer;
ByteBuffer YFacetsBuffer;
ByteBuffer ZFacetsBuffer;

void updateXYZ(){                                                               //2.1創建各種數組

// 定義立方體的8個頂點                                                       
float[] lineVertices = new float[] {
// 上頂面正方形的四個頂點
x, y, z,//0
x, 0,z,//1
0,0,z,//2
0,y,z,//3

// 下底面正方形的四個頂點
x,y,0,//4
x,0,0,//5
0,0,0,//6原點
0,y,0,//7 

};
//定義XYZ座標和顯示的字
float xyzVertices[]=new float[]{
-1.2f ,0f, 0f,//0 x起點,畫座標軸的

1.2f ,0f, 0f,//1 X軸的終點
1.0f,0.1f,0f,//2 X軸箭頭1
1.0f,-0.1f,0f,//3 X軸箭頭2

0f ,-1.2f , 0f,//4 Y軸起點
0f ,1.2f , 0f,//5 Y軸終點
0.1f ,1.0f ,0f,//6 Y軸箭頭1
-0.1f ,1.0f ,0f,//7 Y軸箭頭2

0f ,0f ,-1.2f,//8 Z軸起點
0f ,0f ,1.2f,//9 Z軸終點
0f ,0.1f ,1.0f,//10 Z軸箭頭1
0f ,-0.1f ,1.0f,//11 Z軸箭頭2

1.3f,0f,0f,//12 繪製字X
1.35f,0.1f,0f,//13
1.25f,0.1f,0f,//14
1.25f,-0.1f,0f,//15
1.35f,-0.1f,0f,//16

0f,1.4f,0f,//17 繪製字Y
0f,1.3f,0f,//18
0.05f,1.5f,0f,//19
-0.05f,1.5f,0f,//20

-0.05f ,0.05f ,1.25f,//21  繪製字Z
0.05f,0.05f,1.25f,//22
-0.05f,-0.05f,1.25f,//23
0.05f,-0.05f,1.25f,//24

//刻度X軸刻度
0.6f,0f,0f,//25
0.6f,0.1f,0f,//26
-0.6f,0f,0f,//27
-0.6f,0.1f,0f,//28
//刻度y軸刻度
0f,0.6f,0f,//29
-0.1f,0.6f,0f,//30
0f,-0.6f,0f,//31
-0.1f,-0.6f,0f,//32
//刻度Z軸刻度
0f,0f,0.6f,//33
0f,0.1f,0.6f,//34
0f,0f,-0.6f,//35
0f,0.1f,-0.6f//36

};
//畫長方體的12條邊     裏面表示的是從上面數組中第幾個點到第幾個點畫直線
byte[] lineFacets = new byte[]{
0,1,
0,3,
0,4,
1,2,
1,5,
2,3,
2,6,
3,7,
4,5,
4,7,
5,6,
6,7
};
//向量從原點6指向長方體的0點  
byte[] xiangliangFacets = new byte[] {
6,0//6,0
};

//X座標及其箭頭  
byte[] XFacets = new byte[] {
//起終點
0,1,
//箭頭
1,2,
1,3,
//X
12,13,
12,14,
12,15,
12,16,
//X座標
25,26,
27,28

};
//Y座標及其箭頭  
byte[] YFacets = new byte[] {
//起終點
4,5,
//箭頭
5,6,
5,7,
//字Y
17,18,
17,19,
17,20,
//Y軸刻度
29,30,
31,32

};
//Z座標及其箭頭  
byte[] ZFacets = new byte[] {
//起終點
8,9,
//箭頭
9,10,
9,11,
//字Z
21,22,
22,23,
23,24,
//Z軸刻度
33,34,
35,36
};
// 將立方體的頂點位置數據數組包裝成FloatBuffer;
lineVerticesBuffer = floatBufferUtil(lineVertices);
xyzVerticesBuffer = floatBufferUtil(xyzVertices);
// 將直線的數組包裝成ByteBuffer
lineFacetsBuffer = ByteBuffer.wrap(lineFacets);
xiangliangFacetsBuffer = ByteBuffer.wrap(xiangliangFacets);
XFacetsBuffer = ByteBuffer.wrap(XFacets);
YFacetsBuffer = ByteBuffer.wrap(YFacets);
ZFacetsBuffer = ByteBuffer.wrap(ZFacets);
}
//構造函數帶了參數,是因爲不同類間傳遞參數,將接受方的handler實例傳遞過來
@SuppressLint("HandlerLeak")
public Jiasudu(Handler handler_zjk) {

handler2=handler_zjk;//實際上handler2就是接收方的實例了,巧妙的轉換方法實現不同類間handler傳遞數據
handler = new Handler(){
public void handleMessage(Message msg) {
if(msg.what==200)//這是接收本類中定時器發送過來的信號用來更新正方體
updateXYZ();                                                              // 2.2
   }

};
//定時器任務中發送了兩個信號,給本類中發送了一個,給activity類中發了一個
task = new TimerTask(){

  public void run() {
  String[] xyz = new String[3];//發送給activity用的
//2s生成xyz的隨機數
x=(float) (Math.random()*(-2)+1);
y=(float) (Math.random()*(-2)+1);
z=(float) (Math.random()*(-2)+1);
//設定一下要顯示的XYZ位數,不管正負都顯示小數點後兩位
if(x>0)
xyz[0]=String.valueOf(x).substring(0, 4);
else 
xyz[0]=String.valueOf(x).substring(0, 5);
if(y>0)
xyz[1]=String.valueOf(y).substring(0, 4);
else 
xyz[1]=String.valueOf(y).substring(0, 5);
if(z>0)
xyz[2]=String.valueOf(z).substring(0, 4);
else 
xyz[2]=String.valueOf(z).substring(0, 5);
Message msg = new Message();
msg.what=200;//這是發送給當前類中用來更新立方體的
handler.sendEmptyMessage(msg.what);

msg.what=0x123;//這是發送給activity類中用來更新文本框中XYZ值得
Bundle bundle = new Bundle();//對數據包裝後用MSG發送
//要發送的是一個字符串數組,第一個參數是指定數據的名,當接收數據時可以用這個名來選擇獲取哪個數據,很方便,第二個參數是發送的數組
bundle.putStringArray("xyz", xyz);
msg.setData(bundle);
handler2.sendMessage(msg);//handler2指的是接收處的Handler實例,這裏因爲不是同一類,所以要用構造函數將接受類的handler實例傳遞過來
  
      
  }
  };
  timer.schedule(task, 0, 7000);
}

        //2.3 實現接口裏的三個方法
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)                           //2.3.1
{
// 關閉抗抖動
gl.glDisable(GL10.GL_DITHER);
// 設置系統對透視進行修正
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glClearColor(0, 0, 0, 0);
// 設置陰影平滑模式
gl.glShadeModel(GL10.GL_SMOOTH);
// 啓用深度測試
gl.glEnable(GL10.GL_DEPTH_TEST);
// 設置深度測試的類型
gl.glDepthFunc(GL10.GL_LEQUAL);

}


@Override
public void onSurfaceChanged(GL10 gl, int width, int height)                       //2.3.2
{
// 設置3D視窗的大小及位置
gl.glViewport(0, 0, width, height);
// 將當前矩陣模式設爲投影矩陣
gl.glMatrixMode(GL10.GL_PROJECTION);
// 初始化單位矩陣
gl.glLoadIdentity();
// 計算透視視窗的寬度、高度比
float ratio = (float) width / height;
// 調用此方法設置透視視窗的空間大小。
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
}


// 繪製圖形的方法
@Override
public void onDrawFrame(GL10 gl)                 //2.3.3 
{
// 清除屏幕緩存和深度緩存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 啓用頂點座標數據
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);                     //2.3.3.1
// 啓用頂點顏色數據
//gl.glEnableClientState(GL10.GL_COLOR_ARRAY);                   //2.3.3.2
// 設置當前矩陣模式爲模型視圖。
gl.glMatrixMode(GL10.GL_MODELVIEW);

// --------------------繪製正方體---------------------
// 重置當前的模型視圖矩陣
gl.glLoadIdentity();
gl.glTranslatef(0.0f, -0.0f, -3.0f);//移動中心
// 沿着Y軸旋轉
gl.glRotatef(r, 0f, 0.1f, 0.0f);
r++;
// 沿着X軸旋轉
//gl.glRotatef(0f, 0.1f, 0f, 0f);
gl.glLineWidth(2.0f);
// 設置頂點的位置數據 因爲所有的數據都在次數組中,所以長方體和向量的只要設置這一次就好
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, lineVerticesBuffer);                      //2.3.3.3
// 設置頂點的顏色數據
gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);                       // 2.3.3.4
//gl.glDrawArrays(GL10.GL_LINE_LOOP, 0, 18);//這裏不用二維,用三維的畫法,注意是GL_LINES三維中畫線             
gl.glDrawElements(GL10.GL_LINES, lineFacetsBuffer.remaining(),               //2.3.3.5
GL10.GL_UNSIGNED_BYTE, lineFacetsBuffer);
// --------------------繪製向量---------------------
//繪製向量
gl.glLineWidth(6.0f);//直線寬度 5倍於其他線
//無需再設置點了,都是用的上面的數組中的
// gl.glVertexPointer(3, GL10.GL_FLOAT, 0, lineVerticesBuffer);//向量
gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);//向量
gl.glDrawElements(GL10.GL_LINES, xiangliangFacetsBuffer.remaining(),
GL10.GL_UNSIGNED_BYTE, xiangliangFacetsBuffer);//向量
// --------------------繪製X座標---------------------
//繪製x座標
gl.glLineWidth(3.0f);//直線寬度
//設置XYZ的頂點 因爲所有XYZ的數據都在次數組中,所以XYZ的只要設置這一次就好
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, xyzVerticesBuffer);
// 設置頂點的顏色數據
gl.glColor4f(0.0f, 1.0f, 0.0f, 1.0f);//X
gl.glDrawElements(GL10.GL_LINES, XFacetsBuffer.remaining(),
GL10.GL_UNSIGNED_BYTE, XFacetsBuffer);//X
// --------------------繪製Y座標---------------------
//繪製Y座標
//無需再設置點了,都是用的上面的數組中的
// gl.glVertexPointer(3, GL10.GL_FLOAT, 0, lineVerticesBufferY);//Y
// 設置頂點的顏色數據
gl.glColor4f(1.0f, 1.0f, 0.0f, 1.0f);//Y
gl.glDrawElements(GL10.GL_LINES, YFacetsBuffer.remaining(),
GL10.GL_UNSIGNED_BYTE, YFacetsBuffer);//Y
// --------------------繪製Z座標---------------------
//繪製Z座標
//無需再設置點了,都是用的上面的數組中的
// gl.glVertexPointer(3, GL10.GL_FLOAT, 0, lineVerticesBufferZ);//Y
// 設置頂點的顏色數據
gl.glColor4f(1.0f, 0.0f, 1.0f, 1.0f);//z
gl.glDrawElements(GL10.GL_LINES, ZFacetsBuffer.remaining(),
GL10.GL_UNSIGNED_BYTE, ZFacetsBuffer);//Z
// 繪製結束
gl.glFinish();                               //2.3.3.6
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
// 旋轉角度增加1
//rotate+=1;
}
// 定義一個工具方法,將int[]數組轉換爲OpenGL ES所需的IntBuffer
// private IntBuffer intBufferUtil(int[] arr)
// {
// IntBuffer mBuffer;
// // 初始化ByteBuffer,長度爲arr數組的長度*4,因爲一個int佔4個字節
// ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
// // 數組排列用nativeOrder
// qbb.order(ByteOrder.nativeOrder());
// mBuffer = qbb.asIntBuffer();
// mBuffer.put(arr);
// mBuffer.position(0);
// return mBuffer;
// }
// 定義一個工具方法,將float[]數組轉換爲OpenGL ES所需的FloatBuffer
private FloatBuffer floatBufferUtil(float[] arr)
{
FloatBuffer mBuffer;
// 初始化ByteBuffer,長度爲arr數組的長度*4,因爲一個int佔4個字節
ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
// 數組排列用nativeOrder
qbb.order(ByteOrder.nativeOrder());
mBuffer = qbb.asFloatBuffer();
mBuffer.put(arr);
mBuffer.position(0);
return mBuffer;
}

}


只需在activity函數中實現第一三步就OK了

/****************************************************************

下面是jiasu.java

/*******************************************************************

// 創建GLSurfaceView的內容繪製器
Jiasudu myRender = new Jiasudu(drawlineHandler);
// 爲GLSurfaceView設置繪製器
glView.setRenderer(myRender);


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章