Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/opengl/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在OpenGL中截图 如何在C++中截取OpenGL窗口的截图并将其保存到文件.< /P>_C++_Opengl_Save_Screenshot_Glreadpixels - Fatal编程技术网

如何在OpenGL中截图 如何在C++中截取OpenGL窗口的截图并将其保存到文件.< /P>

如何在OpenGL中截图 如何在C++中截取OpenGL窗口的截图并将其保存到文件.< /P>,c++,opengl,save,screenshot,glreadpixels,C++,Opengl,Save,Screenshot,Glreadpixels,我找到了函数, 但我不知道下一步该怎么办。例如,我可以在哪里设置文件的路径 如果不难,请编写代码。会将位复制到您提供的内存缓冲区中。您必须手动格式化数据(按照您选择的图像格式),并在返回后将其写入磁盘。将该数据保存到文件是您必须自己完成的事情,或者使用第三方库进行保存-OpenGL没有此类功能 如果你想自己动手的话,Windows.bmp可能是最简单的——维基百科有一个。否则,您可以使用图像保存/加载库:libpng、libjpeg等进行低级控制,或者(还有其他库,但这是我最喜欢的,它是一个非常

我找到了函数, 但我不知道下一步该怎么办。例如,我可以在哪里设置文件的路径


如果不难,请编写代码。

会将位复制到您提供的内存缓冲区中。您必须手动格式化数据(按照您选择的图像格式),并在返回后将其写入磁盘。

将该数据保存到文件是您必须自己完成的事情,或者使用第三方库进行保存-OpenGL没有此类功能


如果你想自己动手的话,Windows.bmp可能是最简单的——维基百科有一个。否则,您可以使用图像保存/加载库:libpng、libjpeg等进行低级控制,或者(还有其他库,但这是我最喜欢的,它是一个非常通用的库,与GL配合使用)进行高级“只需执行”图像i/o。

这段代码捕获OpenGL窗口并导出为BMP文件。您必须有库才能运行它

// Make the BYTE array, factor of 3 because it's RBG.
BYTE* pixels = new BYTE[3 * width * height];

glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);

// Convert to FreeImage format & save to file
FIBITMAP* image = FreeImage_ConvertFromRawBits(pixels, width, height, 3 * width, 24, 0x0000FF, 0xFF0000, 0x00FF00, false);
FreeImage_Save(FIF_BMP, image, "C:/test.bmp", 0);

// Free resources
FreeImage_Unload(image);
delete [] pixels;

通常,OpenGL不提供保存图像的功能。我认为最快和最简单的方法就是保存到。格式。然而,这种格式是未压缩的,这意味着它的文件大小将非常大。如今,它只能由相当多的项目来支持

我更喜欢将图像保存到.png文件中,该文件经过压缩,但也提供无损图像,并且受到许多浏览器的支持。要将OpenGL保存为.png格式,我首先推荐。它非常简单,易于使用。例如,要在位置(x,y)保存颜色为(R,G,B)的图像像素,您的代码将是(请参阅PNGwriter网站中的“快速启动”):

请注意,由于PNGwriter从图像的左上角开始保存每个像素,而数组get from glReadPixels()从窗口的左下角开始,因此保存整个图像的代码可能如下所示:

GLfloat* pixels = new GLfloat[nPixels];
glReadPixels(0.0, 0.0, width, height,GL_RGB, GL_FLOAT, pixels);
pngwriter PNG(width, height, 1.0, fileName);
size_t x = 1;   
size_t y = 1;
double R, G, B;
for(size_t i=0; i<npixels; i++) // "i" is the index for array "pixels"
{
      switch(i%3)
     {
           case 2:
                 B = static_cast<double>(pixels[i]); break;
           case 1:
                 G = static_cast<double>(pixels[i]); break;
           case 0:
                 R = static_cast<double>(pixels[i]);
                 PNG.plot(x, y, R, G, B);     // set pixel to position (x, y)
                 if( x == width )             // Move to the next row of image
                 {
                       x=1;
                       y++;
                  }
                  else                       // To the next pixel
                  { x++; }
                  break;
     }
}
PNG.close();
GLfloat*像素=新的GLfloat[nPixels];
glReadPixels(0.0、0.0、宽度、高度、GL_RGB、GL_浮点、像素);
pngwriter PNG(宽度、高度、1.0、文件名);
尺寸x=1;
尺寸y=1;
双R,G,B;

