使用 stbi_write_png 从 OpenGL 帧缓冲区保存的 PNG 向右移动

Posted

技术标签:

【中文标题】使用 stbi_write_png 从 OpenGL 帧缓冲区保存的 PNG 向右移动【英文标题】:PNG saved from OpenGL framebuffer using stbi_write_png is shifted to the right 【发布时间】:2020-09-12 09:38:55 【问题描述】:

我这几天一直在尝试解决这个视觉错误,但没有任何成功,所以我问这个问题,看看是否有人可以帮助我了解正在发生的事情。

首先我将在没有任何代码的情况下描述问题,然后我将展示一些代码。情况如下:

我的 OpenGL 应用程序将此图像渲染到多重采样帧缓冲区:

然后我将该多样本帧缓冲区 blit 到常规帧缓冲区(不是多样本帧缓冲区)。

然后,我使用 glReadPixels 将 RGB 数据从该常规帧缓冲区读取到无符号字节数组中。

最后,我用无符号字节数组调用stbi_write_png。结果如下:

在我看来,第一行字节似乎向右移动,这导致所有其他行都被移动,从而形成对角线形状。

这是我的代码:

创建多重采样帧缓冲区:
   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);

请注意,两个帧缓冲区都报告为完整。

要将多样本帧缓冲区 blit 到常规帧缓冲区,请从常规帧缓冲区读取并写入 PNG 图像:

   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);
请注意,glGetError 不会报告任何错误。

我一直无法弄清楚出了什么问题。感谢您的帮助!

【问题讨论】:

看起来每行末尾都有填充。由于一行是 450*3 = 1250 字节,因此在编写 PNG 时,您没有考虑 2 个填充字节。将步幅更改为width * 3 + 2(尽管2 应该是计算出来的,而不是硬编码的)。 默认情况下,行在 4 个字节上对齐。您的write_png 调用需要一个连续的块。根据这个问题将 GL_PACK_ALIGNMENT 设置为 1:glPixelStorei(GL_UNPACK_ALIGNMENT, 1) Disadvantages? 在每个阶段转储原始图像并找出步幅出错的地方 @1201ProgramAlarm 您的解决方案也解决了我的问题。谢谢! @Rabbid76 是的,我就是这么说的。我链接的问题恰好是关于 UNPACK* :) 【参考方案1】:

glReadPixels 读取图像时,问题是行对齐。默认情况下,图像每行开始的对齐方式假定为 4。 由于图片的宽度是450,不能被4整除(450/4 = 112.5),而且格式是RGB(3字节),所以不得不改变对齐方式。

在读取图像数据之前更改GL_PACK_ALIGNMENTglPixelStore):

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

【讨论】:

我的应用程序现在运行良好,谢谢! :-)

以上是关于使用 stbi_write_png 从 OpenGL 帧缓冲区保存的 PNG 向右移动的主要内容,如果未能解决你的问题,请参考以下文章

高级openg 混合,一个完整程序

如何从 Skia 路径几何中获取网格?

VIPM 2020 For Windows

将渲染设备从 Sandy Bridge GPU 更改为主 GPU

Qt 5.5 和 OpenGL:检索设备信息

COCOS学习笔记--TexturePacker使用详解