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:
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:
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);
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);
I haven't been able to figure out what is wrong. Thank you for any help!
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);