通过帧缓冲区对象渲染到纹理

Posted

技术标签:

【中文标题】通过帧缓冲区对象渲染到纹理【英文标题】:Render to texture via the framebuffer object 【发布时间】:2016-04-09 03:31:31 【问题描述】:

我初始化帧缓冲区。然后,在一个循环中,我将场景渲染为纹理,处理其着色器并推断屏幕。在我的电脑上一切正常。 (Radeon HD 7870。)在另一台 PC (GeForce FX 5200) 上,函数 glCheckFramebufferStatusEXT 返回错误“8cdd”并以 0-1 fps 的帧速率渲染黑屏。

源代码:

#include "main.hpp"

GLuint fbo, fbo_texture, rbo_depth;
GLuint vbo_fbo_vertices;
GLuint program_postproc, attribute_v_coord_postproc, uniform_fbo_texture;
GLuint vs, fs;
Shader shader;

int main(void)

    init();

    glGenFramebuffersEXT(1, &fbo);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
    glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &fbo_texture);
    glBindTexture(GL_TEXTURE_2D, fbo_texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_texture, 0);

    GLenum status;
    if ((status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)) != GL_FRAMEBUFFER_COMPLETE_EXT) 
        fprintf(stderr, "glCheckFramebufferStatus: error %p", status);
    
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

    GLfloat fbo_vertices[] =  -1, -1, 1, -1,-1,  1, 1,  1 ;
    glGenBuffers(1, &vbo_fbo_vertices);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_fbo_vertices);
    glBufferData(GL_ARRAY_BUFFER, sizeof(fbo_vertices), fbo_vertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    shader.load("shaders/post_processing.vert", "shaders/post_processing.frag");

    attribute_v_coord_postproc = glGetAttribLocation(shader.program(), "v_coord");
    if (attribute_v_coord_postproc == -1) 
        fprintf(stderr, "Could not bind attribute %s\n", "v_coord");
        return 0;
    
    uniform_fbo_texture = glGetUniformLocation(shader.program(), "fbo_texture");
    if (uniform_fbo_texture == -1) 
        fprintf(stderr, "Could not bind uniform %s\n", "fbo_texture");
        return 0;
    

    while (!glfwWindowShouldClose(m_window))
    
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
        glClear(GL_COLOR_BUFFER_BIT);
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
        glClear(GL_COLOR_BUFFER_BIT);
        shader.use();
        glBindTexture(GL_TEXTURE_2D, fbo_texture);
        glUniform1i(uniform_fbo_texture, /*GL_TEXTURE*/0);
        glEnableVertexAttribArray(attribute_v_coord_postproc);
        glBindBuffer(GL_ARRAY_BUFFER, vbo_fbo_vertices);
        glVertexAttribPointer(attribute_v_coord_postproc, 2, GL_FLOAT, GL_FALSE, 0, 0);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        glDisableVertexAttribArray(attribute_v_coord_postproc);
        glfwSwapBuffers(m_window);
        glfwPollEvents();
    

    glDeleteRenderbuffersEXT(1, &rbo_depth);
    glDeleteTextures(1, &fbo_texture);
    glDeleteFramebuffersEXT(1, &fbo);
    glDeleteBuffers(1, &vbo_fbo_vertices);
    glDeleteProgram(shader.program());
    glfwDestroyWindow(m_window);
    glfwTerminate();
    exit(EXIT_SUCCESS);


void callbackError(int error, const char* description)

    fputs(description, stderr);


void callbackKey(GLFWwindow* window, int key, int scancode, int action, int mods)

    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);


void callbackFramebufferSize(GLFWwindow* window, int width, int height)

    m_width = width; m_height = height;
    glBindTexture(GL_TEXTURE_2D, fbo_texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    glBindTexture(GL_TEXTURE_2D, 0);


void init()

    glfwSetErrorCallback(callbackError);
    if (!glfwInit()) exit(EXIT_FAILURE);
    m_width = 800; m_height = 600;
    m_window = glfwCreateWindow(m_width, m_height, "Framebuffer Test", NULL, NULL);
    if (!m_window)  glfwTerminate(); exit(EXIT_FAILURE); 
    glfwMakeContextCurrent(m_window);
    glfwSwapInterval(0);
    glfwSetKeyCallback(m_window, callbackKey);
    glfwSetFramebufferSizeCallback(m_window, callbackFramebufferSize);
    glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK) std::cout << "GLEW Init Error" << std::endl;
    glClearColor(0.2, 0.3, 0.4, 1.0);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

结果:

http://itmag.es/4KxQ5/

http://itmag.es/H5RD/

【问题讨论】:

您使用的是 OpenGL 还是 OpenGL ES? GeForce FX 5200 已经很老了,可能不支持你的FBO configuration。抱歉,我知道的不够多,无法为您提供更多帮助。 【参考方案1】:

返回值0x8cdd1是常量gl_FRAMEBUFFER_UNSUPPORTED。 来自documentation

如果附加图像的内部格式组合违反了与实现相关的一组限制,则返回 GL_FRAMEBUFFER_UNSUPPORTED。

要找出问题所在,您可以做几件事:

检查返回值

GLenum lastErr:
...
if ((lastErr = glGetError())!= GL_NO_ERROR)

    fprintf(stderr, "<lastFunctionCalledHere>: error %s", gluErrorString(lastErr));
    return <returnCodeHere>;

检查是否存在 GL_EXT_framebuffer_object 扩展。

使用glGetIntegerv(GL_NUM_EXTENSIONS, ...) 获取分机数量,而不是使用glGetStringi​(GL_EXTENSIONS, i) i 小于所述数量。

检查实施限制

如果以上都没有找到原因,请尝试使用glGetXXX 检索一些有用的值,并使用GL_PROXY_TEXTURE_2D 测试纹理创建。

【讨论】:

以上是关于通过帧缓冲区对象渲染到纹理的主要内容,如果未能解决你的问题,请参考以下文章

WebGL入门(四十一)-使用帧缓冲区对象(FBO)实现将渲染结果作为纹理绘制到另一个物体上

纹理和渲染缓冲区是不是在帧缓冲区对象上共享相同的空间?

OpenGL 帧缓冲区 - 渲染到纹理创建带有主帧缓冲区内容的纹理

OpenGL ES 学习教程(十四) 帧缓冲区对象(FBO) 实现渲染到纹理(Render To Texture/RTT)

JOGL 和帧缓冲区渲染到纹理的问题:无效帧缓冲区操作错误

如何在多采样纹理上渲染帧缓冲区对象?