2025-02-10

定義三角形

OpenGL 允許我們使用三維坐標來定義物體。在繪制三角形前,我們需要定義它各個點的坐標。我們一般使用數組來存儲各個頂點的坐標。

OpenGL ES 默認 [0,0,0] (X,Y,Z) 在GLSurfaceView的中心,[1,1,0]在右上角,[-1,-1,0]在左下角。

 

繪制三角形

在繪制三角形之前,我們必須告訴OpenGL我們正在使用頂點數組。然後我們才使用繪制函數畫出三角形。

實驗步驟:

1.      添加新的類Triangle

代碼如下:

[java] <span style="font-size:16px;">public class Triangle { 
     
    public Triangle() 
    { 
         float triangleCoords[] = { 
                    // X, Y, Z 這是一個等邊三角形  
                    -0.5f, -0.25f, 0, 
                     0.5f, -0.25f, 0, 
                     0.0f,  0.559016994f, 0 
                };  
                 
                // 初始化三角形的頂點緩存    
                ByteBuffer vbb = ByteBuffer.allocateDirect( 
                        // (# of coordinate values * 4 bytes per float)  
                        triangleCoords.length * 4);  
                vbb.order(ByteOrder.nativeOrder());// 使用設備硬件本身的字節序  
                triangleVB = vbb.asFloatBuffer(); // 從ByteBuffer中創建一個浮點緩存  
                triangleVB.put(triangleCoords);    // 向浮點緩存中添加頂點坐標  
                triangleVB.position(0);            // 使緩存讀第一個坐標  
    } 
     
    public void draw(GL10 gl) 
    { 
        gl.glColor4f(0.63671875f, 0.76953125f, 0.22265625f, 0.0f); //設置當前顏色  
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleVB);//設置頂點  
        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);//繪制三角形  
    } 
     
    private FloatBuffer triangleVB; 

</span> 
<span style="font-size:16px;">public class Triangle {
 
 public Triangle()
 {
   float triangleCoords[] = {
              // X, Y, Z 這是一個等邊三角形
              -0.5f, -0.25f, 0,
               0.5f, -0.25f, 0,
               0.0f,  0.559016994f, 0
          };
         
          // 初始化三角形的頂點緩存 
          ByteBuffer vbb = ByteBuffer.allocateDirect(
                  // (# of coordinate values * 4 bytes per float)
                  triangleCoords.length * 4);
          vbb.order(ByteOrder.nativeOrder());// 使用設備硬件本身的字節序
          triangleVB = vbb.asFloatBuffer(); // 從ByteBuffer中創建一個浮點緩存
          triangleVB.put(triangleCoords);    // 向浮點緩存中添加頂點坐標
          triangleVB.position(0);            // 使緩存讀第一個坐標
 }
 
 public void draw(GL10 gl)
 {
  gl.glColor4f(0.63671875f, 0.76953125f, 0.22265625f, 0.0f); //設置當前顏色
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleVB);//設置頂點
        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);//繪制三角形
 }
 
 private FloatBuffer triangleVB;
}
</span>

 

2.      在myGLRenderer類中添加成員privateTriangle mTriangle並在構造函數中初始化。

代碼如下:

 

[java] <span style="font-size:16px;">public myGLRenderer() 
    { 
        mTriangle = new Triangle(); 
    } 
</span> 
<span style="font-size:16px;">public myGLRenderer()
 {
  mTriangle = new Triangle();
 }
</span>

 

3.      在myGLRenderer類的onSurfaceCreated()函數最後添加glEnableClientState()方法來啟用頂點數組。

代碼如下:

 

[java] <span style="font-size:16px;">@Override 
    public void onSurfaceCreated(GL10 gl, EGLConfig config) { 
        // TODO Auto-generated method stub  
        gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); 
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 
    } 
</span> 
<span style="font-size:16px;">@Override
 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
  // TODO Auto-generated method stub
        gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
 }
</span>

 

4.      在myGLRenderer類的onDrawFrame()函數最後添加三角形繪制方法。

代碼如下:

 

[java] <span style="font-size:16px;">@Override 
    public void onDrawFrame(GL10 gl) { 
        // TODO Auto-generated method stub  
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 
        mTriangle.draw(gl); 
    } 
</span> 
<span style="font-size:16px;">@Override
 public void onDrawFrame(GL10 gl) {
  // TODO Auto-generated method stub
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        mTriangle.draw(gl);
 }
</span>

 

這樣,我們便完成瞭一個平面三角形的繪制。但我們發現這個三角形並不像我們定義的那樣是一個等邊三角形,這是由於OpenGL總假設我們的屏幕是一個正方形,這樣在繪制的時候最終圖形會隨著屏幕長寬比例的不同而被拉伸。為瞭得到正確的顯示,我們需要將圖形投影到正確的位置。這一功能我們在下一節進行實現。

 

 

Android設備屏幕通常不是正方形的,而OpenGL總是默認地將正方形坐標系投影到這一設備上,這就導致圖形無法按真實比例顯示。要解決這一問題,我們可以使用OpenGL 的投影模式和相機視圖將圖形的坐標進行轉換以適應不同的設備顯示。

 

實驗步驟:

1.      修改myGLRenderer類的onSurfaceCreated()函數來啟用GL10.GL_PROJECTION模式,計算屏幕的長寬比並使用這一比例來轉換物體的坐標。

代碼如下:

[python] @Override 
    public void onSurfaceChanged(GL10 gl, int width, int height) { 
        // TODO Auto-generated method stub 
        gl.glViewport(0, 0, width, height); 
         
        float ratio = (float) width / height; 
        gl.glMatrixMode(GL10.GL_PROJECTION);   // 設置當前矩陣為投影矩陣 
        gl.glLoadIdentity();                 // 重置矩陣為初始值 
        gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);  // 根據長寬比設置投影矩陣 
    } 
@Override
 public void onSurfaceChanged(GL10 gl, int width, int height) {
  // TODO Auto-generated method stub
        gl.glViewport(0, 0, width, height);
       
        float ratio = (float) width / height;
        gl.glMatrixMode(GL10.GL_PROJECTION);   // 設置當前矩陣為投影矩陣
        gl.glLoadIdentity();                 // 重置矩陣為初始值
        gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);  // 根據長寬比設置投影矩陣
 }

 

2.      修改myGLRenderer的onDrawFrame()方法,啟用MODELVIEW模式,並使用GLU.gluLookAt()來設置視點。

代碼如下:

[java]  @Override 
public void onDrawFrame(GL10 gl) { 
    // TODO Auto-generated method stub  
       gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 
        
       // 設置當前矩陣為模型視圖模式  
       gl.glMatrixMode(GL10.GL_MODELVIEW); 
       gl.glLoadIdentity();   // reset the matrix to its default state  
        
       // 設置視點  
       GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);      
        
       mTriangle.draw(gl); 

 @Override
 public void onDrawFrame(GL10 gl) {
  // TODO Auto-generated method stub
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
       
        // 設置當前矩陣為模型視圖模式
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();   // reset the matrix to its default state
       
        // 設置視點
        GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);    
       
        mTriangle.draw(gl);
 }

這樣,我們繪制的圖形比例就總是正確的,不再受設備的影響而被拉伸變形瞭。

 

摘自 北京大學-Google Android實驗室
 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *