【源碼解讀】Android Opengl OES 紋理怎么渲染到 GL_TEXTURE_2D吉懊?

背景

在客戶端中存在一種應(yīng)用場(chǎng)景:需要將 MediaCodec 或者 Camera 產(chǎn)生的圖像,通過 OpenGL 交給算法做特效假勿,由于算法可能是基于普通的 Texture2D 紋理實(shí)現(xiàn)的借嗽,而 Android 上更常用的則是 GL_TEXTURE_EXTERNAL_OES 紋理,算法一般都是基于 OpenGL 而不是 OpenGLES 環(huán)境實(shí)現(xiàn)的转培,所以就需要客戶端這邊做一個(gè)轉(zhuǎn)換工作恶导。

這個(gè)轉(zhuǎn)換工作當(dāng)然最好是在 GPU 中能完成的,因?yàn)槿绻ㄟ^ CPU 從 OES 紋理中讀出圖像數(shù)據(jù)浸须,再提交到 2D 紋理中惨寿,這一來一回,即浪費(fèi) CPU 頁(yè)占有了內(nèi)存删窒,很不劃算裂垦。所以就出現(xiàn)了這篇文章,如何利用 OpenGL 將 OES 紋理渲染到普通 2D 紋理上肌索。

GL_TEXTURE_EXTERNAL_OES 紋理

外部 GLES 紋理 (GL_TEXTURE_EXTERNAL_OES) 與傳統(tǒng) GLES 紋理 (GL_TEXTURE_2D) 的區(qū)別如下:

  • 外部紋理直接在從 BufferQueue 接收的數(shù)據(jù)中渲染紋理多邊形蕉拢。
  • 外部紋理渲染程序的配置與傳統(tǒng)的 GLES 紋理渲染程序不同。
  • 外部紋理不一定可以執(zhí)行所有傳統(tǒng)的 GLES 紋理活動(dòng)。

外部紋理的主要優(yōu)勢(shì)是它們能夠直接從 BufferQueue 數(shù)據(jù)進(jìn)行渲染晕换。在 Android 平臺(tái)上午乓,BufferQueue 是連接圖形數(shù)據(jù)生產(chǎn)方和消費(fèi)方的隊(duì)列,也就表示 OES 紋理能直接拿到某些生產(chǎn)方產(chǎn)生的圖形數(shù)據(jù)進(jìn)行渲染届巩。

OES Texture 渲染到 TEXTURE_2D

比如現(xiàn)在有個(gè)需求:使用 MediaCodec 解碼視頻硅瞧,最終需要將解碼的每一幀渲染到外部設(shè)置的一個(gè) TEXTURE_2D 紋理上。

實(shí)現(xiàn)方案:MediaCodec 支持將解碼結(jié)果輸出到 Surface 中恕汇,我們可以通過構(gòu)造一個(gè)綁定了 OES 紋理的 SurfaceTexture 來為 MediaCodec 構(gòu)造一個(gè)輸出 Surface腕唧。當(dāng)解碼結(jié)果寫入到 Surface 的 BufferQueue 之后,再利用 SurfaceTexture 將結(jié)果從 BufferQueue 渲染到 OES 紋理上瘾英,然后再通過 OpegGL 管道流水線操作將 OES 紋理上的內(nèi)容渲染到 TEXTURE_2D 紋理:

MediaCodec 解碼到 Surface 偽代碼如下:

oesTextureId = x
sTexture = SurfaceTexture(oesTextureId)
outputSurface = Surface(sTexture)
decoder.setOutputSurface(outputSurface)
復(fù)制代碼

這里可以借鑒 grafika 中 Buffer 的生成和消費(fèi)流程:

然后在參考了 grafika 的流程后設(shè)計(jì)的流程:

正如上圖所示枣接,從 TextureOES 到 Texture2D 的關(guān)鍵是利用 FBO(幀緩沖)。在執(zhí)行 OpenGL 渲染之前缺谴,開始 FBO但惶,渲染完成之后關(guān)閉 FBO。

幀緩沖實(shí)現(xiàn)

如果我們不額外設(shè)置 OpenGL 的幀緩沖湿蛔,OpenGL 所有操作都將在默認(rèn)幀緩沖的渲染緩沖上進(jìn)行膀曾;如果我們激活了自己的幀緩沖,也就是在綁定到 GL_FRAMEBUFFER 目標(biāo)之后阳啥,所有的讀取和寫入幀緩沖的操作將會(huì)影響當(dāng)前綁定的幀緩沖添谊。

所以這里的操作是:創(chuàng)建一個(gè)幀緩沖,將 Texture2D 紋理作為它的顏色緩沖察迟,然后在利用 Shader 從 TextureOES 紋理上采樣之前將這個(gè)幀緩沖設(shè)置為 OpenGL 上下文當(dāng)前激活的幀緩沖斩狱。這樣設(shè)置之后就相當(dāng)于,將 TextureOES 采樣到幀緩沖中扎瓶,而幀緩沖背后又是 Texture2D所踊,就間接的將 TextureOES 采樣到了 Texture2D 上。


class DecodeFBO {

    private var mFrameBuffer = -1

    init {
        val tmp = IntArray(1)
        GLES30.glGenFramebuffers(1, tmp, 0)
        SLGLUtils.checkGlError("glGenFrameBuffer")
        mFrameBuffer = tmp[0]
    }

