繁体   English   中英

渲染出屏幕(使用FBO和RenderBuffer)以及颜色,深度,模版的像素传输

[英]Render off screen (with FBO and RenderBuffer) and pixel transfer of color, depth, stencil

我必须在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;
}

模具是相似的,但使用带有rgb组件的p_stencil

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

编辑

多亏了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;
}

深度..我期望带有渐变灰色三角形的白色图像。

您的代码不建议这样做。

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

这将创建一个整数数组。 这也是内存泄漏; 使用std::vector<uchar>代替。

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

这告诉OpenGL将浮点数写入整数数组。 因此,OpenGL在写入时会将pixels视为GLfloat的数组。

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

我假设这将解释为某种8位每分量整数图像格式。 因此,您将float解释为8位整数。 没有理由期望这种行为合理。

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

另外,您应该始终使用组合的深度/模板图像 ,而不是两个单独的渲染缓冲区。


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

您似乎不了解像素传输的工作原理。

首先, 像素传输格式指定要读取的组件。 没有指定其大小。 GL_DEPTH24_STENCIL8图像格式 ,而不是像素传输格式。 如果要读取图像的深度和模版,请使用GL_DEPTH_STENCIL 像素传输格式没有大小。

因此,此功能只是给您一个OpenGL错误。

大小来自第二个参数,即像素传输类型 在这种情况下, 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位是模板。

好吧,深度图片看起来可以从此代码中看出:

 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。

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

……好吧,Nicol Bolas给出了答案,与我写的答案是正确的。 另外,他指出了内存泄漏。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM