如何在OpenGL中截图

ubby3x7f  于 11个月前  发布在  其他
关注(0)|答案(8)|浏览(133)

如何在C++中对OpenGL窗口进行截图并将其保存到文件中。
我找到了glReadPixels()函数,但我不知道下一步该怎么做。例如,我可以在哪里设置文件的路径?
如果不困难,请写代码。

eh57zj3b

eh57zj3b1#

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

// 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;

字符串

mu0hgdu0

mu0hgdu02#

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

inb24sb23#

可运行示例


的数据
每次您在窗口上单击鼠标时,将使用当前屏幕截图创建一个tmpX.ppm文件。
例如,您可以在Linux上使用eog查看此文件,并使用文本编辑器检查它。
要在不显示窗口的情况下进行渲染,请参见:How to use GLUT/OpenGL to render to a file?

#include <math.h>
#include <stdlib.h>
#include <stdio.h>

#define GL_GLEXT_PROTOTYPES 1
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <GL/glext.h>

static GLubyte *pixels = NULL;
static const GLenum FORMAT = GL_RGBA;
static const GLuint FORMAT_NBYTES = 4;
static const unsigned int HEIGHT = 500;
static const unsigned int WIDTH = 500;
static unsigned int nscreenshots = 0;
static unsigned int time;

/* Model. */
static double angle = 0;
static double angle_speed = 45;

static void init(void)  {
    glReadBuffer(GL_BACK);
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glViewport(0, 0, WIDTH, HEIGHT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);

    pixels = malloc(FORMAT_NBYTES * WIDTH * HEIGHT);
    time = glutGet(GLUT_ELAPSED_TIME);
}

static void deinit(void)  {
    free(pixels);
}

static void create_ppm(char *prefix, int frame_id, unsigned int width, unsigned int height,
        unsigned int color_max, unsigned int pixel_nbytes, GLubyte *pixels) {
    size_t i, j, k, cur;
    enum Constants { max_filename = 256 };
    char filename[max_filename];
    snprintf(filename, max_filename, "%s%d.ppm", prefix, frame_id);
    FILE *f = fopen(filename, "w");
    fprintf(f, "P3\n%d %d\n%d\n", width, HEIGHT, 255);
    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j++) {
            cur = pixel_nbytes * ((height - i - 1) * width + j);
            fprintf(f, "%3d %3d %3d ", pixels[cur], pixels[cur + 1], pixels[cur + 2]);
        }
        fprintf(f, "\n");
    }
    fclose(f);
}

static void draw_scene() {
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    glRotatef(angle, 0.0f, 0.0f, -1.0f);
    glBegin(GL_TRIANGLES);
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3f( 0.0f,  0.5f, 0.0f);
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(-0.5f, -0.5f, 0.0f);
    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3f( 0.5f, -0.5f, 0.0f);
    glEnd();
}

static void display(void) {
    draw_scene();
    glutSwapBuffers();
    glReadPixels(0, 0, WIDTH, HEIGHT, FORMAT, GL_UNSIGNED_BYTE, pixels);
}

static void idle(void) {
    int new_time = glutGet(GLUT_ELAPSED_TIME);
    angle += angle_speed * (new_time - time) / 1000.0;
    angle = fmod(angle, 360.0);
    time = new_time;
    glutPostRedisplay();
}

void mouse(int button, int state, int x, int y) {
    if (state == GLUT_DOWN) {
        puts("screenshot");
        create_ppm("tmp", nscreenshots, WIDTH, HEIGHT, 255, FORMAT_NBYTES, pixels);
        nscreenshots++;
    }
}

int main(int argc, char **argv) {
    GLint glut_display;
    glutInit(&argc, argv);
    glutInitWindowSize(WIDTH, HEIGHT);
    glutInitWindowPosition(100, 100);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutCreateWindow(argv[0]);
    init();
    glutDisplayFunc(display);
    glutIdleFunc(idle);
    glutMouseFunc(mouse);
    atexit(deinit);
    glutMainLoop();
    return EXIT_SUCCESS;
}

字符串
编译工具:

gcc main.c -lm -lGL -lGLU -lglut


在Ubuntu 15.10,OpenGL 4.5.0 NVIDIA 352.63上测试。

瓦肯

这个例子刚刚工作:https://github.com/SaschaWillems/Vulkan/blob/b9f0ac91d2adccc3055a904d3a8f6553b10ff6cd/examples/screenshot/screenshot.cpp如何运行它:在Vulkan中没有Surface可以进行屏幕外渲染吗?


sy5wg1nm

sy5wg1nm4#

简单而快速的解决方案。

  • 输出一个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

h5qlskok

h5qlskok5#

将这些数据保存到文件中是你必须自己做的事情,或者使用第三方库- OpenGL没有这样的功能。
如果你想自己做的话,Windows.bmp可能是最简单的-维基百科有一个pretty good explanation of the file format。否则你可以使用图像保存/加载库:libpng,libjpeg等用于低级控制,或者devIL(还有其他库,但这是我最喜欢的,它是一个非常通用的库,与GL很好地配合)用于高级“Just do it”图像i/o。