    /**
     * 綁定 FBO 到 Texture2D 紋理
     */
    fun begin(texture2D: Int) {
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFrameBuffer)
        SLGLUtils.checkGlError("glBindFrameBuffer")

        //將紋理作為幀緩沖對(duì)象的顏色緩沖
        GLES30.glFramebufferTexture2D(
            GLES30.GL_FRAMEBUFFER,
            GLES30.GL_COLOR_ATTACHMENT0,
            GLES30.GL_TEXTURE_2D,
            texture2D,
            0
        )
        checkGlError("glFramebufferTexture2D")
        val status = GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER)
        if (status != GLES30.GL_FRAMEBUFFER_COMPLETE) {
            Log.e(TAG, "bind FBO failed概荷!")
            return
        }
    }

    fun end() {
        GLES30.glFramebufferTexture2D(
            GLES30.GL_FRAMEBUFFER,
            GLES30.GL_COLOR_ATTACHMENT0,
            GLES30.GL_TEXTURE_2D,
            0,
            0
        )
        checkGlError("detach texture from FBO")
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
        checkGlError("deactivate FBO")
    }

    fun release() {
        GLES30.glDeleteFramebuffers(1, IntArray(1) { mFrameBuffer }, 0)
        checkGlError("glDeleteFramebuffers")
    }
}
復(fù)制代碼

著色器實(shí)現(xiàn)

這里的著色器就不復(fù)雜了秕岛,就是從一個(gè)紋理上采樣,然后設(shè)置給 gl_FragColor误证。

頂點(diǎn)著色器:

private static final String VERTEX_SHADER =
        "uniform mat4 uMVPMatrix;\n" +
                "attribute vec4 aPosition;\n" +
                "attribute vec4 aTextureCoord;\n" +
                "varying vec2 vTextureCoord;\n" +
                "void main() {\n" +
                "  gl_Position = uMVPMatrix * aPosition;\n" +
                "  vTextureCoord = aTextureCoord.xy;\n" +
                "}\n";
復(fù)制代碼

片段著色器:

private static final String FRAGMENT_SHADER =
        "#extension GL_OES_EGL_image_external : require\n" +
                "precision mediump float;\n" +
                "varying vec2 vTextureCoord;\n" +
                "uniform sampler2D sTexture;\n" +
                "void main() {\n" +
                "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
                "}\n";

作者:StefanJi
鏈接:https://juejin.cn/post/7012517274768179236
來源:稀土掘金
著作權(quán)歸作者所有继薛。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處雷厂。

更多Android技術(shù)分享可以關(guān)注@我,也可以加入QQ群號(hào):Android進(jìn)階學(xué)習(xí)群:345659112惋增,一起學(xué)習(xí)交流叠殷。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末改鲫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌像棘,老刑警劉巖稽亏,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異缕题,居然都是意外死亡截歉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門烟零,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘪松,“玉大人,你說我怎么就攤上這事锨阿∠溃” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵墅诡,是天一觀的道長(zhǎng)壳嚎。 經(jīng)常有香客問我,道長(zhǎng)末早,這世上最難降的妖魔是什么烟馅? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮然磷,結(jié)果婚禮上郑趁,老公的妹妹穿的比我還像新娘。我一直安慰自己样屠,他們只是感情好穿撮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著痪欲,像睡著了一般悦穿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上业踢,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天栗柒,我揣著相機(jī)與錄音,去河邊找鬼知举。 笑死瞬沦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的雇锡。 我是一名探鬼主播逛钻,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼锰提!你這毒婦竟也來了曙痘?” 一聲冷哼從身側(cè)響起芳悲,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎边坤,沒想到半個(gè)月后名扛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茧痒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年肮韧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旺订。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡弄企,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出区拳,到底是詐尸還是另有隱情桩蓉,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布劳闹,位于F島的核電站院究,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏本涕。R本人自食惡果不足惜业汰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望菩颖。 院中可真熱鬧样漆,春花似錦、人聲如沸晦闰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)呻右。三九已至跪妥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間声滥,已是汗流浹背眉撵。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留落塑,地道東北人纽疟。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像憾赁,于是被迫代替她去往敵國(guó)和親污朽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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

  • 系列文章: 安卓特效相機(jī)(一) Camera2的使用安卓特效相機(jī)(二) EGL基礎(chǔ)安卓特效相機(jī)(三) OpenGL...
    嘉偉咯閱讀 4,089評(píng)論 2 14
  • 本文主要介紹龙考,如何使用 OpenGL ES 來渲染一張圖片蟆肆。內(nèi)容包括:基礎(chǔ)概念的講解错蝴,如何使用 GLKit 來渲染...
    雷曼同學(xué)閱讀 4,834評(píng)論 11 18
  • 一、前言 OpenGL是Khronos Group開發(fā)維護(hù)的一個(gè)規(guī)范颓芭,它主要為我們定義了用來操作圖形和圖片的一些列...
    dpplh閱讀 853評(píng)論 0 0
  • 本系列所有文章目錄 獲取示例代碼 本文將介紹渲染到紋理技術(shù)。之前的例子都是將3D物體渲染到屏幕上柬赐,在iOS中GLK...
    handyTOOL閱讀 10,750評(píng)論 5 9
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月亡问,有人笑有人哭,有人歡樂有人憂愁肛宋,有人驚喜有人失落州藕,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,536評(píng)論 28 53