【问题标题】:Render off screen (with FBO and RenderBuffer) and pixel transfer of color, depth, stencil离屏渲染(使用 FBO 和 RenderBuffer)和颜色、深度、模板的像素传输
【发布时间】:2013-07-17 22:54:15
【问题描述】:

我必须在 OpenGL 中渲染屏幕外,然后将图像传递给 QImage。另外,为了练习,我想将深度和模板缓冲区也传输到 CPU。

为了在屏幕外绘图,我使用了带有渲染缓冲区的帧缓冲区对象(而不是纹理,因为我不需要纹理)。

使用颜色缓冲区(实际的原始图像)进行像素传输操作。我明白了我的期望。但是深度和模板不起作用.. 深度的奇怪图像和模板没有。

首先,简单的部分,我实际绘制的内容:

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glTranslatef(-1.5f,0.0f,-6.0f);
    glBegin(GL_TRIANGLES);
    glColor3f(1.0f,0.0f,0.0f);
    glVertex3f( 0.0f, 1.0f, 0.0f);
    glColor3f(0.0f,1.0f,0.0f);
    glVertex3f(-1.0f,-1.0f, -1.0f);
    glColor3f(0.0f,0.0f,1.0f);
    glVertex3f( 1.0f,-1.0f, 0.0f);
    glEnd();

这里是 FBO 和 3 渲染缓冲区的初始化:

// frame buffer object
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);

// render buffer as color buffer
glGenRenderbuffers(1, &colorBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as color buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);

// render buffer as depth buffer
glGenRenderbuffers(1, &depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as depth buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);

glGenRenderbuffers(1, &stencilBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as stencil buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_BUFFER, GL_RENDERBUFFER, stencilBuffer);

最后这里有 3 种像素传输方法:

QImage FBO::getImage()
{
    // this is called Pixel Transfer operation: http://www.opengl.org/wiki/Pixel_Transfer
    uchar *pixels;
    pixels = new uchar[width * height * 4];
    for(int i=0; i < (width * height * 4) ; i++ ) {
        pixels[i] = 0;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glReadPixels( 0,0,  width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);
    qi = qi.rgbSwapped();

    return qi;
}

QImage FBO::getDepth()
{
    // this is called Pixel Transfer operation: http://www.opengl.org/wiki/Pixel_Transfer
    uchar *pixels;
    pixels = new uchar[width * height * 4];
    for(int i=0; i < (width * height * 4) ; i++ ) {
        pixels[i] = 0;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
    glReadPixels( 0,0,  width, height, GL_DEPTH_COMPONENT, GL_FLOAT, pixels);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);
    qi = qi.rgbSwapped();

    return qi;
}

QImage FBO::getStencil()
{
    // this is called Pixel Transfer operation: http://www.opengl.org/wiki/Pixel_Transfer
    uchar *pixels;
    pixels = new uchar[width * height * 4];
    for(int i=0; i < (width * height * 4) ; i++ ) {
        pixels[i] = 0;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer);
    glReadPixels( 0,0,  width, height, GL_STENCIL_INDEX, GL_FLOAT, pixels);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);
    qi = qi.rgbSwapped();

    return qi;

}

这里是 2 个屏幕截图(颜色和深度,使用模板我得到一个空的 QImage):

颜色一正是我正在绘制的(翻转但我认为这是正常的)。深度..我期待一个带有渐变灰色三角形的白色图像..肯定我在图像格式上犯了一些错误(GL_FLOAT?)但我尝试了一些组合,这是最好的结果..紫罗兰色背景,里面有毛刺的颜色..还有模板缓冲区..我期待黑色背景中有一个白色三角形轮廓,但是..不知道为什么我什么都看不到..

编辑:

好的,永远不要单独使用模板缓冲区,所以我做了一点重构。

在声明 FBO 时:

// render buffer for both depth and stencil buffer
glGenRenderbuffers(1, &depthStencilBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach render buffer to the fbo as depth buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilBuffer);

像素转移的深度:

