OpenGL系列之十八:FBO離屏渲染

目錄

相關(guān)文章

OpenGL系列之一:OpenGL第一個程序
OpenGL系列之二:繪制三角形
OpenGL系列之三:三角形頂點增加顏色
OpenGL系列之四:繪制四邊形
OpenGL系列之五:繪制點和線
OpenGL系列之六:繪制立方體
OpenGL系列之七:紋理貼圖
OpenGL系列之八:立方體紋理貼圖
OpenGL系列之九:glsl著色器語言
OpenGL系列之十:VAO、VBO、EBO的應(yīng)用
OpenGL系列之十一:Shader圖片轉(zhuǎn)場切換動畫
OpenGL系列之十二:Shader燃燒動畫
OpenGL系列之十三:實現(xiàn)Shader絢麗動畫
OpenGL系列之十四:實現(xiàn)相機抖音特效
OpenGL系列之十五:實現(xiàn)美顏相機
OpenGL系列之十六:實現(xiàn)大眼特效
OpenGL系列之十七:實現(xiàn)人臉貼紙

簡單介紹

FBO(Frame Buffer Object)即幀緩沖區(qū)對象,實際上是一個可添加緩沖區(qū)的容器,可以為其添加紋理或渲染緩沖區(qū)對象(RBO)。
使用 FBO 可以讓渲染操作不用再渲染到屏幕上,而是渲染到離屏 Buffer 中,然后可以使用 glReadPixels 或者 HardwareBuffer 將渲染后的圖像數(shù)據(jù)讀出來,從而實現(xiàn)在后臺利用 GPU 完成對圖像的處理。

實現(xiàn)步驟

1.創(chuàng)建FBO

這里我們是以OpenGL系列之十四:實現(xiàn)相機抖音特效,這篇文章的代碼為基礎(chǔ)
這里我封裝在了CCFBOHelper中,如下所示(這里可以進(jìn)行多次離屏渲染,因此我這里創(chuàng)建了兩個,如果只需要一次那就創(chuàng)建一個就行)
CCFBOHelper.h

#ifndef OPENGLCAMERA_CCFBOHELPER_H
#define OPENGLCAMERA_CCFBOHELPER_H
#include "CCGLPrimitivesDef.h"

class CCFBOHelper {
public:
    GLuint m_FboTextureId[2];
    GLuint m_FboId[2];
    int textureWidth;
    int textureHeight;
    CCFBOHelper();
    ~CCFBOHelper();
    void bindFBO(int index);
    void unBindFBO();
    bool createFBO(int textureWidth,int textureHeight);
};
#endif //OPENGLCAMERA_CCFBOHELPER_H

CCFBOHelper.cpp

#include "CCFBOHelper.h"

CCFBOHelper::CCFBOHelper(){

}
CCFBOHelper::~CCFBOHelper(){

}

bool CCFBOHelper::createFBO(int width,int height) {
    textureWidth = width;
    textureHeight = height;
    for(int i = 0 ; i < 2 ; i++){
        //創(chuàng)建一個 2D 紋理用于連接 FBO 的顏色附著
        glGenTextures(1, &m_FboTextureId[i]);
        glBindTexture(GL_TEXTURE_2D, m_FboTextureId[i]);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glBindTexture(GL_TEXTURE_2D, GL_NONE);

        // 創(chuàng)建 FBO
        glGenFramebuffers(1, &m_FboId[i]);
        // 綁定 FBO
        glBindFramebuffer(GL_FRAMEBUFFER, m_FboId[i]);
        // 綁定 FBO 紋理
        glBindTexture(GL_TEXTURE_2D, m_FboTextureId[i]);
        // 將紋理連接到 FBO 附著
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FboTextureId[i], 0);
        // 分配內(nèi)存大小
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        // 檢查 FBO 的完整性狀態(tài)
        if (glCheckFramebufferStatus(GL_FRAMEBUFFER)!= GL_FRAMEBUFFER_COMPLETE) {
            LOGE("FBOSample::CreateFrameBufferObj glCheckFramebufferStatus status != GL_FRAMEBUFFER_COMPLETE");
            return false;
        }
        // 解綁紋理
        glBindTexture(GL_TEXTURE_2D, GL_NONE);
        // 解綁 FBO
        glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
    }

    return true;
