附源碼】【Android 3D OpenGL】開發之三——漂亮的金字塔【MacroCheng原創】

【附源碼】【Android 3D OpenGL】開發之三——漂亮的金字塔【MacroCheng原創】

一、OpenGL面可見原理

OpenGL中,有一個概念叫做彎曲(winding),意思是vertices繪製時的順序。與現實世界中的對象不同,OpenGL中的多邊形一般沒有兩個面。他們只有一個面,一般是正面,一個三角形只有當其正面面對觀察者的時候纔可以被看到。可以配置OpenGL將一個多邊形作爲兩面的,但是默認情況下三角形只有一個可見的面。知道了那邊是多邊形的正面以後,OpenGL就可以少做一半的計算量。如果設置兩面都可視,則需要多的計算。

雖然有時候一個多邊形會獨立地顯示,但是你或許不是非常需要它的背面顯示,經常一個三角形是一個更大的對象的一部分,多邊形的一面將在這個物體的內部,所以永遠也不會被看到。這個沒有被顯示的一面被稱作背面,OpenGl通過繪製的順序來確定那個面是正面哪個是背面。頂點按照逆時針繪製的是正面(默認是這樣,但是可以被改變)。因爲OpenGL能很容易地確定哪些三角形對用戶是可視的,它就可以通過使用Backface Culling來避免爲那些不顯示在前面的多邊形做無用功。我們將在下一篇文章裏討論視角的問題,但是你現在可以想象它爲一個虛擬攝像機,或者通過一個虛擬的窗口來觀察OpenGL的世界。

在上面的示意圖中,左邊青綠色的的三角形是背面,將不會被繪製,因爲它相對於觀察者來說是順時針的。而在右邊的這個三角形是正面,將會被繪製,因爲繪製頂點的順序相對於觀察者來說是逆時針的。

 

二、金字塔

2.1、金字塔模型

2.2、金字塔的對象類Pyramid.java

package com.macrocheng.opengl3d01;

 

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.nio.FloatBuffer;

import java.nio.ShortBuffer;

 

public class Pyramid {

   

    public Pyramid() {

       // TODO Auto-generated constructor stub

       initPyramid();

    }

   

    //金字塔頂點的Buffer

    private FloatBuffer vertexBuffer;

    public FloatBuffer getVertexBuffer() {

       return vertexBuffer;

    }

 

    public void setVertexBuffer(FloatBuffer vertexBuffer) {

       this.vertexBuffer = vertexBuffer;

    }

 

    //金字塔顏色數組的Buffer

    private FloatBuffer colorBuffer;

    public FloatBuffer getColorBuffer() {

       return colorBuffer;

    }

 

    public void setColorBuffer(FloatBuffer colorBuffer) {

       this.colorBuffer = colorBuffer;

    }

 

    //金字塔索引值得Buffer

    private ShortBuffer indexBuffer;

   

    public ShortBuffer getIndexBuffer() {

       return indexBuffer;

    }

   

    public void setIndexBuffer(ShortBuffer indexBuffer) {

       this.indexBuffer = indexBuffer;

    }

 

    private int numberOfPoint = 4*3;

   

    public int getNumberOfPoint() {

       return numberOfPoint;

    }

   

    /**

     * 初始化金字塔

     */

    private void initPyramid()

    {

       float[] coords = {

              -0.5f, -0.5f, 0.5f, // 0

              0.5f, -0.5f, 0.5f, // 1

              0f, -0.5f, -0.5f, // 2

              0f, 0.5f, 0f, // 3

       };

      

       float[] colors = {

              1f, 0f, 0f, 1f, // point 0 red

              0f, 1f, 0f, 1f, // point 1 green

              0f, 0f, 1f, 1f, // point 2 blue

              1f, 1f, 1f, 1f, // point 3 white

       };

      

       short[] indicesArray = {

              0, 1, 3, // rwg

              0, 2, 1, // rbg

              0, 3, 2, // rbw

              1, 2, 3, // bwg

       };

      

       //float類型有四個字節,分配內存

       ByteBuffer vbb = ByteBuffer.allocateDirect(coords.length*4);

       vbb.order(ByteOrder.nativeOrder());

       vertexBuffer = vbb.asFloatBuffer();

      

       //short類型有2個字節,分配內存

       ByteBuffer ibb = ByteBuffer.allocateDirect(indicesArray.length*2);

       ibb.order(ByteOrder.nativeOrder());

       indexBuffer = ibb.asShortBuffer();

      

       ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);

       cbb.order(ByteOrder.nativeOrder());

       colorBuffer = cbb.asFloatBuffer();

      

       vertexBuffer.put(coords);

       indexBuffer.put(indicesArray);

       colorBuffer.put(colors);

      

       vertexBuffer.position(0);

       indexBuffer.position(0);

       colorBuffer.position(0);

    }

}

 

三、視角聲明

//讓每個不同的面都可見

gl.glEnable(GL10.GL_CULL_FACE);

//逆時針方向的面爲前面,前面就被畫出來

    gl.glFrontFace(GL10.GL_CCW);

    //gl.glFrontFace(GL10.GL_CW);

    //後面就不需要被畫出來

    gl.glCullFace(GL10.GL_BACK);

將這三條語句放在onSurfaceCreated函數裏面,就可以設置好這個視角,然後所有的反向面都是可見的,然後順時針方向繪成的面都不可見。

3.1、VortexRenderer.java

因爲VortexRenderer類有改動,所有就貼出所有的源碼

package com.tcl.macrocheng.opengl3d02;

 

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.nio.FloatBuffer;