QImage FBO::getDepth()
{
    std::vector<uchar> pixels;
    pixels.reserve(width * height*4);
    for(int i=0; i < (width * height*4) ; i++ ) {
        pixels.push_back(0);
    }

    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
    glReadPixels( 0,0,  width, height,  GL_DEPTH24_STENCIL8, GL_UNSIGNED_BYTE, pixels.data());

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    std::vector<uchar> _pixels;
    _pixels.reserve(width * height*4);
    for(int i=0; i < (width * height*4) ; i++ ) {
        uchar p_red = pixels[i];
        uchar p_green = pixels[i+1];
        uchar p_blue = pixels[i+2];

        uchar p_stencil = pixels[i+3];

        _pixels.push_back(p_red);
        _pixels.push_back(p_green);
        _pixels.push_back(p_blue);

        _pixels.push_back(255); // alpha
    }

    QImage qi = QImage(_pixels.data(), width, height, QImage::Format_ARGB32);
    //qi = qi.rgbSwapped();

    return qi;
}

模板类似,但使用p_stencil 和 rgb 组件

.. 结果图像是深度和模板的黑色图像

编辑

感谢 Nicolas 的回答,我设法将渲染缓冲区用于深度和模板缓冲区,并使用以下代码提取深度组件以适应 QImage::Format_ARGB32

QImage FBO1::getDepth()
{
    // sizeof( GLuint ) = 4 byte
    // sizeof( uchar ) = 1 byte

    std::vector<GLuint> pixels(width * height);
    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
    glReadPixels( 0,0,  width, height,  GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, pixels.data());
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    std::vector<uchar> _pixels;
    for(int i=0; i < (width * height) ; i++ ) {
        GLuint color = pixels[i];

        float temp = (color & 0xFFFFFF00) >> 8; // 24 bit of depth component

        // remap temp that goes from 0 to 0xFFFFFF (24 bits) to
        // _temp that goes from 0 to 0xFF (8 bits)
        float _temp = (temp / 0xFFFFFF) * 0xFF;

        _pixels.push_back((uchar)_temp);
        _pixels.push_back((uchar)_temp);
        _pixels.push_back((uchar)_temp);
        _pixels.push_back(255);
    }

    QImage qi = QImage(_pixels.data(), width, height, QImage::Format_ARGB32);
    qi = qi.rgbSwapped();

    return qi;
}

.. 模板组件仍有一些问题(以下代码不起作用:它会产生故障图像):

QImage FBO1::getStencil()
{
    // sizeof( GLuint ) = 4 byte
    // sizeof( uchar ) = 1 byte

    std::vector<GLuint> pixels(width * height);
    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
    glReadPixels( 0,0,  width, height,  GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, pixels.data());
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    std::vector<uchar> _pixels;
    for(int i=0; i < (width * height); i++ ) {
        GLuint color = pixels[i];

        uchar temp = (color & 0x000000FF); // last 8 bit of depth component

        _pixels.push_back(temp);
        _pixels.push_back(temp);
        _pixels.push_back(temp);
        _pixels.push_back(255);
    }

    QImage qi = QImage(_pixels.data(), width, height, QImage::Format_ARGB32);
    qi = qi.rgbSwapped();

    return qi;
}