//    LOGE("FBO創(chuàng)建");
}

void CCFBOHelper::bindFBO(int index){
    // 綁定 FBO
    glBindFramebuffer(GL_FRAMEBUFFER, m_FboId[index]);
}
void CCFBOHelper::unBindFBO(){
    // 解綁 FBO
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

2.使用FBO

使用FBO我們只需要在渲染之前綁定FBO即可,如果要想渲染到屏幕上,只需要在渲染前解綁FBO即可,具體如下(這里只展示了部分代碼,這里我離屏渲染了兩次,第一次為渲染獲取相機數(shù)據(jù),第二次為渲染為抖音特效):

extern "C"
JNIEXPORT void JNICALL
Java_com_itfitness_openglcamera_render_GLRender_ndkResizeGL(JNIEnv *env, jobject thiz, jint width,
                                                            jint height) {
    ccRender->resizeGL(width,height);
    bool result = ccfboHelper.createFBO(width,height);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_itfitness_openglcamera_render_GLRender_ndkPaintGL(JNIEnv *env, jobject thiz,
                                                           jint texture_id) {
    ccfboHelper.bindFBO(0);

    ccRender->paintGL(texture_id);

    ccfboHelper.unBindFBO();

    ccfboHelper.bindFBO(1);

    ccRenderDy->paintGL(ccfboHelper.m_FboTextureId[0]);

    ccfboHelper.unBindFBO();

    //最后我們將與 FBO綁定的紋理渲染到屏幕
    ccRender2d->paintGL(ccfboHelper.m_FboTextureId[1]);
}

效果如下


3.獲取FBO數(shù)據(jù)

我們也可以獲取FBO中的數(shù)據(jù),使用OpenCV進(jìn)行處理,如下所示:

void* pixelBuffer = NULL;
extern "C"
JNIEXPORT void JNICALL
Java_com_itfitness_openglcamera_render_GLRender_ndkPaintGL(JNIEnv *env, jobject thiz,
                                                           jint texture_id) {
    ccfboHelper.bindFBO(0);

    ccRender->paintGL(texture_id);

    if(pixelBuffer == NULL){
        pixelBuffer = malloc(ccfboHelper.textureWidth * ccfboHelper.textureHeight * 4);
    } else{
        memset(pixelBuffer,0,ccfboHelper.textureWidth * ccfboHelper.textureHeight * 4);
    }
    glReadPixels(
            0,
            0,
            ccfboHelper.textureWidth,
            ccfboHelper.textureHeight,
            GL_RGBA,
            GL_UNSIGNED_BYTE,
            pixelBuffer
    );

    cv::Mat imageSrc(ccfboHelper.textureHeight, ccfboHelper.textureWidth, CV_8UC4, pixelBuffer);

    cv::flip(imageSrc, imageSrc, 0);

    cv::cvtColor(imageSrc,imageSrc,cv::COLOR_RGBA2BGR);

    //去色濾鏡
    cv::cvtColor(imageSrc,imageSrc,cv::COLOR_RGBA2GRAY);
    cv::cvtColor(imageSrc,imageSrc,cv::COLOR_GRAY2RGBA);

    cv::cvtColor(imageSrc,imageSrc,cv::COLOR_BGR2RGBA);
    glActiveTexture(ccfboHelper.m_FboTextureId[0]);
    glBindTexture(GL_TEXTURE_2D,ccfboHelper.m_FboTextureId[0]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageSrc.cols, imageSrc.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageSrc.data);
    ccfboHelper.unBindFBO();

    ccRender2d->paintGL(ccfboHelper.m_FboTextureId[0]);
    imageSrc.release();

}

案例源碼

https://gitee.com/itfitness/opengl-fbo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容