19

C++ で OpenGL ウィンドウのスクリーンショットを撮り、ファイルに保存する方法。

関数は見つかったが、glReadPixels()次に何をすればよいか分からない。たとえば、ファイルへのパスをどこに設定できますか?

難しくなければ、コードを書いてください。

4

7 に答える 7

29

このコードは、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;
于 2011-08-04T08:56:55.260 に答える
13

glReadPixelsは、指定したメモリ バッファにビットをコピーします。データを (選択した画像形式に) 手動でフォーマットし、 glReadPixelsが返された後にディスクに書き込む必要があります。

于 2011-04-30T22:01:43.820 に答える
8

実行可能な例

ウィンドウをマウスでクリックするたびにtmpX.ppm、現在のスクリーンショットを含むファイルが作成されます。

このファイルは、たとえばeogLinux で表示し、テキスト エディターで調べることができます。

ウィンドウを表示せずにレンダリングするには、GLUT/OpenGL を使用してファイルにレンダリングする方法を参照してください。

#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 なしでオフスクリーン レンダリングを行うことは可能ですか?

ここに画像の説明を入力

于 2016-03-26T15:03:48.757 に答える
4

そのデータをファイルに保存するには、自分で行うか、サードパーティのライブラリを使用する必要があります.OpenGLにはそのような機能はありません.

Windows の .bmp は、自分で行う場合、おそらく最も簡単です。ウィキペディアには、ファイル形式に関するかなり適切な説明があります。それ以外の場合は、画像の保存/読み込みライブラリを使用できます。低レベルの制御には libpng、libjpeg など、高レベルの制御にはdevIL (他にもありますが、これは私のお気に入りであり、GL と相性の良い非常に用途の広いライブラリです)。レベル「やればいい」画像I/O。

于 2011-04-30T22:02:01.167 に答える
1

@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

于 2020-09-15T15:36:41.670 に答える
0

通常、OpenGL には画像を保存する機能はありません。これを行う最も速くて簡単な方法は、に保存することだと思います。PPM形式。ただし、この種の形式は圧縮されていないため、ファイル サイズが非常に大きくなります。そして、今日ではかなりの数のプログラムでしかサポートできません。

私は画像を圧縮された .png ファイルに保存することを好みますが、ロスレス画像を提供し、多くのブラウザーでサポートされています。OpenGL を .png 形式で保存するには、まずPNGwriterをお勧めします。とてもシンプルで使いやすいです。たとえば、画像のピクセルを色 (R、G、B) の位置 (x、y) に保存するには、コードは次のようになります (PNGwriter Web サイトの「クイックスタート」を参照)。

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

PNGwriter は画像の左上隅から始まる各ピクセルを保存するため、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();
于 2014-07-23T14:37:29.620 に答える