OpenGL:如何从缓存中读取颜色、深度信息【转】

23 篇文章 4 订阅
19 篇文章 0 订阅

GPU渲染完数据在显存,回传内存的唯一方式glReadPixels函数

显存也被叫做显示内存、帧缓存,它是用来存储显示芯片处理过或者即将读取的渲染数据。如同计算机的内存一样,显存是用来存储图形数据的硬件。在显示器上显示出的画面是由一个个的像素点构成的,而每个像素点都以4至64位的数据来控制它的亮度和色彩,这些点构成一帧的图形画面。为了保持画面流畅,要输出和要处理的多幅帧的像素数据必须通过显存来保存,达到缓冲效果,再交由显示芯片和CPU调配,最后把运算结果转化为图形输出到显示器上
glReadPixels:读取一些像素。当前可以简单理解为“把已经绘制好的像素(它可能已经被保存到显卡的显存中)读取到内存”。

注意:glReadPixels实际上是从缓冲区中读取数据,如果使用了双缓冲区,则默认是从正在显示的缓冲(即前缓冲)中读取,而绘制工作是默认绘制到后缓冲区的。因此,如果需要读取已经绘制好的像素,往往需要先交换前后缓冲。

颜色缓冲区

         OpenGL在绘制图元时,先是在一个缓冲区中完成渲染,然后再把渲染结果交换到屏幕上。我们把这两个缓冲区称为前颜色缓冲区(屏幕)和后颜色缓冲区。在默认情况下,OpenGL命令是在后颜色缓冲区进行渲染的。当然,也可以直接在前颜色缓冲区中进行渲染。
        若要在前颜色缓冲区中进行渲染,第一种方法是直接告诉OpenGL希望在前颜色缓冲区中进行绘图,可以调用下面这个函数来实现这个目的:
        void glDrawBuffer(Glenum mode);
        如果参数mode指定为GL_FRONT,OpenGL就会在前颜色缓冲区中进行渲染;
        如果参数mode指定为GL_BACK,那么渲染将在后颜色缓冲区中进行。
        在前颜色缓冲区进行渲染的第二种方法是在OpenGL被初始化时简单地不要求进行双缓冲区渲染。进行单缓冲区渲染时,如果希望把渲染结果实际绘制到屏幕上,需要调用glFlush()或glFinsh(),这点非常重要。
        OpenGL实现除了支持单纯的前颜色缓冲区和后颜色缓冲区之外,还支持其他模式,如用于立体渲染的左和右缓冲区以及辅助缓冲区。

方法一、保存为图片

QImage* img=new QImage(WINDOW_WIDTH,WINDOW_HEIGHT,QImage::Format_ARGB32);
    uchar* tmpBIT = img->bits();
 
 
    //从颜色缓冲区中读取数据
    int tmpPixelSize = WINDOW_WIDTH*WINDOW_HEIGHT * 4;
    char* tmpPixelsBuffer = (char*)malloc(tmpPixelSize);
    glReadPixels(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT,GL_RGBA, GL_UNSIGNED_BYTE, tmpPixelsBuffer);
    for (int y = WINDOW_HEIGHT-1; y >=0 ; y--)
    {
        for (int x = 0; x < WINDOW_WIDTH; x++)
        {
 
            //蓝色
            tmpBIT[0] = tmpPixelsBuffer[(y*WINDOW_WIDTH + x) * 4 + 2];
            //绿色
            tmpBIT[1] = tmpPixelsBuffer[(y*WINDOW_WIDTH + x) * 4 + 1];
            //红色
            tmpBIT[2] = tmpPixelsBuffer[(y*WINDOW_WIDTH + x) * 4 + 0];
            tmpBIT[3] = 100;//不起作用
            tmpBIT += 4;
        }
    }
 
    //从深度缓冲区读取数据
    int tmpPixelSize = WINDOW_WIDTH*WINDOW_HEIGHT;
    float* tmpPixelsBuffer = new float[ tmpPixelSize ];
    //GLfloat* tmpPixelsBuffer = new GLfloat[ tmpPixelSize ];
    //glReadBuffer(GL_NONE);
    //glReadBuffer(GL_BACK);
    glReadPixels(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT,GL_DEPTH_COMPONENT, GL_FLOAT, tmpPixelsBuffer);
 
    GLenum aaa = glGetError();
    for (int y = WINDOW_HEIGHT-1; y >=0 ; y--)
    {
        for (int x = 0; x < WINDOW_WIDTH; x++)
        {
            float iii=tmpPixelsBuffer[(y*WINDOW_WIDTH + x)  + 0];
            iii*=255;
            //蓝色
            tmpBIT[0] = iii;
            //绿色
            tmpBIT[1] = iii;
            //红色
            tmpBIT[2] = iii;
            tmpBIT[3] = 255;//不起作用
            tmpBIT += 4;
        }
    }
 
 
    img->save("a.jpg");

方法二、输出到纹理

1、配置FBO,使颜色、深度输出到纹理

    glGenFramebuffers(1, &m_fbo);
 
    // Создаем буфер глубины
    glGenTextures(1, &m_shadowMap);
    glBindTexture(GL_TEXTURE_2D, m_shadowMap);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
                           m_shadowMap, 0);
 
 
    //增加一个颜色缓冲区
    glGenTextures(1, &m_shadowColor);
    glBindTexture(GL_TEXTURE_2D, m_shadowColor);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, WindowWidth, WindowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                           m_shadowColor, 0);
    glDrawBuffer(GL_COLOR_ATTACHMENT0);
 
 
 
    // 不允许读写颜色缓冲区,也会禁用片元着色器
    //glDrawBuffer(GL_NONE);
    //glReadBuffer(GL_NONE);
 
    GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
 
    if (Status != GL_FRAMEBUFFER_COMPLETE){
        printf("FB error, status: 0x%x\n", Status);
        return false;
    }
    return true;

2、在默认帧缓冲区(或其他帧缓冲区)使用纹理

    glActiveTexture(TextureUnit);
    glBindTexture(GL_TEXTURE_2D, m_shadowMap);

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值