您的位置:首页 > 移动开发 > Objective-C

关于OpenGL中FrameBuffer Object的使用

2016-10-14 18:32 561 查看

问题1:如何在GPU中存放一些texture以后用

问题2:如何把camera传过来的对应TEXTURE_EXTERNAL_OES目标的texture转换为TEXTURE_2D类型使用

Part1:FBO的介绍和学习

OpenGL frambuffer object简称FBO,我们在OpenGL最终的渲染地点在framebuffer中,相比default的framebuffer(连接在屏幕上),OpenGL frambuffer object是用户自定义的,可以用于offscreen render以及将一些texture暂时渲染到这个地方,之后再次使用。下面是几点需要注意的地方:

关于FBO网上我觉得最好的材料是:

http://www.songho.ca/opengl/gl_fbo.html

https://www.opengl.org/wiki/Framebuffer_Object_Examples#Quick_example.2C_render_to_texture_.282D.29

如果英文不好,可以看下对应的部分译文 http://blog.csdn.net/xiajun07061225/article/details/7283929

framebuffer本身是没有内存的,实际上可以把他理解为一个插线板,他上面可以附加一些外设(包含两种:texture和render buffer),而这两种外设是需要分配内存的,同时一个framebuffer可以附加的外设的数量也是有限的,这个最大数量GL_MAX_COLOR_ATTACHMENTS与硬件相关,可以通过glget函数查询。

Part2:在GPU中存放多张待使用的texture

我想要实现的应用是,对camera传来的数据,在GPU中buffer 30帧,之后要以texture的方式要使用这30帧。这件事可以有两种解决方法:

第一,我的手机每个framebuffer最多可以挂载8个texture,那么我就建立4个framebuffer(4*8=32>0),依次把这30帧draw到每一个texture中。

第二,直接建立30个framebuffer,每个framebuffer都只挂载一个texture,之后每帧都draw到每个独立的framebuffer中的texture中。

在网上大部分帖子都推荐第一种方法,原因是,切换同一个framebuffer的不同texture挂载点(这里用gldrawbuffer函数)速度要比切换framebuffer(使用glBindFramebuffer)要快。但是我一开始开开心心的使用第一种方法的时候发现不管怎么整,我明明分配了8个Texture id挂载到一个framebuffer的,然而只有第一个才能用,也就是只有framebuffer的GL_COLOR_ATTACHMENT0可以用,挂载到其他texture的都是不报错,但是draw进去没有一点反应,全是黑的,气cry!如果有人能解决这个问题,就是从camera中读取texture之后draw到一个framebuffer的不同挂载texture的,求告知怎么做~

好,下面说第二种方法怎么做。

首先,你得分配n个空的Texture id,同时分配好空间

public int[] createMultipleTextureObject(int n,int width,int height) {

int[] textures = new int
;
GLES30.glGenTextures(n, textures, 0);
GlUtil.checkGlError("glGenTextures");

for(int k=0;k<n;k++) {
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[k]);
GlUtil.checkGlError("glBindTexture " + textures[k]);

GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER,
GLES30.GL_NEAREST);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER,
GLES30.GL_LINEAR);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S,
GLES30.GL_CLAMP_TO_EDGE);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T,
GLES30.GL_CLAMP_TO_EDGE);
GlUtil.checkGlError("glTexParameter");

GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D,0,GLES30.GL_RGBA8,width,height,0,GLES30.GL_RGBA,GLES30.GL_UNSIGNED_BYTE,null);
GlUtil.checkGlError("glTexImage2D");

}
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
return textures;
}


然后,你要产生n个framebuffer,分别与刚刚产生的texture id绑定,把他们挂载到GL_COLOR_ATTACHMENT0(这里提一句,对于OPENGL ES而言,只有GLES30中可以存在GLES30.GL_COLOR_ATTACHMENTn,而GLES20只有GLES20.GL_COLOR_ATTACHMENT0),采用循环的方式运行下面这个函数,把所以TextureId绑定了

public int creatMultipleFrameBuffer(int TextureId) {

int[] fboId=new int[1];
GLES30.glGenFramebuffers(1, fboId,0);
GlUtil.checkGlError("glGenFramebuffers");

GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fboId[0]);
GlUtil.checkGlError("glBindFramebuffer");

if (GLES30.glIsTexture(TextureId)) {
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER,
GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, TextureId, 0);
GlUtil.checkGlError("glFramebufferTexture2D");
Log.v("Texture","texture is valid");
}

if(GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE)
throw new RuntimeException("glCheckFramebufferStatus not complete");

return fboId[0];

}


最后,你要循环的绑定framebuffer,从camera那里每来一张texture,你就把他draw到你当前的framebuffer里边的那个texture里,这样texture就存在GPU中了。之后想要再把这个存好的texture重新使用起来,比如draw到屏幕上,你需要首先把framebuffer 捆绑到(bind)原来的系统默认framebuffer上(默认framebuffer id为0),之后直接使用texture id绘制就好了,下面是一个将摄像头数据延迟一定时间显示的程序

private void drawFrame() {
//Log.d(TAG, "drawFrame");
if (mEglCore == null) {
Log.d(TAG, "Skipping drawFrame after shutdown");
return;
}

// Latch the next frame from the camera.
mDisplaySurface.makeCurrent();
mCameraTexture.updateTexImage();

mCameraTexture.getTransformMatrix(mTmpMatrix);

// Fill the SurfaceView with it.
SurfaceView sv = (SurfaceView) findViewById(R.id.continuousCapture_surfaceView);

int viewWidth = sv.getWidth();
int viewHeight = sv.getHeight();

int cirIdx=mFrameNum%buffer_TotalNum;
mFullFrameBlit.SelectProgram(1);
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mMultipleFrameBufferId[cirIdx]);
GlUtil.checkGlError("glBindFramebuffer");

GlUtil.checkGlError("glDrawBuffers");

GLES30.glViewport(0, 0, VIDEO_WIDTH, VIDEO_HEIGHT);
GlUtil.checkGlError("glViewport");
mFullFrameBlit.drawFrame(mTextureId, mTmpMatrix, FullFrameRect.DrawType.ENCODE,false);

if(mFrameNum>buffer_TotalNum)
{
cirIdx=(mFrameNum+1)%buffer_TotalNum;
mFullFrameBlit.SelectProgram(0);
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
GlUtil.checkGlError("glBindFramebuffer");
GLES30.glViewport(0, 0, viewWidth, viewHeight);

mFullFrameBlit.drawFrame(mMultipleTextureId[cirIdx], mTmpMatrix, FullFrameRect.DrawType.DISPLAY,true);
mDisplaySurface.swapBuffers();
}

mFrameNum++;

}


Part3:如何转换texture的target

在android中,从camera那里拿过来的texture对应的target只能是GLES11Ext.GL_TEXTURE_EXTERNAL_OES,而我们常用的texture target都是.GL_TEXTURE_2D的,原因是这样:camera拿来的数据是YUV格式的,而普通我们从图像中upload的texture是RGBA格式的,OpenGL ES可以通过在shader language中添加

"#extension GL_OES_EGL_image_external : require\n"


来接收GL_TEXTURE_EXTERNAL_OES的texture,同时将其格式由YUV转换为RGB之后渲染,所以如果你非要想拿到一个GL_TEXTURE_2D类型的texture,你可以使用framebuffer先挂载一个空的GL_TEXTURE_2D类型的texture,之后利用代用上面命令的shader把GL_TEXTURE_EXTERNAL_OES类型的texture绘制到这个texture中去,这样就可以得到一个GL_TEXTURE_2D的texture了,你可以随时使用它
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: