3

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:

  • My OpenGL application renders this image to a multisample framebuffer:

    enter image description here

  • I then blit that multisample framebuffer into a regular framebuffer (not a multisample one).

  • I then read the RGB data from that regular framebuffer into an array of unsigned bytes using glReadPixels.

  • Finally, I call stbi_write_png with the array of unsigned bytes. This is the result:

    enter image description here

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:

  • To create the multisample framebuffer:
   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);
  • To create the regular framebuffer:
   // 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);
  • Note that both framebuffers are reported as complete.

  • To blit the multisample framebuffer into the regular one, read from the regular one and write the PNG image:

   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);
  • Note that glGetError reports no errors.

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

user3266738
  • 477
  • 2
  • 12
  • Looks like there's padding at the end of each line. Since one line is 450*3 = 1250 bytes, there are 2 padding bytes that you don't account for when writing the PNG. Change the stride to `width * 3 + 2` (although that `2` should be calculated and not hard coded). – 1201ProgramAlarm May 25 '20 at 17:44
  • 1
    By default, rows are aligned on 4 bytes. Your `write_png` call expects one contiguous block. set GL_PACK_ALIGNMENT to 1 as per this question: [glPixelStorei(GL\_UNPACK\_ALIGNMENT, 1) Disadvantages?](https://stackoverflow.com/questions/11042027/glpixelstoreigl-unpack-alignment-1-disadvantages) – Botje May 25 '20 at 17:45
  • dump out a raw image at each stage and find out where the stride is going wrong – Alan Birtles May 25 '20 at 17:46
  • @1201ProgramAlarm Your solution also fixes my problem. Thank you! – user3266738 May 25 '20 at 17:58
  • 1
    @Rabbid76 Yes, that is what I said. The question I linked just happens to talk about UNPACK* :) – Botje May 25 '20 at 19:49

1 Answers1

5

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);
Rabbid76
  • 202,892
  • 27
  • 131
  • 174