对于(大小i=0;i可运行示例

每次用鼠标单击窗口时,都会使用当前屏幕截图创建一个
tmpX.ppm
文件

例如,您可以在Linux上使用
eog
查看此文件,并使用文本编辑器检查它

要在不显示窗口的情况下进行渲染,请参见:

在Ubuntu 15.10、OpenGL 4.5.0和NVIDIA 352.63上测试

Vulkan

这个例子刚刚奏效:如何运行它:


一个简单快速的解决方案

  • 输出TARGA文件,但可以轻松转换为PNG(提供脚本)
  • 不需要额外的库
  • 将同时使用C和C++(有一些小的更改)。
  • 注意:输出文件应具有
    tga
    文件扩展名
代码如下:

void saveScreenshotToFile(std::string filename, int windowWidth, int windowHeight) {    
    const int numberOfPixels = windowWidth * windowHeight * 3;
    unsigned char pixels[numberOfPixels];

    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glReadBuffer(GL_FRONT);
    glReadPixels(0, 0, windowWidth, windowHeight, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);

    FILE *outputFile = fopen(filename.c_str(), "w");
    short header[] = {0, 2, 0, 0, 0, 0, (short) windowWidth, (short) windowHeight, 24};

    fwrite(&header, sizeof(header), 1, outputFile);
    fwrite(pixels, numberOfPixels, 1, outputFile);
    fclose(outputFile);

    printf("Finish writing to file.\n");
}
并调用该函数:

saveScreenshotToFile("test.tga", 1200, 900);
用于将TARGA文件转换为PNG的bash脚本:

for oldFileName in *.tga; do
    [ -f "$oldFileName" ] || break         # Break out if no .tga files found.
    newFileName=${oldFileName//.tga/.png}
    convert $oldFileName $newFileName
    rm $oldFileName
    echo "Converted $oldFileName to $newFileName"
done

您可以使用@Rafael的答案和OpenCV保存屏幕截图:

void Game::saveScreenshotToFile(std::string filename, int windowWidth, int windowHeight) {    

    cv::Mat img(windowHeight, windowWidth, CV_8UC3);
    glPixelStorei(GL_PACK_ALIGNMENT, (img.step & 3) ? 1 : 4);

    glPixelStorei(GL_PACK_ROW_LENGTH, img.step/img.elemSize());
    glReadPixels(0, 0, img.cols, img.rows, GL_BGR, GL_UNSIGNED_BYTE, img.data);
    cv::flip(img, img, 0);
    //cv::imshow("Image",img);
    //cv::waitKey(0);
    cv::imwrite(filename, img);
    }

感谢OpenCV:

使用什么函数可以将内存缓冲区写入磁盘?@EthanHunt:
fwrite
将数据写入文件*,
fopen
将为您提供一个与指定文件名对应的文件*。但不要忽略图像格式部分,这是一个棘手的部分。对于arr,使用
GL\u UNSIGNED\u BYTE
是明智的也可以,而不是windows特定的
BYTE
。对不起,我的意思是
GLubyte
@Mortennobel如果使用
GL|u PACK_ALIGNMENT
,会更好,因为
glReadPixels
是打包而不是解包:)另请参见:PPM | PNG | FFMPEG一个。
saveScreenshotToFile("test.tga", 1200, 900);
for oldFileName in *.tga; do
    [ -f "$oldFileName" ] || break         # Break out if no .tga files found.
    newFileName=${oldFileName//.tga/.png}
    convert $oldFileName $newFileName
    rm $oldFileName
    echo "Converted $oldFileName to $newFileName"
done
void Game::saveScreenshotToFile(std::string filename, int windowWidth, int windowHeight) {    

    cv::Mat img(windowHeight, windowWidth, CV_8UC3);
    glPixelStorei(GL_PACK_ALIGNMENT, (img.step & 3) ? 1 : 4);

    glPixelStorei(GL_PACK_ROW_LENGTH, img.step/img.elemSize());
    glReadPixels(0, 0, img.cols, img.rows, GL_BGR, GL_UNSIGNED_BYTE, img.data);
    cv::flip(img, img, 0);
    //cv::imshow("Image",img);
    //cv::waitKey(0);
    cv::imwrite(filename, img);
    }