【问题标题】:off-screen rendering with fbo and texture got black screen使用 fbo 和纹理进行离屏渲染时出现黑屏
【发布时间】:2018-02-27 16:48:24
【问题描述】:

我正在 mac os x (10.12) 上开发一个简单的离屏渲染应用程序,但总是黑屏。我确认shader编译链接没问题,把贴图改成单色也没问题。然后我评论了有关深度信息的代码,但没有效果。单步调试时,没有发现glGetError返回错误。下面是代码,请问有什么问题?

GLuint program;
if (compile_link_shader(vertex_shader_source, fragment_shader_source, &program) < 0) {
   base_error_log("call compile_link_shader failed\n");
   return -1;
}
glViewport(0, 0, target->width, target->height);

GLuint vao, vbo, ibo, loc_attr;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ibo);

glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);

glBufferData(GL_ARRAY_BUFFER, sizeof(pos_coord), pos_coord, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_index), element_index, GL_STATIC_DRAW);

loc_attr = _static_cast(GLuint) glGetAttribLocation(program, "vertexPosition");
glVertexAttribPointer(loc_attr, 2, GL_FLOAT, GL_FALSE,
                          4 * sizeof(GLfloat), _static_cast(GLvoid*) (0 * 2 * sizeof(GLfloat)));
glEnableVertexAttribArray(loc_attr);

loc_attr = (GLuint) glGetAttribLocation(program, "textureCoordinate");
glVertexAttribPointer(loc_attr, 2, GL_FLOAT, GL_FALSE,
                          4 * sizeof(GLfloat), (GLvoid*) (1 * 2 * sizeof(GLfloat)));
glEnableVertexAttribArray(loc_attr);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
GLuint texture;
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/* glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // Removed from GL 3.1 and above */

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
GLenum format = origin->channels == 3 ? GL_RGB : GL_RGBA;
glTexImage2D(GL_TEXTURE_2D, 0, format, origin->width, origin->height, 0,
                 format, GL_UNSIGNED_BYTE, origin->image);
glGenerateMipmap(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, 0);

GLuint fbo;
GLuint depthBufferName;
glGenRenderbuffers(1, &depthBufferName);
glBindRenderbuffer(GL_RENDERBUFFER, depthBufferName);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, origin->width, origin->height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);

glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferName);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        base_error_log("glCheckFramebufferStatus not GL_FRAMEBUFFER_COMPLETE\n");
    return -1;
}

glUseProgram(program);

GLint loc_uniform = glGetUniformLocation(program, "texture");
glUniform1i(loc_uniform, texture);

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);

glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glDrawElements(GL_TRIANGLES, 2 * 3, GL_UNSIGNED_INT, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

glReadBuffer(GL_COLOR_ATTACHMENT0);
target->image = malloc(_static_cast(size_t) (target->width * target->height * target->channels));
if (!target->image) {
     base_error_log("malloc target->image failed\n");
     return -1;
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glReadPixels(0, 0, target->width, target->height, (target->channels == 3 ? GL_RGB : GL_RGBA),
                 GL_UNSIGNED_BYTE, target->image);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

还有一部分是:

#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
#define GLEW_STATIC
#include <GL/glew.h>
#include <OpenGL/OpenGL.h>
#include <OpenGL/gl3.h>
#include <OpenGL/gl3ext.h>
static GLfloat pos_coord[] = {
        // Vertex Positions     // Texture Coordinates
        -1.0f,  1.0f,           0.0f, 1.0f, // Left Top
         1.0f,  1.0f,           1.0f, 1.0f, // Right Top
         1.0f, -1.0f,           1.0f, 0.0f, // Right Bottom
        -1.0f, -1.0f,           0.0f, 0.0f, // Left Bottom
};
static GLuint element_index[] = { // Note that we start from 0 with GL_CCW!
        0, 3, 2, // First Triangle
        0, 2, 1, // Second Triangle
};
static const char *vertex_shader_source = ""
        "#version 330 core\n"
        "precision mediump float;\n"
        "in vec2 vertexPosition;\n"
        "in vec2 textureCoordinate;\n"
        "out vec2 varyingTextureCoordinate;\n"
        "void main() {\n"
        "    gl_Position = vec4(vertexPosition, 0.0, 1.0);\n"
        "    varyingTextureCoordinate = textureCoordinate;\n"
        "}";
static const char *default_fragment_shader_source = ""
        "#version 330 core\n"
        "precision mediump float;\n"
        "in vec2 varyingTextureCoordinate;\n"
        "out vec4 fragmentColor;\n"
        "uniform sampler2D texture;\n"
        "void main() {\n"
        "    fragmentColor = texture(texture, varyingTextureCoordinate);\n"
       /* "    fragmentColor = vec4(1.0, 1.0, 0.0, 1.0);\n" */ // this line work fine
        "}";

以下代码具有相同的效果:黑屏。是不是同样的问题?

glViewport(0, 0, target->width, target->height);

GLuint vao, vbo, ibo, loc_attr;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ibo);

glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);

