OpenGL繪制一張圖片的流程--以android-openGL-canvas為例

樣例代碼來(lái)源: android-openGL-canvas

OpenGL文檔參考:
OpenGL文檔
本文只對(duì)流程做排序以及一些簡(jiǎn)單的說(shuō)明,想了解原理的請(qǐng)查看OpenGL相關(guān)文檔肮蛹,有對(duì)相應(yīng)函數(shù)有疑問(wèn)的可查閱上述文檔刻帚。

一潦嘶、創(chuàng)建 eglContext。EGLContext 是一個(gè)比較重的對(duì)象崇众,所以一般只創(chuàng)建一次掂僵。

例子代碼代碼主要在 EglHelper

  • egl = EGLContext.getEGL()
  • 獲取 eglDisplay = eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
  • 獲取 EGLConfig eglChooseConfig(..)
  • eglInitialize(mEgl, mEglDisplay, mEglConfig, eglContext)
  • 創(chuàng)建 eglContext = eglCreateContext(display, config, eglContext, attrib_list))
  • 大致的相關(guān)代碼
    public EGLContext start(EGLContext eglContext) {
        if (GLThread.LOG_EGL) {
            Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
        }
        /*
         * Get an EGL instance
         */
        mEgl = (EGL10) EGLContext.getEGL();

        /*
         * Get to the default display.
         */
        mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

        if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
            throw new RuntimeException("eglGetDisplay failed");
        }

        /*
         * We can now initialize EGL for that display
         */
        int[] version = new int[2];
        if (!mEgl.eglInitialize(mEglDisplay, version)) {
            throw new RuntimeException("eglInitialize failed");
        }
        mEglConfig = eglConfigChooser.chooseConfig(mEgl, mEglDisplay);

            /*
            * Create an EGL context. We want to do this as rarely as we can, because an
            * EGL context is a somewhat heavy object.
            */
        mEglContext = eglContextFactory.createContext(mEgl, mEglDisplay, mEglConfig, eglContext);
        if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
            mEglContext = null;
            throwEglException("createContext");
        }
        if (GLThread.LOG_EGL) {
            Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
        }

        mEglSurface = null;

        return mEglContext;
    }

二、創(chuàng)建 surface

  • eglCreateWindowSurface(display, config, nativeWindow, surfaceAttribs)

  • 將 context 和 surface 聯(lián)系起來(lái)顷歌。
    eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)

  • 如果需要更換surface锰蓬,就先destroy現(xiàn)在的
    eglDestroySurface

  • 大致的相關(guān)代碼

    public boolean createSurface(Object surface) {
        if (GLThread.LOG_EGL) {
            Log.w("EglHelper", "createSurface()  tid=" + Thread.currentThread().getId());
        }
        /*
         * Check preconditions.
         */
        if (mEgl == null) {
            throw new RuntimeException("egl not initialized");
        }
        if (mEglDisplay == null) {
            throw new RuntimeException("eglDisplay not initialized");
        }
        if (mEglConfig == null) {
            throw new RuntimeException("mEglConfig not initialized");
        }

        /*
         *  The window size has changed, so we need to create a new
         *  surface.
         */
        destroySurfaceImp();

        /*
         * Create an EGL surface we can render into.
         */
        mEglSurface = eglWindowSurfaceFactory.createWindowSurface(mEgl,
                mEglDisplay, mEglConfig, surface);

        if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
            int error = mEgl.eglGetError();
            if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
                Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
            }
            return false;
        }

        /*
         * Before we can issue GL commands, we need to make sure
         * the context is current and bound to a surface.
         */
        if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
            /*
             * Could not make the context current, probably because the underlying
             * SurfaceView surface has been destroyed.
             */
            logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
            return false;
        }

        return true;
    }

三、接下來(lái)就是使用vertex數(shù)據(jù)眯漩,texture數(shù)據(jù)芹扭,vertex shader, texture shader進(jìn)行繪制了,這一部分是可以隨時(shí)替換數(shù)據(jù)繪制新的東西的,其中比較耗性能的部分是編譯鏈接shader的部分冯勉,所以要對(duì)編譯鏈接部分進(jìn)行緩存以保證性能。

代碼主要在GLES20Canvas

vertex 數(shù)據(jù)

glGenBuffers(...)
glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId)
glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer, GLES20.GL_STATIC_DRAW)

  • 大致的相關(guān)代碼
    private int uploadBuffer(Buffer buffer, int elementSize) {
        mGLId.glGenBuffers(1, mTempIntArray, 0);
        checkError();
        int bufferId = mTempIntArray[0];
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId);
        checkError();
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer,
                GLES20.GL_STATIC_DRAW);
        checkError();
        return bufferId;
    }

