glReadPixels 为空时如何生成屏幕截图?

Posted

技术标签:

【中文标题】glReadPixels 为空时如何生成屏幕截图?【英文标题】:How can I generate a screenshot when glReadPixels is empty? 【发布时间】:2013-10-04 00:15:54 【问题描述】:

我有一个使用 OpenGL(带有 freeglut 2.8.1 的 VS2012)在窗口中运行的程序。基本上在每个时间步(通过从我的glutIdleFunc 钩子调用glutPostRedisplay 运行)我调用我自己的draw 函数,然后调用glFlush 来显示结果。然后我调用我自己的 screenShot 函数,它使用 glReadPixels 函数将像素转储到 tga 文件中。

这个设置的问题是当窗口最小化时文件是空的。也就是说glReadPixels的输出为空;我怎样才能避免这种情况?

这是我正在使用的screenShot 函数的副本(我不是版权所有者):

//////////////////////////////////////////////////
// Grab the OpenGL screen and save it as a .tga //
// Copyright (C) Marius Andra 2001              //
// http://cone3d.gz.ee  EMAIL: cone3d@hot.ee    //
//////////////////////////////////////////////////
// (modified by me a little)
int screenShot(int const num)

    typedef unsigned char uchar;
    // we will store the image data here
    uchar *pixels;
    // the thingy we use to write files
    FILE * shot;
    // we get the width/height of the screen into this array
    int screenStats[4];

    // get the width/height of the window
    glGetIntegerv(GL_VIEWPORT, screenStats);

    // generate an array large enough to hold the pixel data 
    // (width*height*bytesPerPixel)
    pixels = new unsigned char[screenStats[2]*screenStats[3]*3];
    // read in the pixel data, TGA's pixels are BGR aligned
    glReadPixels(0, 0, screenStats[2], screenStats[3], 0x80E0, 
    GL_UNSIGNED_BYTE, pixels);

    // open the file for writing. If unsucessful, return 1
    std::string filename = kScreenShotFileNamePrefix + Function::Num2Str(num) + ".tga";

    shot=fopen(filename.c_str(), "wb");

    if (shot == NULL)
        return 1;

    // this is the tga header it must be in the beginning of 
    // every (uncompressed) .tga
    uchar TGAheader[12]=0,0,2,0,0,0,0,0,0,0,0,0;
    // the header that is used to get the dimensions of the .tga
    // header[1]*256+header[0] - width
    // header[3]*256+header[2] - height
    // header[4] - bits per pixel
    // header[5] - ?
    uchar header[6]=((int)(screenStats[2]%256)),
    ((int)(screenStats[2]/256)),
    ((int)(screenStats[3]%256)),
    ((int)(screenStats[3]/256)),24,0;

    // write out the TGA header
    fwrite(TGAheader, sizeof(uchar), 12, shot);
    // write out the header
    fwrite(header, sizeof(uchar), 6, shot);
    // write the pixels
    fwrite(pixels, sizeof(uchar), 
    screenStats[2]*screenStats[3]*3, shot);

    // close the file
    fclose(shot);
    // free the memory
    delete [] pixels;

    // return success
    return 0;

那么,无论 Windows 是否决定在显示器上实际显示内容,我如何才能将屏幕截图打印到 TGA 文件?

注意:因为我试图保持模拟进度的可视化记录,所以我需要打印每一帧,无论是否正在渲染。我意识到最后一条语句有点矛盾,因为我需要渲染帧才能生成屏幕截图。改写; 我需要glReadPixels(或一些替代函数)在每一步生成程序的更新状态,以便我可以将其打印到文件中,而不管窗口是否会选择显示它。

【问题讨论】:

【参考方案1】:

听起来你正在与pixel ownership problem 发生冲突。

渲染到FBO 并使用glReadPixels() 来代替前端缓冲区来抓取图像。

【讨论】:

这是答案+1【参考方案2】:

我建议将最后渲染的帧存储在内存中,并在调用更新并且新渲染中存在实际像素数据时更新此内存的内容。或者你可以使用累加器,虽然我不记得它是如何存储旧帧的(它可能最终更新得太快以至于它也没有存储渲染数据。

另一种解决方案可能是使用着色器手动渲染每一帧并将结果写入文件

【讨论】:

据我了解您的解决方案,它会打印相同的屏幕截图,直到显示更新,对吗?对于我的特殊情况,我需要每次渲染屏幕截图,无论窗口是否会显示其内容(因为我需要所有屏幕截图)。我已经更新了我的问题以反映这一澄清.. 从技术上讲,我的方式仍然会产生正确的结果,因为如果没有发生更新,渲染将保持不变,除非你有像物体随着时间旋转的东西,你最小化,然后当你重新打开它时在最小化期间​​仍然移动。如果是这样的话,那我就不知所措了。我建议尝试我的解决方案,看看会发生什么 @ausairman 刚刚更新了我的答案,请尝试使用累积缓冲区 累积缓冲区不会更新,因为像素会在早期测试中被丢弃,它的用途完全不同

以上是关于glReadPixels 为空时如何生成屏幕截图?的主要内容,如果未能解决你的问题,请参考以下文章

当数组为空时扑出显示空间[]

文本字段为空时隐藏自动完成表格视图

在 OpenGL 中创建屏幕截图不起作用

screenshot() 给出:ImportError: cannot import name glReadPixels error in kivy

osg::readPixels,glreadPixels截图,保存图片的alpha不对,总是255

当我的搜索字段为空时,如何在不搜索结果的情况下呈现页面,但在搜索时仍然异步呈现?