2011-04-30 61 views
10

如何截取C++中的OpenGL窗口並將其保存到文件中。如何在OpenGL中截屏

我發現glReadPixels()函數, 但我不知道接下來該做什麼。例如,我可以在哪裏設置文件路徑?

如果不難,請寫下代碼。

回答

6

glReadPixels會將這些位複製到您提供的內存緩衝區中。您必須手動格式化數據(以您選擇的圖像格式),並在glReadPixels返回後將其寫入磁盤。

+0

用什麼函數可以將內存緩衝區寫入磁盤? – EthanHunt 2011-04-30 22:21:40

+2

@EthanHunt:'fwrite'將數據寫入FILE *,'fopen'爲您提供一個與您指定的文件名相對應的FILE *。但不要忽略關於圖像格式的部分,這是棘手的問題。 – 2011-05-01 00:00:32

+0

你可以給我示例代碼嗎? – EthanHunt 2011-05-02 18:47:15

2

將數據保存到文件是您必須自己做的或者使用第三方庫的 - OpenGL沒有這樣的功能。

Windows .bmp可能是最簡單的,如果你想自己做 - 維基百科有pretty good explanation of the file format。否則,您可以使用圖像保存/加載庫:libpng,libjpeg等進行低級別控制,或者使用devIL(還有其他的,但這是我最喜歡的,它是一個非常靈活的庫,可與GL良好配合)級別「只是做」圖像I/O。

18

這段代碼捕獲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; 
+0

'glPixelStorei(GL_UNPACK_ALIGNMENT,1);''是'glReadPixels'之前的一個好主意(更多關於[OpenGL Wiki](http://www.opengl.org/wiki/GLAPI/glPixelStore)) – Mortennobel 2013-06-19 13:50:29

+0

合理使用GL_UNSIGNED_BYTE'''也是數組,而不是特定於窗口的'''BYTE'''。 – Riot 2013-07-08 08:32:50

+1

對不起,我的意思是'''GLubyte'''。 – Riot 2013-07-08 08:48:58

0

通常,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會從圖像的左上角開始保存每個像素,而從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(); 
3

可運行例如

每次用鼠標單擊該窗口上,一個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_UNPACK_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。