45

使用 OpenGL 为 Camera 添加各种滤镜

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzIxNzU1Nzk3OQ%3D%3D&%3Bmid=2247489541&%3Bidx=3&%3Bsn=5a822a52a0b24f2fc1c587ac386afb01
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

封面出自: 板栗懒得很

 本章仅对部分代码进行讲解,以帮助读者更好的理解章节内容。本系列文章涉及的项目HardwareVideoCodec已经开源到Github,支持软编和硬编。使用它你可以很容易的实现任何分辨率的视频编码,无需关心摄像头预览大小。一切都如此简单。目前已迭代多个稳定版本,欢迎查阅学习和使用,如有BUG或建议,欢迎Issue。

  在第二章中,我们通过一个Camera SurfaceTexture纹理,把摄像头数据绘制到这个纹理上,同时TextureView的SurfaceTexture纹理通过id与第一个纹理关联起来,从而把摄像头画面直接绘制到屏幕上。

  对OpenGL有一定了解的人可能会知道,要使用OpenGL渲染各种好看的特效,FBO必不可少。通过FBO,我们可以先把摄像头数据绘制到Camera SurfaceTexture纹理上,然后把这个纹理数据再绘制到一个离屏的FBO,我们可以在这个FBO上做各种特效处理,处理完之后再把离屏FBO中的数据绘制到TextureView的SurfaceTexture纹理,也就是手机屏幕上。

zm6JbyN.png!web

滤镜绘制流程,从上图中我们不难看出:

  1. 两端的纹理都包含EGL环境,只有中间FBO没有,因为FBO只是作为缓存的作用,不跟任何设备有关联。

  2. 其实Camer SurfaceTexture也需要一个FBO,因为需要把摄像头数据缓存到这个FBO,那为什么TextureView却不需要呢,因为广义上来说,屏幕的缓存就是它的FBO。

  3. 从数据流方向来看,Camer SurfaceTexture从Camera中读取数据缓存到自己的FBO中,然后离屏FBO通过Camer SurfaceTexture的ID读取Camera FBO中的数据缓存到自己的内存中,在这里可以对这个FBO做各种处理,最后TextureView从离屏FBO的ID读取缓存的数据绘制到屏幕上。

 在前两章的基础上,我们已经有了Camera SurfaceTexture和TextureView SurfaceTexture,现在我们需要再加入一层离屏FBO。

一、向OpenGL申请FBO,frameBuffer和frameBufferTexture:

  1. frameBuffer指向OpenGL的一块内存。

  2. frameBufferTexture可以把frameBuffer中的数据作为一个纹理绑定到OpenGL。

open fun initFrameBuffer() {

val frameBuffer = IntArray(1)

val frameBufferTex = IntArray(1)

GLES20.glGenFramebuffers(1, frameBuffer, 0)

GLES20.glGenTextures(1, frameBufferTex, 0)

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameBufferTex[0])

GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null)

GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,

GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat())

GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,

GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR.toFloat())

GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,

GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE.toFloat())

GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,

GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE.toFloat())

GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer[0])

GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, frameBufferTex[0], 0)

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)

GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)

val error = GLES20.glGetError()

if (error != GLES20.GL_NO_ERROR) {

val msg = "initFrameBuffer: glError 0x" + Integer.toHexString(error)

debug_e(msg)

return

}

//FBO,通过这个往FBO内存中写入数据

frameBuffer = frameBuffer[0]

//FBO ID,通过这个从FBO的内存中读取数据

frameBufferTexture = frameBufferTex[0]

}

二、现在我们有了frameBuffer和frameBufferTexture,接下来我们把Camera SurfaceTexture FBO中的数据绘制到这个frameBuffer中。

private fun drawCamera() {

if (null != cameraWrapper.surfaceTexture) {

cameraSurfaceTexture?.updateTexImage()

cameraSurfaceTexture?.getTransformMatrix(transformMatrix)

}

cameraEgl?.makeCurrent()

GLES20.glViewport(0, 0, cameraPreviewHeight, cameraPreviewWidth)

GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)

GLES20.glClearColor(0.3f, 0.3f, 0.3f, 0f)


GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer!!)

GLES20.glUseProgram(shaderProgram!!)

GLES20.glActiveTexture(GLES20.GL_TEXTURE0)

GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraSurfaceTextureId)

GLES20.glUniform1i(uTextureLocation, 0)

enableVertex(aPositionLocation, aTextureCoordinateLocation, buffer!!, verticesBuffer!!)

GLES20.glUniformMatrix4fv(uTextureMatrix, 1, false, transformMatrix, 0)


drawer.draw()


GLES20.glFinish()

GLES20.glDisableVertexAttribArray(aPositionLocation)

GLES20.glDisableVertexAttribArray(aTextureCoordinateLocation)

GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_NONE)

GLES20.glUseProgram(GLES20.GL_NONE)

GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_NONE)

}

三、现在frameBuffer中已经有了数据,在这里可以运用OpenGL对数据进行处理,这里以简单的GreyFilter为例,这里就不贴具体代码了。

private fun drawFilter() {

synchronized(filterLock) {

if (null == filter) return

GLES20.glViewport(0, 0, parameter.video.width, parameter.video.height)

GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)

filter?.drawTexture(null)

}

}

四、最后通过frameBufferTexture把数据绘制到屏幕。

fun drawScreen(){

screenWrapper?.egl?.makeCurrent()

//根据需要对画面进行裁剪

GLES20.glViewport(viewportX, viewportY, width, height)

GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)

GLES20.glClearColor(0.3f, 0.3f, 0.3f, 0f)

screenWrapper?.drawTexture(transformMatrix)

screenWrapper?.egl?.swapBuffers()

}

  以上就是本章关于OpenGL为Camera添加各种滤镜的所有内容,至于如何编写OpenGL Shape实现各种滤镜,由于内容比较多,以后有机会单独另开章节详细介绍。

本章知识点:

  1. FBO的使用。

  2. OpenGL纹理绘制的基本流程。

本章相关源码·HardwareVideoCodec项目:

  • DefaultRenderImpl

  • CameraTextureWrapper

  • GreyFilter

  • ScreenTextureWrappe

相关阅读:

B3Q7faa.jpg!web

如果你想要跟大家分享你的文章

欢迎投稿


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK