Android OpenGL ES2.0(一)—— 繪制靜態(tài)形狀

快速入門

關(guān)鍵類

  1. GLSurfaceView
    • 繪制的載體
    • 小部分區(qū)域時可使用TextureView
    • 理論上可使用SurfaceView
  2. GLSurfaceView.Renderer
    • 實質(zhì)的繪制動作
授權(quán)
登錄
簽名

構(gòu)鍵一個GLSurfaceView對象

class MyGLSurfaceView extends GLSurfaceView {

    public MyGLSurfaceView(Context context){
        super(context);
        
        // 創(chuàng)建一個OpenGL ES 2.0 context
        setEGLContextClientVersion(2);
        
        // 設(shè)置Renderer到GLSurfaceView
        setRenderer(new MyRenderer());
         
        // 只有在繪制數(shù)據(jù)改變時才繪制view
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }
}

GLSurfaceView.RENDERMODE_WHEN_DIRTY會阻止繪制GLSurfaceView的幀谦纱,直到你調(diào)用了requestRender()看成,這樣會非常高效。

繪制灰色背景的Renderer

public class MyGL20Renderer implements GLSurfaceView.Renderer {

    // 僅調(diào)用一次跨嘉,用于設(shè)置view的OpenGLES環(huán)境
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // 設(shè)置背景的顏色
        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    }

    public void onDrawFrame(GL10 unused) {
        // 重繪背景色
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    }
    
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }
}

這些方法們都具有一個GL10參數(shù)川慌,但你使用的卻是OpengGLES 2.0 API們,這其實是為了使Android框架能簡單的兼容各OpenGLES版本而做的。

定義形狀

通過OpenGLES相對Android的坐標系統(tǒng),可以定義各種形狀梦重。其中三角形是所有形狀的基礎(chǔ)兑燥,因為很多復雜的三位圖形都可以看做是大量三角形組成的。因為我們先了解定義三角形琴拧,再通過三角形組成正方形降瞳。

三角形

定義一個三角形,需要定義這個三角形的三個頂點在三維空間中的坐標蚓胸。在OpenGL中力崇,典型的方式是為坐標定義一個浮點類型的頂點數(shù)組。

為了最高效赢织,你應(yīng)把這些坐標都寫進一個ByteBuffer,它會被傳到OpenGLES圖形管線以進行處理馍盟。

缺省情況下于置,OpenGLES 假定[0,0,0]是GLSurfaceView 幀的中心,[1,1,0]是右上角贞岭,[-1,-1,0]是左下角八毯。坐標的順序為(X, Y, Z)。

class Triangle {

    private FloatBuffer vertexBuffer;

    // 數(shù)組中每個頂點的坐標數(shù)
    static final int COORDS_PER_VERTEX = 3;
    static float triangleCoords[] = { // 按逆時針方向順序:
         0.0f,  0.622008459f, 0.0f,   // top
        -0.5f, -0.311004243f, 0.0f,   // bottom left
         0.5f, -0.311004243f, 0.0f    // bottom right
    };

    // 設(shè)置顏色瞄桨,分別為red, green, blue 和alpha (opacity)
    float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

    public Triangle() {
        // 為存放形狀的坐標话速,初始化頂點字節(jié)緩沖
        ByteBuffer bb = ByteBuffer.allocateDirect(
                // (坐標數(shù) * 4)float占四字節(jié)
                triangleCoords.length * 4);
        // 設(shè)用設(shè)備的本點字節(jié)序
        bb.order(ByteOrder.nativeOrder());

        // 從ByteBuffer創(chuàng)建一個浮點緩沖
        vertexBuffer = bb.asFloatBuffer();
        // 把坐標們加入FloatBuffer中
        vertexBuffer.put(triangleCoords);
        // 設(shè)置buffer,從第一個坐標開始讀
        vertexBuffer.position(0);
    }
}

此形狀的坐標是按逆時針方向定義的芯侥。繪制順序很重要泊交,因為它定義了哪面是形狀的正面,哪面是反面柱查,使用OpenGLES 的cullface特性廓俭,你可以只畫正面而不畫反面。

正方形

定義正方形有很多方法唉工,典型的做法是使用兩個三角形研乒。

opengl_square.jpg

按照上面的圖形,定義4個關(guān)鍵坐標點淋硝,以及兩個三角形的繪制順序雹熬,即可定義好這個正方形。

class Square {

    private FloatBuffer vertexBuffer;
    private ShortBuffer drawListBuffer;

    // 每個頂點的坐標數(shù)
    static final int COORDS_PER_VERTEX = 3;
    static float squareCoords[] = { -0.5f,  0.5f, 0.0f,   // top left
                                    -0.5f, -0.5f, 0.0f,   // bottom left
                                     0.5f, -0.5f, 0.0f,   // bottom right
                                     0.5f,  0.5f, 0.0f }; // top right

    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // 頂點的繪制順序

