1

我希望捕捉我在 openGL 中渲染的图像。我使用 glReadPixels 然后用 CImg 保存图像。不幸的是,结果是错误的。见下文。左边的图像是正确的。我用 GadWin PrintScreen 捕捉到了它。右图不正确。我用 glReadPixels 和 CImg 创建了它:

在此处输入图像描述 在此处输入图像描述

我已经对可能出现的问题进行了很多网络研究,但我没有追寻的途径。这里的代码捕获图像:

void snapshot() {
    int width = glutGet(GLUT_WINDOW_WIDTH);
    int height = glutGet(GLUT_WINDOW_HEIGHT);
    glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    int bytes = width*height*3; //Color space is RGB
    GLubyte *buffer = (GLubyte *)malloc(bytes);

    glFinish();
    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer);
    glFinish();

    CImg<GLubyte> img(buffer,width,height,1,3,false);
    img.save("ScreenShot.ppm");
    exit(0);
}

这里是我调用快照方法的地方:

void display(void) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        drawIndividual(0);
        snapshot();
        glutSwapBuffers();
    }

跟进评论,我抓住了位深度并打印到控制台。结果如下:

redbits=8
greenbits=8
bluebits=8
depthbits=24
4

2 回答 2

5

您显然是从帧缓冲区中获取图像,但行长似乎不对。您设置了包装对齐方式(好),但还有更多可设置的参数:

  • GL_PACK_ROW_LENGTH
  • GL_PACK_SKIP_PIXELS
  • GL_PACK_SKIP_ROWS

将它们设置为 0 以进行紧密包装。glReadPixels() 之后的 glFinish() 也是多余的。glReadPixels 不能异步工作——它仅在读取的数据被复制到目标缓冲区后返回(或者在写入 PBO 的情况下,从 PBO 获取数据将等待操作完成)。

于 2011-07-27T15:29:11.583 回答
2

CImg does not interleave its colors. That is, 3 red, green, blue pixels would be stored linearly as:

R1, R2, R3, ..., G1, G2, G3, ..., B1, B2, B3, ...

However, OpenGL's glReadPixel and glDrawPixel expect interleaved color components like this:

R1, G1, B1, R2, G2, B2, ...

Additionally, OpenGL puts the origin (0,0) at the lower left corner of an image. CImg uses the more approach where the origin is at the top left of the image.

Here is a routine I wrote convert the interleaved colors to CImg's channel-oriented format.

void convertToNonInterleaved(int w, int h, unsigned char* tangled, unsigned char* untangled) {
    //Take string in format R1 G1 B1 R2 G2 B2... and re-write it 
    //in the format format R1 R2 G1 G2 B1 B2... 
    //Assume 8 bit values for red, green and blue color channels.
    //Assume there are no other channels
    //tangled is a pointer to the input string and untangled 
    //is a pointer to the output string. This method assumes that 
    //memory has already been allocated for the output string.

    int numPixels = w*h;
    int numColors = 3;
    for(int i=0; i<numPixels; ++i) {
        int indexIntoInterleavedTuple = numColors*i;
        //Red
        untangled[i] = tangled[indexIntoInterleavedTuple];
        //Green
        untangled[numPixels+i] = tangled[indexIntoInterleavedTuple+1];
        //Blue
        untangled[2*numPixels+i] = tangled[indexIntoInterleavedTuple+2];
    }
}

I should have probably added the code to account for the change in origin, but I felt lazy and decided to use CImg to do that. Once this routine is run, this code creates the image object:

unsigned char* p = (unsigned char *)malloc(bytes);
convertToNonInterleaved(width, height, buffer, p);
CImg<unsigned char> img(p, width, height,1,3);
free(p);
img.mirror('y');
CImgDisplay main_disp(img,"Snapshot");
while (!main_disp.is_closed() ) {
    main_disp.wait();
}

And as far as I can tell, I do not have to set any packing parameters. Here is the code I use to grab the pixels from the OpenGL render target:

bytes = width*height*3; //Color space is RGB
if(buffer) 
    free(buffer);
buffer = (GLubyte *)malloc(bytes);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer);
于 2011-08-01T05:33:08.043 回答