c++openglpngfbostb-image

PNG saved from OpenGL framebuffer using stbi_write_png is shifted to the right


I have been trying to solve this visual bug for a few days without any success, so I'm asking this question to see if somebody can help me understand what is happening.

First I will describe the problem without any code, and then I will present some code. Here is the situation:

To me it looks like the first line of bytes is shifted to the right, which causes all the other lines to be shifted, resulting in a diagonal shape.

Here is my code:

   int width        = 450;
   int height       = 450;
   int numOfSamples = 1;

   // Create the multisample framebuffer

   glGenFramebuffers(1, &mMultisampleFBO);

   glBindFramebuffer(GL_FRAMEBUFFER, mMultisampleFBO);

   // Create a multisample texture and use it as a color attachment
   glGenTextures(1, &mMultisampleTexture);

   glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mMultisampleTexture);
   glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, numOfSamples, GL_RGB, width, height, GL_TRUE);
   glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);

   glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, mMultisampleTexture, 0);

   // Create a multisample renderbuffer object and use it as a depth attachment
   glGenRenderbuffers(1, &mMultisampleRBO);

   glBindRenderbuffer(GL_RENDERBUFFER, mMultisampleRBO);
   glRenderbufferStorageMultisample(GL_RENDERBUFFER, numOfSamples, GL_DEPTH_COMPONENT, width, height);
   glBindRenderbuffer(GL_RENDERBUFFER, 0);

   glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mMultisampleRBO);
   // Create the regular framebuffer

   glGenFramebuffers(1, &mRegularFBO);

   glBindFramebuffer(GL_FRAMEBUFFER, mRegularFBO);

   // Create a texture and use it as a color attachment
   glGenTextures(1, &mRegularTexture);

   glBindTexture(GL_TEXTURE_2D, mRegularTexture);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
   glBindTexture(GL_TEXTURE_2D, 0);

   glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mRegularTexture, 0);
   int width  = 450;
   int height = 450;
   static GLubyte* data = new GLubyte[3 * 450 * 450];
   memset(data, 0, 3 * width * height);

   // Blit the multisample framebuffer into the regular framebuffer
   glBindFramebuffer(GL_READ_FRAMEBUFFER, mMultisampleFBO);
   glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mRegularFBO);
   glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);

   // Read from the regular framebuffer into the data array
   glBindFramebuffer(GL_FRAMEBUFFER, mRegularFBO);
   glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);

   // Write the PNG image
   int numOfComponents = 3; // RGB
   int strideInBytes   = width * 3;
   stbi_write_png(imgName.c_str(), width, height, 3, data, width * 3);

I haven't been able to figure out what is wrong. Thank you for any help!


Solution

  • The issue is cause be the alignment of a row, when the image is read by glReadPixels. By default the alignment of the start of each row of the image is assumed to be 4.
    Since the width of the image is 450, which is not divisible by 4 (450/4 = 112.5) and the format is RGB (3 bytes), the alignment has to be changed.

    Change the GL_PACK_ALIGNMENT (glPixelStore) before reading the image data:

    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);