当前位置: 首页 > 知识库问答 >
问题:

屏幕外渲染(使用FBO和RenderBuffer)和颜色、深度、模板的像素转移

易宣
2023-03-14

我必须在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个屏幕截图(颜色和深度,用模具我得到一个空的图像):

颜色正是我正在绘制的(翻转,但我认为这是正常的)。深度...我期待一张带有渐变灰色三角形的白色图像...当然,我在图像格式上犯了一些错误(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_模具

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

感谢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;
}

共有2个答案

雍马鲁
2023-03-14

深度图似乎可以从以下代码中看出:

 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。

此外,单独的深度模板也有助于提高性能。尽可能使用组合格式。

尼科尔·博拉斯的答案和我写的一样。此外,他还指出了内存泄漏。

乜承嗣
2023-03-14

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

您的代码并没有这样建议。

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

这将创建一个整数数组。这也是内存泄漏;使用std::向量

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

这会告诉OpenGL将浮点写入整数数组。因此,在编写时,OpenGL将像素视为GLfloats的数组。

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

我假设这将解释为某种每分量8位的整数图像格式。所以你把floats解释为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位是模板。

 类似资料:
  • 我想在FBO中加载两个纹理,其中一个纹理包含HDR图像,我的第一个目标是将图像从第一个纹理“复制”到第二个纹理(该纹理为空),并称为“下采样纹理”。 所以我创建FBO,加载我想用颜色_附件_0书写的纹理,并绑定它;然后初始化我的着色器程序并渲染一个四边形,其中包含我要在GL\u texture\u 0中读取的纹理。 然后我解开FBO并绑定“DownSamplingTex”,然后画一个四元组。 我不

  • 问题内容: 从gluOrtho角度看,我已经在OpenGL中渲染了3D场景。在我的应用程序中,我正在查看体积为100x70x60mm(我为1000x700x600像素)的立方体的正面。在此立方体内部,我绘制了一个简单的蓝色球体,该球体恰好位于中间并“填充”立方体(半径300像素)。 现在,我想读取立方体内特定点的像素的颜色值(以3D表示);即我想知道说点(100,100,-200)是蓝色还是空白(

  • 我有一个问题,我认为它与屏幕渲染及其生命周期有关。基本上我有两个屏幕(菜单和游戏)。在GameScreen渲染方法中,我称之为World。更新,然后我的渲染。在(GameScreen的)隐藏方法中,我从Redner类中处理SpriteBatch。 因此,当我将屏幕从游戏更改为菜单(intheworld.update)时,Java崩溃。据我所知,飞机失事了。所以我的问题是,当我在渲染周期中间设置一个

  • 我正试图通过使用在中显示一个带有QT的Coin3D/Open Inventor场景,我需要帮助将其转换为 到目前为止,我尝试的是将场景渲染到中,并获得如下缓冲区: 然后从缓冲区数据创建一个: 这是正确的方法吗? 正如在这个关于绘制图像的问题中所描述的,如果源是一张图片,我就能够绘制它。 e:为了确保我的缓冲区实际上包含一个场景,我将缓冲区内容写入两个文件。例如,您可以使用Irfan View及其插

  • 6.1 渲染模板 一旦你拥有一个模版文件,你可以通过给一个map来给它传递数据。 map是一个变量及赋予的值的集合,模板使用它来得到变量的值,或者对于块标签求值。 它的渲染函数有一个可选的变量键值对map 通过 ctx.Render() 方法来渲染模板,例如: func (r *Render) Serve(ctx *faygo.Context) error { return ctx.Ren

  • Tango默认核心不包含模板渲染功能,在官方中间件中包含两个渲染引擎中间件,一个是 Go标准模板引擎, 另一个是 Pongo2模板引擎