編譯摹芙,鏈接shader等

  • 以兩個(gè) String 對(duì)象作為參數(shù)灼狰,分別是 vertex shader 和 fragment shader
  • 編譯
    glCreateShader
    glShaderSource
    glCompileShader
    private static int loadShader(int type, String shaderCode) {
        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        checkError();
        GLES20.glCompileShader(shader);
        checkError();

        return shader;
    }
  • 鏈接
    glCreateProgram
    glAttachShader vertex
    glAttachShader fragment
    glLinkProgram
    glGetProgramiv
    private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) {
        int program = GLES20.glCreateProgram();
        checkError();
        if (program == 0) {
            throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError());
        }
        GLES20.glAttachShader(program, vertexShader);
        checkError();
        GLES20.glAttachShader(program, fragmentShader);
        checkError();
        GLES20.glLinkProgram(program);
        checkError();
        int[] mLinkStatus = mTempIntArray;
        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0);
        if (mLinkStatus[0] != GLES20.GL_TRUE) {
            Log.e(TAG, "Could not link program: ");
            Log.e(TAG, GLES20.glGetProgramInfoLog(program));
            GLES20.glDeleteProgram(program);
            program = 0;
        }
        loadHandles(params, program);
        return program;
    }

設(shè)定繪制區(qū)域

  • glViewport(width, height)
    此時(shí)就可以利用 width 和 height 來(lái)對(duì) model view projection 矩陣進(jìn)行一些初始設(shè)置了
    @Override
    public void setSize(int width, int height) {
        mWidth = width;
        mHeight = height;
        GLES20.glViewport(0, 0, mWidth, mHeight);
        checkError();
        Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
        Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1);
        if (getTargetTexture() == null) {
            mScreenWidth = width;
            mScreenHeight = height;
            Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0);
            Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1);
        }
    }

對(duì)model-view矩陣進(jìn)行處理(也可以不處理,看需求)

可以是平移浮禾,旋轉(zhuǎn)交胚,放縮之類的。

傳入 texture, 一個(gè)帶有寬高和圖像數(shù)據(jù)的對(duì)象盈电。

  • glUseProgram(programId) 使用之前編譯鏈接好的的id
  • 將texture數(shù)據(jù)傳入到OpenGL里的gl操作
    glActiveTexture(GLES20.GL_TEXTURE0)
    private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) {
        GLES20.glUseProgram(program);
        checkError();
        enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        checkError();
        texture.onBind(this);
        GLES20.glBindTexture(texture.getTarget(), texture.getId());
        checkError();
        GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);
        checkError();
        GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha());
        checkError();
    }

glGenTextures
glBindTexture
glTexParameteri 設(shè)定相關(guān)的texture params

    @Override
    public void setTextureParameters(BasicTexture texture) {
        int target = texture.getTarget();
        GLES20.glBindTexture(target, texture.getId());
        checkError();
        GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    }
  • 如果bitmap的寬高是2的倍數(shù)
    glBindTexture
    glTexImage2D
    @Override
    public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
        int target = texture.getTarget();
        GLES20.glBindTexture(target, texture.getId());
        checkError();
        GLUtils.texImage2D(target, 0, bitmap, 0);
    }
  • 如果不是2的倍數(shù)
    glBindTexture
    GLUtils.texSubImage2D
    @Override
    public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
            int format, int type) {
        int target = texture.getTarget();
        GLES20.glBindTexture(target, texture.getId());
        checkError();
        GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
    }
glUniform1i(...) // 對(duì)shader里面的textureSampler傳值蝴簇,因?yàn)锳ctiveTexture的是Texture0,所以傳0就可以了匆帚。
    GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);

將vertex的值傳入OpenGL

glBindBuffer
glVertexAttribPointer
glBindBuffer 0 清理

    private void setPosition(ShaderParameter[] params, int offset) {
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates);
        checkError();
        GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE);
        checkError();
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
        checkError();
    }

texture Matrix

  • 利用texture的寬高設(shè)定 textureMatrix熬词。 textureMatrix 是shader 里的一個(gè)uniform對(duì)象,用來(lái)映射從texture對(duì)應(yīng)位置獲取的顏色到繪制區(qū)域吸重。例如在不進(jìn)行平移旋轉(zhuǎn)等情況下互拾,第一個(gè)像素點(diǎn)就繪制在第一個(gè)位置。
        TextureMatrixTransformer.copyTextureCoordinates(...);
        TextureMatrixTransformer.convertCoordinate(...);
        TextureMatrixTransformer.setTextureMatrix(...);

model view projection matrix 將這個(gè)matrix傳入到OpenGL里

translateM
scaleM
multiplyMM
glUniformMatrix4fv(...)

    private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) {
        Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
        Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
        Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0);
        GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE);
        checkError();
    }