gywdnpxw

gywdnpxw6#

你可以用@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:https://stackoverflow.com/a/9098883/10152334

whlutmcx

whlutmcx7#

一般来说,OpenGL不提供保存图像的功能。我认为最快和最简单的方法是保存到. PPM。然而,这种格式是未压缩的,这意味着它的文件大小会非常大。现在只有相当多的程序才能支持它。
我更喜欢将图像保存为.png文件,它是压缩的,但也提供无损图像,并受到许多浏览器的支持。要将OpenGL保存为.png格式,我首先推荐PNGwriter。它非常简单易用。例如,要保存图像的像素,颜色为(R,G,B),位置为(x,y),您的代码将是(请参阅PNGwriter网站中的“快速入门”):

pngwriter PNG(width, height, 1.0, fileName); // "1.0" stand for the white background
PNG.plot(x, y, R, G, B);
PNG.close();

字符串
请注意,由于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();

gijlo24d

gijlo24d8#

我已经修改了Ciro Bullli OurBigBook.com的答案,以包括线程和更好的性能。

#include <thread>
#include <Windows.h>
#define GL_GLEXT_PROTOTYPES 1
#include <GL\GL.h>
#include <GL\GLU.h>
#include <glut.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>

static GLubyte *pixels = NULL;
static const GLenum FORMAT = GL_RGBA;
static const GLuint FORMAT_NBYTES = 4;
static const unsigned int HEIGHT = 500;
static const unsigned int WIDTH = 500;
static unsigned int nscreenshots = 0;
static unsigned int gameTime;
static bool grabScreen = false;
static bool threadRunning = false;

/* Model. */
static double angle = 0;
static double angle_speed = 45;

static void init(void)  {
    glReadBuffer(GL_BACK);
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glViewport(0, 0, WIDTH, HEIGHT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);

    pixels = (GLubyte*)malloc(FORMAT_NBYTES * WIDTH * HEIGHT);
    gameTime = glutGet(GLUT_ELAPSED_TIME);
}

static void deinit(void)  {
    free(pixels);
}

static void create_ppm(char *prefix, int frame_id, unsigned int width, unsigned int height,
    unsigned int color_max, unsigned int pixel_nbytes, GLubyte *pixels) {
    size_t i, j, k, cur;
    enum Constants { max_filename = 256 };
    char filename[max_filename];
    sprintf(filename, "%s%d.ppm", prefix, frame_id);
    FILE *f = fopen(filename, "w");
    fprintf(f, "P3\n%d %d\n%d\n", width, HEIGHT, 255);
    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j++) {
            cur = pixel_nbytes * ((height - i - 1) * width + j);
            fprintf(f, "%3d %3d %3d ", pixels[cur], pixels[cur + 1], pixels[cur + 2]);
        }
        fprintf(f, "\n");
    }
    fclose(f);
}

static void draw_scene() {
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    glRotatef(angle, 0.0f, 0.0f, -1.0f);
    glBegin(GL_TRIANGLES);
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3f(0.0f, 0.5f, 0.0f);
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(-0.5f, -0.5f, 0.0f);
    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3f(0.5f, -0.5f, 0.0f);
    glEnd();
}

static void savepixels()
{
    threadRunning = true;
    create_ppm("tmp", nscreenshots, WIDTH, HEIGHT, 255, FORMAT_NBYTES, pixels);
    nscreenshots++;
    threadRunning = false;
}

static void display(void) {
    draw_scene();
    glutSwapBuffers();
    if (grabScreen){
        grabScreen = false;
        if (threadRunning){
            printf("Thread running\n");
        }
        else{
            glReadPixels(0, 0, WIDTH, HEIGHT, FORMAT, GL_UNSIGNED_BYTE, pixels);
            puts("screenshot");
            std::thread t{ savepixels };
            t.detach();
        }
    }
}

static void idle(void) {
    int new_gameTime = glutGet(GLUT_ELAPSED_TIME);
    angle += angle_speed * (new_gameTime - gameTime) / 1000.0;
    angle = fmod(angle, 360.0);
    gameTime = new_gameTime;
    glutPostRedisplay();
}

void mouse(int button, int state, int x, int y) {
    if (state == GLUT_DOWN && grabScreen == false) {
        grabScreen = true;
    }
}

int main(int argc, char **argv) {
    GLint glut_display;
    glutInit(&argc, argv);
    glutInitWindowSize(WIDTH, HEIGHT);
    glutInitWindowPosition(100, 100);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutCreateWindow(argv[0]);
    init();
    glutDisplayFunc(display);
    glutIdleFunc(idle);
    glutMouseFunc(mouse);
    atexit(deinit);
    glutMainLoop();
    return EXIT_SUCCESS;
}

字符串

相关问题