【问题讨论】:

    标签: c++ opengl fbo depth-buffer stencil-buffer


    【解决方案1】:

    深度..我期待一个带有渐变灰色三角形的白色图像..

    您的代码不建议这样做。

    uchar *pixels;
    pixels = new uchar[width * height * 4];
    

    这将创建一个 整数数组。这也是内存泄漏;请改用std::vector&lt;uchar&gt;

    glReadPixels( 0,0,  width, height, GL_DEPTH_COMPONENT, GL_FLOAT, pixels);
    

    这告诉 OpenGL 将 浮点数 写入您的 整数 数组。所以OpenGL在写的时候会把pixels当成一个GLfloats的数组。

    QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);
    

    我假设这会将其解释为某种每组件 8 位 integer 图像格式。因此,您将 floats 解释为 8 位整数。没有理由期望这会理性行事。

    您应该将深度缓冲区读取为浮点数并以更合理的方式将其转换为像素颜色,或者您应该将深度缓冲区读取为整数值,让 OpenGL 为您进行灰度转换。

    此外,您应该始终使用combined depth/stencil image,而不是两个单独的渲染缓冲区。


    glReadPixels( 0,0,  width, height,  GL_DEPTH24_STENCIL8, GL_UNSIGNED_BYTE, pixels.data());
    

    你似乎不明白pixel transfers 的工作原理。

    首先,pixel transfer format 指定您正在阅读的组件。它确实指定它们的大小。 GL_DEPTH24_STENCIL8an image format,不是像素传输格式。如果您想从图像中读取深度和模板,请使用GL_DEPTH_STENCIL。像素传输格式没有大小。

    所以这个函数只是给你一个 OpenGL 错误。

    大小来自第二个参数pixel transfer type。在这种情况下,GL_UNSIGNED_BYTE 意味着它将读取深度和模板,将每个转换为一个无符号的 8 位值,并将其中两个每个像素存储到 pixels.data()

    深度缓冲区每个像素仅存储 1 个值。深度/模板仅存储 2。您无法使用 OpenGL 将它们复制为每像素 4 个组件的格式。因此,无论您以后如何构建 QImage,它必须使用某种方法,每个像素需要 1 或 2 个值。

    一般来说,如果您想读取深度缓冲区,并且希望深度缓冲区的数据真正有意义,您可以这样做:

    std::vector<GLuint> pixels(width * height);  //std::vector value-initializes its elements.
    
    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glReadPixels( 0,0,  width, height,  GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, pixels.data());
    

    如何将它们放在 QImage 中进行可视化取决于您。但这会得到一个无符号整数数组,其中高 24 位是深度分量,低 8 位是模板。

    【讨论】:

    • 好的,谢谢,深度缓冲区没问题,但后来我重构了深度和模板在同一个缓冲区中,并且......全黑。查看编辑
    • 哇,这比我通常在我的计算机上的深度要深得多。谢谢你的旅行!
    • 无论如何,我可以通过移位操作正确地提取前 24 位的深度并将它们重新映射到 QImage ARGB32,但我仍然遇到模板故障。我是否必须使用 glEnable(GL_STENCIL_TEST); 以外的东西来启用它?
    【解决方案2】:

    嗯,深度图片看起来可以从这段代码中得到:

     glReadPixels( 0,0,  width, height, GL_DEPTH_COMPONENT, GL_FLOAT, pixels);
    
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
     QImage qi = QImage(pixels, width, height, QImage::Format_ARGB32);
    

    QImage 不知道如何处理浮点数,因此它将每组 32 位(在浮点内)解释为好像它们是 ARGB 分量,每组 8 位。浮点数的低位有 25 位尾数,可以很好地映射到这些组件中。你看到的那些带只是增加的尾数,被裁剪成一些位数。

    QImage 确实是一个非常有限的东西(它甚至不能处理 HDR 配置,比如每通道 16 位,这有点令人沮丧)。无论如何,最好的办法是将这个浮点数转换为 0…255 的范围,并将其作为灰度图像传递给 QImage。

    另外,单独的深度模板不利于性能。尽可能使用组合格式。

    ……嗯,尼可波拉斯的回答来了,和我写的一样。另外,他指出了内存泄漏。

    【讨论】:

    • 好的,谢谢,深度缓冲区没问题,但后来我重构了深度和模板在同一个缓冲区中,并且......全黑。查看编辑
    • @nkint 我不确定使用组合附件是否是个好主意,因为我猜 Qt 也无法对此做出有意义的事情。 glReadPixels 也不适用于组合格式。这就是为什么它都是黑色的。模板附件根本不起作用的一个很好的原因可能是您在第一个示例中使用了GL_STENCIL_BUFFER 而不是GL_STENCIL_ATTACHMENT(如果这不是错字的话)。
    • @nkint 好的,glReadPixels 确实适用于组合格式,但仅适用于有限范围的类型(您可能想要的是 GL_UNSIGNED_INT_24_8)而不适用于调整大小的内部格式(改用普通的GL_DEPTH_STENCIL)。
    • ohu.. 你是对的.. 如果我将GL_STENCIL_ATTACHMENT 与仅用于模板的渲染缓冲区一起使用,我会得到GL_FRAMEBUFFER_UNSUPPORTED
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-27
    相关资源
    最近更新 更多