opengl fbo 离屏渲染

时间:2024-05-19 15:16:22

之前的博客opengl 离屏渲染,实现相机的实时滤镜讲到过离屏渲染,主要是android层的应用,这里在jni层来实现,并来讲解下原理,以及和android层的不同的地方。

samplerExternalOES  、 sampler2D区别

#extension GL_OES_EGL_image_external : require
uniform samplerExternalOES vTexture;

samplerExternalOES  纹理

是直接的相机渲染的纹理着色器,相机数据会直接推送到这个纹理着色器当中,在自己做相机app,进行滤镜的时候用到。并且它的纹理映射点 和  顶点的映射在 博客:android opengl camera2 实现相机的实时预览 以及 点坐标samplerExternalOES的纹理坐标种讲到,这里就丢出个图:在设置顶点  和 纹理映射点的时候,只要对应即可。

           opengl fbo 离屏渲染

(-1,-1) ------>(0 ,1)

(-1,1) ------>(0 ,0)

(1,-1) ------>(1 ,1)

(1,1) ------>(1 ,0)

sampler2D 生成的纹理

它的纹理映射则是上下相反的,即当我们设计顶点和纹理的对应点的时候,需要 :

(-1,-1) ------>(0 ,0)

(-1,1) ------>(0 ,1)

(1,-1) ------>(1 ,0)

(1,1) ------>(1 ,1)

纹理显示才正确

opengl离屏渲染

平时我们设置shader,然后draw是将我们的纹理图像绘制到了系统默认的0号frambuffer中,这个buffer用户是读取不到的,只是用来显示渲染用。因此为了离屏渲染,我们需要自己创建frambuffer,当然我们自己创建的frambuffer是可以读取到里面的colorbuffer的,即texure。

创建frambuffer

glGenFramebuffers(1 , &fboObject);

这样创建了frambuffer我们还不能用来绑定,因为frambuffer得含有三部分:

colorBuffer:即所谓的texture的buffer

创建和纹理的创建一样:

void CreateTexture2D( GLuint* textures ,int number ,unsigned char *pixelData, int width, int height ,GLenum type){
    glGenTextures(number ,textures);
    for(int i=0 ;i< number ; i++) {
        glBindTexture(GL_TEXTURE_2D, textures[i]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// 表示图像放大时候,使用线性过滤
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);// 表示图像缩小时候,使用线性过滤
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexImage2D(GL_TEXTURE_2D, 0, type, width, height, 0, type, GL_UNSIGNED_BYTE,
                     pixelData);//GL_RGBA
        glBindTexture(GL_TEXTURE_2D, 0);
    }

}

depthBuffer:只让opengl读取,用户无法读取,但还是得创建

stencilBuffer:模板buffer,同样只有opengl读取,但也得创建。

其中后面的2个buffer,组成RenderBuffer,RenderBuffer第一个为depthbuffer,第二个为stencilBuffer

创建2个buffer,只需要创建RenderBuffer

    //生成renderbuffer   
 glGenRenderbuffers(1, &rbo);
    glBindRenderbuffer(GL_RENDERBUFFER,rbo);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, height);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    // 挂载 depth buffer fbo上
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
    //挂载stencil buffer fbo上
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);

node:这里的width和height都为纹理渲染的大小,若renderbuffer中的glRenderbufferStorage小了,可能导致图像纹理显示不全。

使用frambuffer

创建好了renderBuffer 和 colorbuffer后,调用下面函数就可以bind frambuffer,然后我们渲染的图像就会会知道我们定义的framebuffer中,屏幕将不会显示我们的图片,这时候如果调用filter,即其他的图像处理shader,处理我们定义的frambuffer中的colorbuffer,即texture, 就可以达到多次处理的效果了。

void fbo::bind(){
    glBindFramebuffer(GL_FRAMEBUFFER , fboObject);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,colorBuffer[(currentColorBufferIndex + 1)%2],0);
    currentColorBufferIndex = (currentColorBufferIndex + 1)%2;
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);

}

解除frambuffer

void fbo::unBind() {
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

这里给出demo,给大家参考。