概述
上一章探讨了如何采用SurfaceTexture+GLSurfaceView显示YUV数据,减少了片段着色器的工作量和代码量,但是采用GLSL采用的外部纹理真正的内容是在物理内存中,GPU只负责维护元数据,这样就增加了GPU取数据的时间,若进行一些运算密集的算法例如高斯滤波,每次都会到外部纹理取数据,这样则会造成明显的卡顿,所以仅采用外部纹理实现实时滤镜行不通。同时采用JNI格式转换随着预览分辨率提高时间也达不到要求,采用Shader转换则每次加权计算都需要进行格式转换,也增加了运算量。
好在OpenGL允许用户创建帧缓存对象,可以将数据渲染到纹理。因此我们可以将对应相机预览图像的外部纹理先渲染到内部纹理中,剩下的滤镜再绑定到该纹理,对其进行操作,这样就增加渲染速度,经实验帧数达到要求。
以下代码和思路参考GPUImage中的GPUImageFilterGroup,因为Android平台采用的是OpenGL ES,与OpenGL略有不同。
一.分配帧缓冲对象:
1)创建帧缓冲对象:
有N+1个滤镜(其中第一个从外部纹理接收的无滤镜效果),就需要分配N个帧缓冲对象,首先创建大小为N的两个数组mFrameBuffers和mFrameBufferTextures,分别用来存储缓冲区id和纹理id,通过GLES20.glGenFramebuffers(1, mFrameBuffers, i)来创建帧缓冲对象
2)创建纹理:
创建输出纹理,方法基本相同,不同之处在于glTexImage2D最后一个参数为null,不指定数据指针。
3)绑定帧缓冲区:
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[i]),第一个参数是target,指的是你要把FBO与哪种帧缓冲区进行绑定。
4)绑定纹理:
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,GLES20.GL_TEXTURE_2D, mFrameBufferTextures[i], 0)
glFramebufferTexture2D()把一幅纹理图像关联到一个FBO。第一个参数一定是GL_FRAMEBUFFER_,第二个参数是关联纹理图像的关联点。一个帧缓冲区对象可以有多个颜色关联点(GL_COLOR_ATTACHMENT0, ..., GL_COLOR_ATTACHMENTn),L_DEPTH_ATTACHMENT, 和GL_STENCIL_ATTACHMENT。第三个参数textureTarget在多数情况下是GL_TEXTURE_2D。第四个参数是纹理对象的ID号。最后一个参数是要被关联的纹理的mipmap等级 如果参数textureId被设置为0,那么纹理图像将会被从FBO分离。如果纹理对象在依然关联在FBO上时被删除,那么纹理对象将会自动从当前帮的FBO上分离。然而,如果它被关联到多个FBO上然后被删除,那么它将只被从绑定的FBO上分离,而不会被从其他非绑定的FBO上分离。
5)解绑默认帧缓存和纹理:
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
二.依次绘制:
首先第一个一定是绘制与SurfaceTexture绑定的外部纹理处理后的无滤镜效果,之后的操作与第一个一样,都是绘制到纹理。首先与之前相同传入纹理id,并重新绑定到对应的缓冲区对象GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[i]),之后draw对应的纹理id。若不是最后一个滤镜,需要解绑缓冲区,下一个滤镜的新的纹理id即上一个滤镜的缓冲区对象所对应的纹理id,同样执行上述步骤,直到最后一个滤镜。
三.关键代码:
1)创建部分:
for (int i = 0; i < size - 1; i++) {
GLES20.glGenFramebuffers(1, mFrameBuffers, i);
GLES20.glGenTextures(1, mFrameBufferTextures, i);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mFrameBufferTextures[i]);
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);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[i]);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, mFrameBufferTextures[i], 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
}
绘制部分:
for (int i = 0; i < size; i++) {
GPUImageFilter filter = mMergedFilters.get(i);
boolean isNotLast = i < size - 1;
if (isNotLast) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[i]);
GLES20.glClearColor(0, 0, 0, 0);
}
if (i == 0) {
filter.onDraw(previousTexture, cubeBuffer, textureBuffer);
} else if (i == size - 1) {
filter.onDraw(previousTexture, mGLCubeBuffer, (size % 2 == 0) ? mGLTextureFlipBuffer : mGLTextureBuffer);
} else {
filter.onDraw(previousTexture, mGLCubeBuffer, mGLTextureBuffer);
}
if (isNotLast) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
previousTexture = mFrameBufferTextures[i];
}
根据上述思路,我们可以对GPUImage进行修改,首先是取消JNI部分转换,将SurfaceTexure与GLSurfaceView绑定(GPUImage中虽然使用了SurfaceTexture,但明显没有用到它的作用,并且addPreviewCallback也使用错误,毕竟2年没有修改过了)。然后选取滤镜时,将Camera输出到缓冲区对象,选取的滤镜对该纹理进行渲染即可。可以重构GPUImage中的滤镜基类,将所有滤镜的基类都归为Group滤镜,然后修改Group滤镜的一些方法。
最后
以上就是靓丽乐曲为你收集整理的android动画帧缓存,Android平台Camera实时滤镜实现方法探讨(六)--创建帧缓存对象(FBO)加速实时滤镜处理...的全部内容,希望文章能够帮你解决android动画帧缓存,Android平台Camera实时滤镜实现方法探讨(六)--创建帧缓存对象(FBO)加速实时滤镜处理...所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复