【原始碼解讀】Android Opengl OES 紋理怎麼渲染到 GL_TEXTURE_2D?
在客戶端中存在一種應用場景:需要將 MediaCodec 或者 Camera 產生的影像,透過 OpenGL 交給演算法做特效,由於演算法可能是基於普通的 Texture2D 紋理實現的,而 Android 上更常用的則是 GL_TEXTURE_EXTERNAL_OES 紋理,演算法一般都是基於 OpenGL 而不是 OpenGLES 環境實現的,所以就需要客戶端這邊做一個轉換工作。
GL_TEXTURE_EXTERNAL_OES 紋理
外部 GLES 紋理 (GL_TEXTURE_EXTERNAL_OES) 與傳統 GLES 紋理 (GL_TEXTURE_2D) 的區別如下:
-
外部紋理直接在從 BufferQueue 接收的資料中渲染紋理多邊形。
-
外部紋理渲染程式的配置與傳統的 GLES 紋理渲染程式不同。
-
外部紋理不一定可以執行所有傳統的 GLES 紋理活動。
外部紋理的主要優勢是它們能夠直接從 BufferQueue 資料進行渲染。在 Android 平臺上,BufferQueue 是連線圖形資料生產方和消費方的佇列,也就表示 OES 紋理能直接拿到某些生產方產生的圖形資料進行渲染。
OES Texture 渲染到 TEXTURE_2D
比如現在有個需求:使用 MediaCodec 解碼影片,最終需要將解碼的每一幀渲染到外部設定的一個 TEXTURE_2D 紋理上。
實現方案:MediaCodec 支援將解碼結果輸出到 Surface 中,我們可以透過構造一個繫結了 OES 紋理的 SurfaceTexture 來為 MediaCodec 構造一個輸出 Surface。當解碼結果寫入到 Surface 的 BufferQueue 之後,再利用 SurfaceTexture 將結果從 BufferQueue 渲染到 OES 紋理上,然後再透過 OpegGL 管道流水線操作將 OES 紋理上的內容渲染到 TEXTURE_2D 紋理:
MediaCodec 解碼到 Surface 虛擬碼如下:
oesTextureId = x
sTexture = SurfaceTexture(oesTextureId)
outputSurface = Surface(sTexture)
decoder.setOutputSurface(outputSurface)
複製程式碼
這裡可以借鑑 grafika 中 Buffer 的生成和消費流程:
然後在參考了 grafika 的流程後設計的流程:
正如上圖所示, 從 TextureOES 到 Texture2D 的關鍵是利用 FBO(幀緩衝) 。在執行 OpenGL 渲染之前,開始 FBO,渲染完成之後關閉 FBO。
幀緩衝實現
如果我們不額外設定 OpenGL 的幀緩衝,OpenGL 所有操作都將在預設幀緩衝的渲染緩衝上進行;如果我們啟用了自己的幀緩衝,也就是在繫結到
GL_FRAMEBUFFER
目標之後,所有的讀取和寫入幀緩衝的操作將會影響當前繫結的幀緩衝。
所以這裡的操作是:建立一個幀緩衝,將 Texture2D 紋理作為它的顏色緩衝,然後在利用 Shader 從 TextureOES 紋理上取樣之前將這個幀緩衝設定為 OpenGL 上下文當前啟用的幀緩衝。這樣設定之後就相當於,將 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")
//將紋理作為幀緩衝物件的顏色緩衝
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")
}
}
複製程式碼
著色器實現
這裡的著色器就不復雜了,就是從一個紋理上取樣,然後設定給
gl_FragColor
。
頂點著色器:
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";
複製程式碼
片段著色器:
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
來源:稀土掘金
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69983917/viewspace-2794433/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- OpenGL 紋理詳解
- OpenGL Android課程六:介紹紋理過濾Android
- OpenGL Android課程四:介紹紋理基礎Android
- OpenGL ES 壓縮紋理
- Android OpenGL ES 2.0 手把手教學(6)- 紋理Android
- OpenGL/OpenGL ES入門: 紋理應用 - 紋理座標及案例解析(金字塔)
- OpenGL/OpenGL ES入門:紋理初探 - 常用API解析API
- 一文詳解 OpenGL ES 紋理顏色混合
- 第四個OpenGL程式,texture 紋理
- 圖形學_opengl紋理對映
- OpenGL ES 高階進階:紋理陣列陣列
- 簡單的 OpenGL 紋理貼圖不起作用?
- Android硬體加速(二)-RenderThread與OpenGL GPU渲染AndroidthreadGPU
- Android Handler訊息機制原始碼解讀Android原始碼
- 怎麼閱讀原始碼【除錯觀察原始碼】原始碼除錯
- SDL3 入門(5):紋理渲染
- Spring原始碼解讀之BeanFactoryPostProcessor的處理Spring原始碼Bean
- OpenGL 紋理取樣 在GPU中哪個部件完成GPU
- eKuiper 原始碼解讀:從一條 SQL 到流處理任務的旅程UI原始碼SQL
- Vue 原始碼解讀(10)—— 編譯器 之 生成渲染函式Vue原始碼編譯函式
- Android API開發之OpenGL開發之Android OpenGL STL詳解AndroidAPI
- 詳解 OpenGL ES 2.x 渲染流程
- android php 亂碼怎麼解決AndroidPHP
- Android 程式碼跟蹤到native怎麼辦Android
- OpenGL/OpenGL ES入門: 影象渲染實現以及渲染問題
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- OpenGL 之 GPUImage 原始碼分析GPUUI原始碼
- PostgreSQL 原始碼解讀(225)- Transaction(子事務處理)SQL原始碼
- android下vulkan與opengles紋理互通Android
- Laravel 原始碼解讀Laravel原始碼
- reselect原始碼解讀原始碼
- Swoft 原始碼解讀原始碼
- Seajs原始碼解讀JS原始碼
- ReentrantLock原始碼解讀ReentrantLock原始碼
- MJExtension原始碼解讀原始碼
- Axios 原始碼解讀iOS原始碼
- SDWebImage原始碼解讀Web原始碼
- MJRefresh原始碼解讀原始碼