最后就是繪制了

glEnableVertexAttribArray positionHandle
glDrawArrays
glDisableVertexAttribArray positionHandle

    private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width,
            float height) {
        setMatrix(params, x, y, width, height);
        int positionHandle = params[INDEX_POSITION].handle;
        GLES20.glEnableVertexAttribArray(positionHandle);
        checkError();
        GLES20.glDrawArrays(type, 0, count);
        checkError();
        GLES20.glDisableVertexAttribArray(positionHandle);
        checkError();
    }

四嚎幸、總結(jié)

以上颜矿,說(shuō)明了OpenGL繪制的三個(gè)流程。由于OpenGL是獨(dú)立于語(yǔ)言的嫉晶,這些流程不僅適用于Android骑疆,使用其它語(yǔ)言編寫也可借鑒。從上面冗長(zhǎng)的流程可以看到替废,每次要進(jìn)行繪制都寫那么多代碼實(shí)在不經(jīng)濟(jì)箍铭,那么接下來(lái)的文章就會(huì)討論如何封裝以上的流程了。其實(shí)開(kāi)頭的github倉(cāng)庫(kù)里面的代碼已經(jīng)有封裝好的類了椎镣,有需要的話可以先去看源代碼坡疼。感謝閱讀!
接下來(lái)可以看:
如何封裝 opengl 流程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末衣陶,一起剝皮案震驚了整個(gè)濱河市柄瑰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌剪况,老刑警劉巖教沾,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異译断,居然都是意外死亡授翻,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)堪唐,“玉大人巡语,你說(shuō)我怎么就攤上這事』床ぃ” “怎么了男公?”我有些...
    開(kāi)封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)合陵。 經(jīng)常有香客問(wèn)我枢赔,道長(zhǎng),這世上最難降的妖魔是什么拥知? 我笑而不...
    開(kāi)封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任踏拜,我火速辦了婚禮,結(jié)果婚禮上低剔,老公的妹妹穿的比我還像新娘速梗。我一直安慰自己,他們只是感情好襟齿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布镀琉。 她就那樣靜靜地躺著,像睡著了一般蕊唐。 火紅的嫁衣襯著肌膚如雪屋摔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天替梨,我揣著相機(jī)與錄音钓试,去河邊找鬼。 笑死副瀑,一個(gè)胖子當(dāng)著我的面吹牛弓熏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播糠睡,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼挽鞠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了狈孔?” 一聲冷哼從身側(cè)響起信认,我...
    開(kāi)封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎均抽,沒(méi)想到半個(gè)月后嫁赏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡油挥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年潦蝇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了款熬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡攘乒,死狀恐怖贤牛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情则酝,我是刑警寧澤殉簸,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站堤魁,受9級(jí)特大地震影響喂链,放射性物質(zhì)發(fā)生泄漏返十。R本人自食惡果不足惜妥泉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望洞坑。 院中可真熱鬧盲链,春花似錦、人聲如沸迟杂。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)排拷。三九已至侧漓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間监氢,已是汗流浹背布蔗。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浪腐,地道東北人纵揍。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像议街,于是被迫代替她去往敵國(guó)和親泽谨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

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

  • 目錄結(jié)構(gòu): 第一步特漩,明確要干嘛 第二步吧雹,怎么去畫(純理論) 第三步,怎么去畫(實(shí)戰(zhàn)) 第四步涂身,練練手 第一步吮炕,明確...
    半紙淵閱讀 8,079評(píng)論 18 57
  • OpenGL學(xué)習(xí)大致的理解 OpenGL為什么會(huì)涉及這么多操作順序。這是因?yàn)榉玫茫臀覀儸F(xiàn)在使用的C++龙亲、JAVA這種...
    wo不懂閱讀 5,266評(píng)論 10 8
  • 這個(gè)文章主要是幫助自己記住以下一些函數(shù)陕凹,opengles很有套路,如果要了解具體如何顯示一個(gè)圖片鳄炉,還是自行百度看其...
    keepKK閱讀 2,010評(píng)論 0 0
  • 在OpenGL中杜耙,大量的數(shù)據(jù)在著色器中傳遞,數(shù)據(jù)通過(guò)Buffer和Texture兩種形式組織拂盯。 1 緩存(Buff...
    RichardJieChen閱讀 8,006評(píng)論 1 10
  • 打開(kāi)一個(gè)應(yīng)用佑女,五分鐘后關(guān)閉。又打開(kāi)一個(gè)應(yīng)用谈竿,五分鐘后又再關(guān)閉团驱。再一次感覺(jué)自己的狀態(tài)---焦慮。 讀過(guò)一篇篇優(yōu)秀的文...
    水平生閱讀 347評(píng)論 0 0