glBufferData(GL_ARRAY_BUFFER, sizeof(pos_coord), pos_coord, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_index), element_index, GL_STATIC_DRAW);

loc_attr = _static_cast(GLuint) glGetAttribLocation(program, "vertexPosition");
glVertexAttribPointer(loc_attr, 2, GL_FLOAT, GL_FALSE,
                      4 * sizeof(GLfloat), _static_cast(GLvoid*) (0 * 2 * sizeof(GLfloat)));
glEnableVertexAttribArray(loc_attr);

loc_attr = (GLuint) glGetAttribLocation(program, "textureCoordinate");
glVertexAttribPointer(loc_attr, 2, GL_FLOAT, GL_FALSE,
                      4 * sizeof(GLfloat), (GLvoid*) (1 * 2 * sizeof(GLfloat)));
glEnableVertexAttribArray(loc_attr);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

glActiveTexture(GL_TEXTURE0);
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/* glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // Removed from GL 3.1 and above */

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
GLenum format = origin->channels == 3 ? GL_RGB : GL_RGBA;
glTexImage2D(GL_TEXTURE_2D, 0, format, origin->width, origin->height, 0,
             format, GL_UNSIGNED_BYTE, origin->image);
glGenerateMipmap(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, 0);

GLuint fbo;
GLuint colorBufferName, depthBufferName;

glGenRenderbuffers(1, &colorBufferName);
glBindRenderbuffer(GL_RENDERBUFFER, colorBufferName);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, origin->width, origin->height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);

glGenRenderbuffers(1, &depthBufferName);
glBindRenderbuffer(GL_RENDERBUFFER, depthBufferName);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, origin->width, origin->height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);

glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBufferName);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferName);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
    base_error_log("glCheckFramebufferStatus not GL_FRAMEBUFFER_COMPLETE\n");
    return -1;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);

glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glUseProgram(program);

GLint loc_uniform = glGetUniformLocation(program, "texture");
glUniform1i(loc_uniform, texture);

glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glDrawElements(GL_TRIANGLES, 2 * 3, GL_UNSIGNED_INT, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);

glDrawBuffer(GL_COLOR_ATTACHMENT0);
glReadBuffer(GL_COLOR_ATTACHMENT0);

target->image = malloc(_static_cast(size_t) (target->width * target->height * target->channels));
if (!target->image) {
    base_error_log("malloc target->image failed\n");
    return -1;
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glReadPixels(0, 0, target->width, target->height, (target->channels == 3 ? GL_RGB : GL_RGBA),
             GL_UNSIGNED_BYTE, target->image);

glBindFramebuffer(GL_FRAMEBUFFER, 0);

【问题讨论】:

    标签: c macos opengl textures framebuffer


    【解决方案1】:

    texture 是写入帧缓冲区的颜色附件,纹理绑定到读取的纹理采样器。这是一种未定义的行为。

    Khronos OpenGL 文档Framebuffer Object - Feedback Loops 说:

    可以将纹理绑定到 FBO,将相同的纹理绑定到着色器,然后尝试同时使用它进行渲染。
    将一个图像从纹理绑定到 FBO,然后使用该纹理进行渲染是完全有效的,只要您阻止自己从该图像中采样。如果您确实尝试读取和写入同一图像,则会得到未定义的结果。这意味着它可能会做你想做的事,采样器可能会得到旧数据,采样器可能会得到一半旧数据和一半新数据,或者它可能会得到垃圾数据。这些都是可能的结果。
    不要这样做。您将得到的是未定义的行为。


    由于texture 是活动帧缓冲区的颜色附件,glClear 命令将清除纹理

    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
    
    .....
    
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    

    如果要渲染到纹理,则必须创建第二个纹理,将其附加到帧缓冲区颜色平面并写入。

    另见:


    答案的扩展

    您必须将纹理单元的索引设置为纹理采样器统一,而不是纹理对象本身。纹理单元由glActiveTexture 设置,glBindTexture 将纹理绑定到目标(例如GL_TEXTURE_2D)和活动纹理单元。
    顺便说一句,你必须重命名统一的名称texture,因为它与GLSL 函数的名称texture 相同。在下面的代码中,我使用u_texture

    GLint loc_uniform = glGetUniformLocation( program, "u_texture" );
    
    .....
    
    int textureUnitIndex = 0; // 0, 1, 2 ...
    glActiveTexture( GL_TEXTURE0 + textureUnitIndex );
    glBindTexture( GL_TEXTURE_2D, texture );
    
    .....
    
    glUseProgram( program );
    glUniform1i( loc_uniform, textureUnitIndex ); // <-- index of the texture unit
                                                  //     NOT the texture object
    

    【讨论】:

    • 非常感谢您的精彩解释。我用两种纹理修复它。如果我改用颜色渲染缓冲区,是我创建两个帧缓冲区和位复制缓冲区的唯一方法吗?[添加到问题中]
    • 非常感谢您的完美建议,程序恢复正常并正常工作,我也明白了很多!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-20
    相关资源
    最近更新 更多