    public Square() {
        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
        // (坐標數(shù) * 4)
                squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);

        // 為繪制列表初始化字節(jié)緩沖
        ByteBuffer dlb = ByteBuffer.allocateDirect(
        // (對應(yīng)順序的坐標數(shù) * 2)short是2字節(jié)
                drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);
    }
}

繪制形狀

OpenGL ES 2.0畫一個定義好的圖形需要很多代碼谣膳,典型的需要定義以下幾個東西:

  1. VertexShader-用于渲染形狀的頂點
  2. FragmentShader-用于渲染形狀的外觀(顏色或紋理)
  3. Program-包含了你想要用來繪制一個或多個形狀的Shader竿报。

繪制Program中至少需要保護一個VertexShader和一個FragmentShader。

OpenGLShading Language

如下為OpenGLShading Language (GLSL)代碼参歹,Shader必須經(jīng)過編譯才能添加到Program中仰楚,然后通過Program進行繪制。

private final String vertexShaderCode =
    "attribute vec4 vPosition;" +
    "void main() {" +
    "  gl_Position = vPosition;" +
    "}";

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

可使用如下的靜態(tài)方法編譯Shader

public static int loadShader(int type, String shaderCode){

    // 創(chuàng)建一個vertex shader類型(GLES20.GL_VERTEX_SHADER)
    // 或fragment shader類型(GLES20.GL_FRAGMENT_SHADER)
    int shader = GLES20.glCreateShader(type);

    // 將源碼添加到shader并編譯之
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);

    return shader;
}

編譯OpenGLES shader們和鏈接Program們是很耗CPU的,所以你應(yīng)該避免多次執(zhí)行僧界。

繪制

一般在形狀類中創(chuàng)建draw()負責繪制侨嘀。下面的代碼設(shè)置位置和顏色值到形狀的VertexShader和FragmentShader,然后執(zhí)行繪制功能捂襟。

private final int vertexCount = triangleCoords.length; // COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex

public void draw() {
    // 將program加入OpenGL ES環(huán)境中
    GLES20.glUseProgram(mProgram);

    // 獲取指向vertex shader的成員vPosition的 handle
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

    // 啟用一個指向三角形的頂點數(shù)組的handle
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    // 準備三角形的坐標數(shù)據(jù)
    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                 GLES20.GL_FLOAT, false,
                                 vertexStride, vertexBuffer);

    // 獲取指向fragment shader的成員vColor的handle 
    mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

    // 設(shè)置三角形的顏色
    GLES20.glUniform4fv(mColorHandle, 1, color, 0);

    // 畫三角形
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

    // 禁用指向三角形的頂點數(shù)組
    GLES20.glDisableVertexAttribArray(mPositionHandle);
}

最終只需要在Renderer的onDrawFrame()調(diào)用draw()方法咬腕,即可完成形狀的繪制。

Square的繪制邏輯和Triangle類似葬荷,只需要修改關(guān)鍵的繪制方法涨共。

//GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市宠漩,隨后出現(xiàn)的幾起案子举反,更是在濱河造成了極大的恐慌,老刑警劉巖扒吁,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件火鼻,死亡現(xiàn)場離奇詭異,居然都是意外死亡雕崩,警方通過查閱死者的電腦和手機魁索,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盼铁,“玉大人粗蔚,你說我怎么就攤上這事∪幕穑” “怎么了鹏控?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長趁窃。 經(jīng)常有香客問我牧挣,道長,這世上最難降的妖魔是什么醒陆? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任瀑构,我火速辦了婚禮,結(jié)果婚禮上刨摩,老公的妹妹穿的比我還像新娘寺晌。我一直安慰自己,他們只是感情好澡刹,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布呻征。 她就那樣靜靜地躺著,像睡著了一般罢浇。 火紅的嫁衣襯著肌膚如雪陆赋。 梳的紋絲不亂的頭發(fā)上沐祷,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音攒岛,去河邊找鬼赖临。 笑死,一個胖子當著我的面吹牛灾锯,可吹牛的內(nèi)容都是我干的兢榨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼顺饮,長吁一口氣:“原來是場噩夢啊……” “哼吵聪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起兼雄,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤吟逝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后赦肋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澎办,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年金砍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片麦锯。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡恕稠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扶欣,到底是詐尸還是另有隱情鹅巍,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布料祠,位于F島的核電站骆捧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏髓绽。R本人自食惡果不足惜敛苇,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望顺呕。 院中可真熱鬧枫攀,春花似錦、人聲如沸株茶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽启盛。三九已至蹦掐,卻和暖如春技羔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卧抗。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工藤滥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人颗味。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓超陆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親浦马。 傳聞我的和親對象是個殘疾皇子时呀,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容