OpenGLES_FBO使用

基本知识

 1. Android系统默认渲染器 OpenGL,系统启动时,经过BootLoader启动,kernel启动---->init进程启动核心进程(ServiceManager,zygote,OpenGL)---->播放开机动画
        OpenGL渲染管线的最后一个阶段就是帧缓冲区(FrameBuffer)

 2. OpenGL渲染管线的最后阶段FrameBuffer,Android系统存在默认缓冲区(window-system-provided frame),用于屏幕显示。GPU往显示缓冲区写入数据时,屏幕会显示缓冲内容。
        使用FBO可以让数据不渲染到屏幕上,渲染到离屏的Buffer中
        Android中后台给视频添加水印,Camera实时滤镜,需要把原数据经过处理后保存但是不显示数据

 3.对于默认的相机,使用系统提供的OpenGL渲染的帧缓冲区(OES),OpenGL所有渲染结果直接到达帧缓冲区---->on-Srceen渲染方式
       对于贴纸相机,使用帧缓冲区对象,OpenGL将提供给窗口的帧缓冲区重定向FBO之中

 4. FBO提供缓冲区:颜色缓冲区,深度缓冲区,模板缓冲区
        FBO提供2种绑定的对象:纹理图片(texture images) 和 渲染图像(renderbuffer images) 
        4.1 纹理绑定FBO,OpenGL执行渲染到纹理操作
        4.2 渲染绑定FBO,OpenGL执行离屏渲染
        4.3 通过GL_MAX_COLOR_ATTACHMENTS查询颜色缓冲区挂节点
        4.4 纹理对象 glFramebufferTexture2D
            渲染对象 glFramebufferRenderbuffer
 5. FBO (Frame Buffer Object) 帧缓冲区对象,FBO本身不能用于渲染,只有添加了纹理或者渲染缓冲区后才能作为渲染目标            

 6. FBO 使用流程图

FBO

使用

1.搭建基本OpenGLES FBO 仅输出 纹理对象

1.1

1
2
protected int[] mFrameBuffers;
protected int[] mFrameBufferTextures;

1.2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
GlUtil.checkGlError(TAG, "[onReady()][Start]");
super.onReady(width, height);
if (mFrameBuffers != null) {
destroyFrameBuffers();
}
mFrameBuffers = new int[1];
//1 创建fbo
GLES20.glGenFramebuffers(mFrameBuffers.length, mFrameBuffers, 0);
mFrameBufferTextures = new int[1];
//2 创建fbo纹理
GlUtil.glConfigureTextures(mFrameBufferTextures);
//3 绑定纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mFrameBufferTextures[0]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mOutputWidth, mOutputHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
//4 fbo绑定纹理
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[0]);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mFrameBufferTextures[0], 0);
//5 解绑
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GlUtil.checkGlError(TAG, "[onReady()][End]");

2.使用FBO处理图片滤镜(自己创建FrameBuffer,同时输出到纹理对象,渲染对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public void createEnvi() {
Log.d(TAG, "[createEnvi]");
//创建帧缓冲对象
GLES20.glGenFramebuffers(1, fFrame, 0);

//创建渲染缓冲对象
GLES20.glGenRenderbuffers(1, fRender, 0);
//相似地,我们打算把渲染缓冲对象绑定,这样所有后续渲染缓冲操作都会影响到当前的渲染缓冲对象
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, fRender[0]);
//创建一个深度和模板渲染缓冲对象
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, mBitmap.getWidth(), mBitmap.getHeight());
//附加帧缓冲 渲染缓冲对象附加到帧缓冲的深度和模板附件
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, fRender[0]);
//解绑渲染缓冲
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0);


//fTexture size 2
//fTexture[0] 普通纹理(由bitmap转成)
//fTexture[1] 帧缓冲纹理(GLES20.glTextImage2D)
GLES20.glGenTextures(2, fTexture, 0);
for (int i = 0; i < 2; i++) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fTexture[i]);
if (i == 0) {
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mBitmap, 0);
} else {//纹理的维度设置为图片的大小,传递null作为纹理的data,只分配内存,不写入数据
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mBitmap.getWidth(), mBitmap.getHeight(), 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
}
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_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);
}
mBuffer = ByteBuffer.allocate(mBitmap.getWidth() * mBitmap.getHeight() * 4);
}

//绑定执行
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fFrame[0]);

//将创建好的帧缓冲纹理附加到帧缓冲 纹理对象
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, fTexture[1], 0);

//FBO挂接 渲染对象 Renderbuffer 渲染缓冲 对象的一大优点是,它以OpenGL原生渲染格式储存它的数据,因此在离屏渲染到帧缓冲的时候,这些数据就相当于被优化过的了
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, fRender[0]);

GLES20.glViewport(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
mFilter.setTextureId(fTexture[0]);
mFilter.draw();


GLES20.glReadPixels(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mBuffer);
if (mCallback != null) {
mCallback.onCall(mBuffer);
}

deleteEnvi();
mBitmap.recycle();

3. 使用FBO处理相机纹理

3.1获取相机纹理后,最后返回步骤1中生成的mFrameBufferTextures[0],即可对相机纹理进行多个滤镜或者变换处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
GlUtil.checkGlError(TAG, "[onDrawFrame()][Start]");
GLES20.glViewport(left_margin, 0, mOutputWidth, mOutputHeight);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[0]);
GLES20.glUseProgram(mGLProgramId);

mGLVertexBuffer.position(0);
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, mGLVertexBuffer);
GLES20.glEnableVertexAttribArray(vPosition);
mGLTextureBuffer.position(0);
GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT, false, 0, mGLTextureBuffer);
GLES20.glEnableVertexAttribArray(vCoord);

GLES20.glUniformMatrix4fv(vMatrix, 1, false, matrix, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
GLES20.glUniform1i(vTexture, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GlUtil.checkGlError(TAG, "[onDrawFrame()][Start]");
return mFrameBufferTextures[0];//返回创建好的纹理对象----->进行后续的操作

4.释放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 private void deleteEnvi() {
GLES20.glDeleteTextures(2, fTexture, 0);
GLES20.glDeleteRenderbuffers(1, fRender, 0);
GLES20.glDeleteFramebuffers(1, fFrame, 0);
}


private void destroyFrameBuffers() {
if (mFrameBufferTextures != null) {
GLES20.glDeleteTextures(1, mFrameBufferTextures, 0);
mFrameBufferTextures = null;
}

if (mFrameBuffers != null) {
GLES20.glDeleteFramebuffers(1, mFrameBuffers, 0);
mFrameBuffers = null;
}
}

5.参考

  1. MrYeLiang Android-OpenGL-Filter 相机纹理3次FBO纹理变化
  2. 湖广午王 AndroidOpenGLDemo FBO 图片灰度滤镜

6.Camera2+GLSurfaceView+FBO