1

I am trying to save OpenGL screen to file using glReadPixels. I found this code:

void saveScreenshotToFile(const char* filename, int WIDTH, int HEIGHT) {
    int* buffer = new int[WIDTH * HEIGHT * 3];
    glReadPixels(0, 0, WIDTH, HEIGHT, GL_BGR, GL_UNSIGNED_BYTE, buffer);
    FILE* out = fopen(filename, "w");
    short  TGAhead[] = { 0, 2, 0, 0, 0, 0, WIDTH, HEIGHT, 24 };
    fwrite(&TGAhead, sizeof(TGAhead), 1, out);
    fwrite(buffer, 3 * WIDTH * HEIGHT, 1, out);
    fclose(out);
}

after it I am just calling

saveScreenshotToFile("file.tga", 800, 600);

This is what I expect. But this is what I am getting. I am using 2 VAOs, one for objects and one for light.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174

1 Answers1

2

This is because the GL_PACK_ALIGNMENT parameter is 4 by default. Since the image has 3 color channels (GL_BGR) and is tightly packed, the size of one line of the image may not be aligned to 4 bytes.
If an RGB image with 3 color channels is read from the GPU and 3*width is not divisible by 4, GL_PACK_ALIGNMENT must be set to 1 before reading the image with glReadPixels:

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, WIDTH, HEIGHT, GL_BGR, GL_UNSIGNED_BYTE, buffer);

Another problem may be that your screen is DPI-scaled and the framebuffer size does not match the window size. Determine the framebuffer size before you take the screenshot, e.g. for GLFW:

int w, h;
glfwGetFramebufferSize(wnd, &w, &h);
saveScreenshotToFile("file.tga", w, h);

More C++-like function:

void saveScreenshotToFile(const char* filename, int width, int height) {
    short tgaHead[] = { 0, 2, 0, 0, 0, 0, (short)width, (short)height, 24 };
    std::vector<char> buffer(width*height*3);
    glReadPixels(0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, buffer.data());
    
    std::fstream outFile;
    outFile.open(filename, std::ios::app | std::ios::binary);
    outFile.write(reinterpret_cast<char*>(&tgaHead), sizeof(tgaHead));
    outFile.write(buffer.data(), buffer.size());
    outFile.close();
}
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • 1
    Surely one of the most short-sighted and troublesome design decisions in an otherwise incredibly successful API. Even back in the days of SGI 'big iron', I never understood why the GL couldn't handle this internally, or use the default: `(1)` – Brett Hale Aug 26 '22 at 09:25