1. 程式人生 > >Android OpenGLES2.0(五)——繪製立方體

Android OpenGLES2.0(五)——繪製立方體

上篇部落格中我們提到了OpenGLES中繪製的兩種方法,頂點法和索引法。之前我們所使用的都是頂點法,這次繪製立方體使用索引法來繪製立方體。

構建立方體

上篇部落格講到正方形的繪製,立方體是是由六個正方形組成,我們將這六個正方形繪製出來,立方體就繪製完畢了。既然選擇用索引法來繪製,立方體擁有八個頂點,我們先將這八個頂點列出來,放到一個數組中:

final float cubePositions[] = {
            -1.0f,1.0f,1.0f,    //正面左上0
            -1.0f,-1.0f,1.0f,   //正面左下1
            1.0f,-1.0f,1.0
f, //正面右下2 1.0f,1.0f,1.0f, //正面右上3 -1.0f,1.0f,-1.0f, //反面左上4 -1.0f,-1.0f,-1.0f, //反面左下5 1.0f,-1.0f,-1.0f, //反面右下6 1.0f,1.0f,-1.0f, //反面右上7 };

正面由032和021兩個三角形組成,其他面諸如此類拆分,得到索引陣列:

final short index[]={
            0,3,2,0,2,1,    //正面
            0,1
,5,0,5,4, //左面 0,7,3,0,4,7, //上面 6,7,4,6,4,5, //後面 6,3,7,6,2,3, //右面 6,5,1,6,1,2 //下面 };

如果使用單一顏色,最後繪製出來的立方體不方便看效果,所以我們來繪製多種顏色的立方體出來。定義各個頂點的顏色:

//八個頂點的顏色,與頂點座標一一對應
float color[] = {
            0f,1f,0f,1f,
            0f,1f,0f,1f,
            0f,1
f,0f,1f, 0f,1f,0f,1f, 1f,0f,0f,1f, 1f,0f,0f,1f, 1f,0f,0f,1f, 1f,0f,0f,1f, };

繪製立方體

確定好立方體座標和顏色後,我們需要建立頂點著色器和片元著色器,可使用與Android OpenGLES2.0(三)——等腰直角三角形和彩色的三角形中彩色三角形相同的頂點著色器和片元著色器。
接著其他的步驟與繪製三角形相同:

  1. 初始化座標資料、索引資料、顏色資料,具體操作為將座標資料、顏色資料分別寫入到獨自的FloatBuffer中,將索引資料寫入到ShortBuffer中
  2. 建立OpenGL2.0程式,將頂點著色器和片元著色器加入到程式中,並連結程式。
  3. 使用建立的OpenGLES2.0程式,寫入變換矩陣、頂點座標資料及顏色資料。
  4. 索引法繪製出所有頂點座標組成的三角形,得到一個立方體。

如果我們僅僅只做了以上事情,往往我們得不到一個正確的立方里,反而會出現比較奇怪的立方體,比如這樣的:
這裡寫圖片描述

這是因為我們沒有開啟深度測試GLES20.glEnable(GLES20.GL_DEPTH_TEST),並在繪製前清除深度快取導致GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT)的。
加入後,我們即可得到正常的立方體:
這裡寫圖片描述
openGL裡常出現深度測試,關於深度測試內容如下:

(1)什麼是深度?
深度其實就是該象素點在3d世界中距離攝象機的距離(繪製座標),深度快取中儲存著每個象素點(繪製在螢幕上的)的深度值!
深度值(Z值)越大,則離攝像機越遠。
深度值是存貯在深度快取裡面的,我們用深度快取的位數來衡量深度快取的精度。深度快取位數越高,則精確度越高,目前的顯示卡一般都可支援16位的Z Buffer,一些高階的顯示卡已經可以支援32位的Z Buffer,但一般用24位Z Buffer就已經足夠了。
(2)為什麼需要深度?
在不使用深度測試的時候,如果我們先繪製一個距離較近的物體,再繪製距離較遠的物體,則距離遠的物體因為後繪製,會把距離近的物體覆蓋掉,這樣的效果並不是我們所希望的。而有了深度緩衝以後,繪製物體的順序就不那麼重要了,都能按照遠近(Z值)正常顯示,這很關鍵。
實際上,只要存在深度緩衝區,無論是否啟用深度測試,OpenGL在畫素被繪製時都會嘗試將深度資料寫入到緩衝區內,除非呼叫了glDepthMask(GL_FALSE)來禁止寫入。這些深度資料除了用於常規的測試外,還可以有一些有趣的用途,比如繪製陰影等等。
(3)啟用深度測試
使用 glEnable(GL_DEPTH_TEST);
在預設情況是將需要繪製的新畫素的z值與深度緩衝區中對應位置的z值進行比較,如果比深度快取中的值小,那麼用新畫素的顏色值更新幀快取中對應畫素的顏色值。
但是可以使用glDepthFunc(func)來對這種預設測試方式進行修改。
其中引數func的值可以為GL_NEVER(沒有處理)、GL_ALWAYS(處理所有)、GL_LESS(小於)、GL_LEQUAL(小於等於)、GL_EQUAL(等於)、GL_GEQUAL(大於等於)、GL_GREATER(大於)或GL_NOTEQUAL(不等於),其中預設值是GL_LESS。
一般來將,使用glDepthFunc(GL_LEQUAL);來表達一般物體之間的遮擋關係。
(4)啟用了深度測試,那麼這就不適用於同時繪製不透明物體。

具體實現

public class Cube extends Shape{

    private FloatBuffer vertexBuffer,colorBuffer;
    private ShortBuffer indexBuffer;
    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "uniform mat4 vMatrix;"+
                    "varying  vec4 vColor;"+
                    "attribute vec4 aColor;"+
                    "void main() {" +
                    "  gl_Position = vMatrix*vPosition;" +
                    "  vColor=aColor;"+
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "varying vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";

    private int mProgram;

    final int COORDS_PER_VERTEX = 3;
    final float cubePositions[] = {
            -1.0f,1.0f,1.0f,    //正面左上0
            -1.0f,-1.0f,1.0f,   //正面左下1
            1.0f,-1.0f,1.0f,    //正面右下2
            1.0f,1.0f,1.0f,     //正面右上3
            -1.0f,1.0f,-1.0f,    //反面左上4
            -1.0f,-1.0f,-1.0f,   //反面左下5
            1.0f,-1.0f,-1.0f,    //反面右下6
            1.0f,1.0f,-1.0f,     //反面右上7
    };
    final short index[]={
            6,7,4,6,4,5,    //後面
            6,3,7,6,2,3,    //右面
            6,5,1,6,1,2,    //下面
            0,3,2,0,2,1,    //正面
            0,1,5,0,5,4,    //左面
            0,7,3,0,4,7,    //上面
    };

    float color[] = {
            0f,1f,0f,1f,
            0f,1f,0f,1f,
            0f,1f,0f,1f,
            0f,1f,0f,1f,
            1f,0f,0f,1f,
            1f,0f,0f,1f,
            1f,0f,0f,1f,
            1f,0f,0f,1f,
    };

    private int mPositionHandle;
    private int mColorHandle;

    private float[] mViewMatrix=new float[16];
    private float[] mProjectMatrix=new float[16];
    private float[] mMVPMatrix=new float[16];

    private int mMatrixHandler;

    //頂點個數
    private final int vertexCount = cubePositions.length / COORDS_PER_VERTEX;
    //頂點之間的偏移量
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 每個頂點四個位元組


    public Cube(View mView) {
        super(mView);
        ByteBuffer bb = ByteBuffer.allocateDirect(
                cubePositions.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(cubePositions);
        vertexBuffer.position(0);

        ByteBuffer dd = ByteBuffer.allocateDirect(
                color.length * 4);
        dd.order(ByteOrder.nativeOrder());
        colorBuffer = dd.asFloatBuffer();
        colorBuffer.put(color);
        colorBuffer.position(0);

        ByteBuffer cc= ByteBuffer.allocateDirect(index.length*2);
        cc.order(ByteOrder.nativeOrder());
        indexBuffer=cc.asShortBuffer();
        indexBuffer.put(index);
        indexBuffer.position(0);
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);
        //建立一個空的OpenGLES程式
        mProgram = GLES20.glCreateProgram();
        //將頂點著色器加入到程式
        GLES20.glAttachShader(mProgram, vertexShader);
        //將片元著色器加入到程式中
        GLES20.glAttachShader(mProgram, fragmentShader);
        //連線到著色器程式
        GLES20.glLinkProgram(mProgram);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //開啟深度測試
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //計算寬高比
        float ratio=(float)width/height;
        //設定透視投影
        Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 20);
        //設定相機位置
        Matrix.setLookAtM(mViewMatrix, 0, 5.0f, 5.0f, 10.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //計算變換矩陣
        Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT| GLES20.GL_DEPTH_BUFFER_BIT);
        //將程式加入到OpenGLES2.0環境
        GLES20.glUseProgram(mProgram);
        //獲取變換矩陣vMatrix成員控制代碼
        mMatrixHandler= GLES20.glGetUniformLocation(mProgram,"vMatrix");
        //指定vMatrix的值
        GLES20.glUniformMatrix4fv(mMatrixHandler,1,false,mMVPMatrix,0);
        //獲取頂點著色器的vPosition成員控制代碼
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        //啟用三角形頂點的控制代碼
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //準備三角形的座標資料
        GLES20.glVertexAttribPointer(mPositionHandle, 3,
                GLES20.GL_FLOAT, false,
                0, vertexBuffer);
        //獲取片元著色器的vColor成員的控制代碼
        mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
        //設定繪製三角形的顏色
//        GLES20.glUniform4fv(mColorHandle, 2, color, 0);
        GLES20.glEnableVertexAttribArray(mColorHandle);
        GLES20.glVertexAttribPointer(mColorHandle,4,
                GLES20.GL_FLOAT,false,
                0,colorBuffer);
        //索引法繪製正方體
        GLES20.glDrawElements(GLES20.GL_TRIANGLES,index.length, GLES20.GL_UNSIGNED_SHORT,indexBuffer);
        //禁止頂點陣列的控制代碼
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
}

原始碼

相關推薦

Android OpenGLES2.0——繪製立方體

上篇部落格中我們提到了OpenGLES中繪製的兩種方法,頂點法和索引法。之前我們所使用的都是頂點法,這次繪製立方體使用索引法來繪製立方體。 構建立方體 上篇部落格講到正方形的繪製,立方體是是由六個正方形組成,我們將這六個正方形繪製出來,立方體就繪製完畢了

Android OpenGLES2.0十三——流暢的播放逐幀動畫

在當前很多直播應用中,擁有給主播送禮物的功能,當用戶點選贈送禮物後,視訊介面上會出現比較炫酷的禮物特效。這些特效,有的是用粒子效果做成的,但是更多的時用播放逐幀動畫實現的,本篇部落格將會講解在Android下如何利用OpenGLES流暢的播放逐幀動畫。在本篇部落

Android OpenGLES2.0——正方形和圓形

上篇部落格中我們已經使用到了相機和投影,利用變換矩陣,繪製出了等腰直角三角形。在本篇部落格中,我們繪製正方形和圓形同樣少不了變換矩陣。 構建正方形和圓形 前面提到過,在OpenGLES的世界裡面是沒有正方形和圓形的,只有點、線、三角形。三角形就是Open

Android OpenGLES2.0——瞭解OpenGLES2.0

什麼是OpenGL ES? OpenGL(全寫Open Graphics Library)是指定義了一個跨程式語言、跨平臺的程式設計介面規格的專業的圖形程式介面。它用於三維影象(二維的亦可),是一個功能強大,呼叫方便的底層圖形庫。 OpenGL在不同

Android OpenGLES2.0——OpenGL中的平移、旋轉、縮放

在前面的部落格中,所有的例子都是一個物件,類似繪製圓錐繪製圓柱,我們都是傳入一個引數,然後去控制那個圓面的位置,如果我們要繪製幾個個正方形,它的位置、大小、方向都是不相同的,按照那種方式該多麻煩啊。所以我們需要更好的辦法——矩陣變換。 什麼是矩陣 其實在

Android快樂貪吃蛇遊戲實戰專案開發教程-06虛擬方向鍵繪製方向鍵箭頭

package net.chengyujia.happysnake; import android.content.Context; import android.graphics.Canvas; import android.graphics.Matrix; import android.g

Android OpenGLES2.0十六——3D模型貼圖及光照處理obj+mtl

在Android OpenGLES2.0(十四)——Obj格式3D模型載入中實現了Obj格式的3D模型的載入,載入的是一個沒有貼圖,沒有光照處理的帽子,為了呈現出立體效果,“手動”加了光照,擁有貼圖的紋理及光照又該怎麼載入呢? 模型檔案 本篇部落格例子中

Android OpenGLES2.0十七——球形天空盒VR效果實現

在3D遊戲中通常都會用到天空盒,在3D引擎中也一般會存在天空盒元件,讓開發者可以直接使用。那麼天空盒是什麼?天空盒又是如何實現的呢?本篇部落格主要介紹如何在Android中利用OpenGLES繪製一個天空盒,並實現VR效果。 天空盒、天空穹、天空球和VR

Android OpenGLES2.0十二——FBO離屏渲染

之前的部落格我們所做的示例都是直接渲染到螢幕上的,如果我們並不需要渲染到螢幕上,也就是離屏渲染,該怎麼做呢?FBO離屏渲染是一個很好的選擇。在這篇部落格中,我們將以渲染攝像頭資料為例,使用FBO進行離屏渲染。 關於FBO離屏渲染 所謂的FBO就是Fram

Android 開發:初識ListView(列表佈局)

效果: xml: <ListView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/li

Android 進階》應用分享簡單實現-LazyApkShare

LazyApkShare 分享當前應用的簡單實現。 開源地址 LazyApkShare 新增依賴 Gradle 步驟一. 根目錄下build.gradle allprojects { repositories { maven { url

Android入門筆記

五、ViewPager 5.1 ViewPager 的作用 為 UI 新增 ViewPager 後,使用者可以左右滑動螢幕,切換檢視不同列表項的明細頁面。 5.2 ViewPager 的實現 (1)建立佈局檔案,設定根元素為 ViewPager,可命名為:ac

床頭筆記之Android開發學習

執行第一個程式 好了,前幾篇已經把環境搭好了,專案建好了,然後目錄一些執行機制弄明白了,下面來執行開始體驗開發的樂趣吧。 eclipse連線夜神模擬器進行開發 用安卓自帶的模擬器啟動時間過長,反應慢,簡直不能忍,那為什麼不用別的安卓模擬器,本次使用夜神模擬器,去

Android Firebase接入-- Firebase推送通知Cloud Message

Firebase Cloud Message(FCM)可以幫助Android App實現訊息推送功能,並且可以在推送通知中攜帶引數,當用戶點選推送通知時,推送中攜帶的引數資訊將傳遞到主Activity的getIntent中。一、配置Android應用並下載google-ser

android個人筆記——解析ini檔案

package com.example.effecttest; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import j

Android學習筆記廣播Broadcast

5. 廣播 5.1 廣播 Android中的廣播主要分為兩種型別:標準廣播和有序廣播。 標準廣播:         是一種完全非同步執行的廣播,廣播發出後,所有的廣播接收器都會在同一時間內接受這條廣

Android軟鍵盤如何判斷軟鍵盤是否顯示

前幾次分析了軟鍵盤自動彈出的現象,以及佈局上移上移等問題,這次記錄一下,如何判斷軟鍵盤是否在顯示。 先來看一下網上比較流行的答案: 一: if(getWindow().getAttributes().softInputMode == WindowManager.Layou

Android NDK 開發AndroidStudio 2.2 NDK的開發環境搭建

前言 之前一直在用Eclipse 做開發,直到今年年初才將專案遷移到Google 推薦的AndroidStudio上面,畢竟這是一個趨勢,可誰知道事情根本沒有我想的那麼簡單,這期間遇到了N多坑,我想這些坑可能大家也有可能遇到,不在這裡詳細敘述。最終一個個問題的

Android產品研發-->多渠道打包

國內的Android開發者還是很苦逼的,由於眾所周知的原因,google play無法在國內開啟(翻牆的就不在考慮之內了),所以Android系的應用市場,群雄爭霸。後果就是國記憶體在著有眾多的應用市場,產品在不同的渠道可能有這不同的統計需求,為此Andro

Android學習筆記——通過全域性變數傳遞資料

1、全域性物件是Activity之間傳遞資料的一種比較實用的方式,比如在JavaWeb中有四個作用域,這四個作用域從小到大分別是Page、Request、Session和Application,其中Application域在應用程式的任何地方都可以使用和訪問,除非是Web伺