import java.nio.ShortBuffer;

 

import javax.microedition.khronos.egl.EGLConfig;

import javax.microedition.khronos.opengles.GL10;

 

import org.apache.http.util.ByteArrayBuffer;

 

import android.opengl.GLSurfaceView;

 

public class VortexRenderer implements GLSurfaceView.Renderer {

    private static final String LOG = VortexRenderer.class.getSimpleName();

    private float red = 0.9f;

    private float green = 0.2f;

    private float blue = 0.2f;

   

    private FloatBuffer colorBuffer;

   

    @Override

    public void onDrawFrame(GL10 gl) {

       // TODO Auto-generated method stub

       //定義定點的顏色

       gl.glClearColor(0f, 0f, 0f, 1.0f);

       //清除顏色的Buffer然後讓現實上面我們通過glClearColor來定義的顏色

       gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

       gl.glLoadIdentity();

       gl.glRotatef(xAngle, 1f, 0f, 0f);

       gl.glRotatef(yAngle, 0f, 1f, 0f);

       /**

        * 第一個參數是大小,也是頂點的維數。我們使用的是x,y,z三維座標。

        * 第二個參數,GL_FLOAT定義buffer中使用的數據類型。

        * 第三個變量是0,是因爲我們的座標是在數組中緊湊的排列的,沒有使用offset。

        * 最後,第四個參數頂點緩衝。

        */

       gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

      

       /**

        * 參數4表示RGBA(RGBA剛好是四個值),其餘的幾個參數大家都比較熟悉了。

        */

       gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);

      

       /**

        * 將所有這些元素畫出來。第一個參數定義了什麼樣的圖元將被畫出來。

        * 第二個參數定義有多少個元素,

        * 第三個是indices使用的數據類型。

        * 最後一個是繪製頂點使用的索引緩衝。

        */

       gl.glDrawElements(GL10.GL_TRIANGLES, nrOfVertices*3, GL10.GL_UNSIGNED_SHORT, indexBuffer);

       //gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);

       //gl.glDrawElements(GL10.GL_TRIANGLE_FAN, nrOfVertices, GL10.GL_UNSIGNED_SHORT, indexBuffer);

    }

 

    @Override

    public void onSurfaceChanged(GL10 gl, int width, int height) {

       // TODO Auto-generated method stub

       gl.glViewport(0, 0, width, height);

    }

 

    @Override

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

       // TODO Auto-generated method stub

       //讓每個不同的面都可見

       gl.glEnable(GL10.GL_CULL_FACE);

       //逆時針方向的面爲前面,前面就被畫出來

       gl.glFrontFace(GL10.GL_CCW);

       //gl.glFrontFace(GL10.GL_CW);

       //後面就不需要被畫出來

       gl.glCullFace(GL10.GL_BACK);

       gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

       gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

       initTriangle();

    }

   

    public void setColor(float r,float g,float b)

    {

       red = r;

       green = g;

       blue = b;

    }

   

    //保存索引

    private ShortBuffer indexBuffer;

    //爲三角形保存座標

    private FloatBuffer vertexBuffer; 

    //x需要多少個定點

    private int nrOfVertices = 4;

    //初始化一個三角形

    public void initTriangle()

    {  

       float[] coords = {

//            -0.5f,-0.5f,0f,//x1,y1,z1

//            0.5f,0.5f,0f,//x2,y2,z2

//            0f,0.5f,0f //x3,y3,z3

              -0.5f, -0.5f, 0.5f, // 0

              0.5f, -0.5f, 0.5f, // 1

              0f, -0.5f, -0.5f, // 2

              0f, 0.5f, 0f, // 3

       };

      

       float[] colors = {

//            1f, 0f, 0f, 1f, // point 1

//            0f, 1f, 0f, 1f, // point 2

//            0f, 0f, 1f, 1f, // point 3

              1f, 0f, 0f, 1f, // point 0 red

              0f, 1f, 0f, 1f, // point 1 green

              0f, 0f, 1f, 1f, // point 2 blue

              1f, 1f, 1f, 1f, // point 3 white

       };

      

       short[] indicesArray = {

              0, 1, 3, // rwg

              0, 2, 1, // rbg

              0, 3, 2, // rbw

              1, 2, 3, // bwg

       };

      

       //float類型有四個字節,分配內存

       ByteBuffer vbb = ByteBuffer.allocateDirect(coords.length*4);

       vbb.order(ByteOrder.nativeOrder());

       vertexBuffer = vbb.asFloatBuffer();

      

       //short類型有2個字節,分配內存

       ByteBuffer ibb = ByteBuffer.allocateDirect(indicesArray.length*2);

       ibb.order(ByteOrder.nativeOrder());

       indexBuffer = ibb.asShortBuffer();

      

       ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);

       cbb.order(ByteOrder.nativeOrder());

       colorBuffer = cbb.asFloatBuffer();

      

       vertexBuffer.put(coords);

       indexBuffer.put(indicesArray);

       colorBuffer.put(colors);

      

       vertexBuffer.position(0);

       indexBuffer.position(0);

       colorBuffer.position(0);

    }

   

   

    //x座標旋轉角度

    private float xAngle = 0;

    //y座標旋轉角度

    private float yAngle = 0;

    public void setXAngle(float x)

    {

       xAngle = x;

    }

   

    public float getXAngle()

    {

       return xAngle;

    }

   

    public void setYAngle(float y)

    {

       yAngle = y;

    }

   

    public float getYAngle()

    {

       return yAngle;

    }

 

}

 

四、效果預覽

 

 

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