PNG, сохраненный из фреймбуфера OpenGL с использованием stbi_write_png, смещен вправо - PullRequest
2 голосов
/ 25 мая 2020

Я несколько дней безуспешно пытался решить эту визуальную ошибку, поэтому я задаю этот вопрос, чтобы узнать, может ли кто-нибудь помочь мне понять, что происходит.

Сначала я опишу проблема без кода, а затем я представлю код. Вот ситуация:

  • Мое приложение OpenGL отображает это изображение во фреймбуфере с несколькими выборками:

    enter image description here

  • Затем я преобразовываю этот буфер кадра с несколькими выборками в обычный буфер кадра (не в буфер с несколькими выборками).

  • Затем я считываю данные RGB из этого обычного буфера кадра в массив байтов без знака используя glReadPixels.

  • Наконец, я вызываю stbi_write_png с массивом байтов без знака. Это результат:

    enter image description here

Мне кажется, что первая строка байтов сдвинута вправо, что вызывает все остальные строки должны быть сдвинуты, в результате чего получится диагональная форма.

Вот мой код:

  • Для создания мультисэмплового буфера кадра:
   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);
  • Обратите внимание, что оба фреймбуфера сообщаются как завершенные.

  • Чтобы преобразовать мультисэмпловый буфер кадра в обычный, прочтите из обычного буфера и запишите изображение 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 не сообщает об ошибках.

Я не смог понять, что не так. Спасибо за любую помощь!

1 Ответ

4 голосов
/ 25 мая 2020

Проблема связана с выравниванием строки, когда изображение считывается glReadPixels. По умолчанию предполагается, что начало каждой строки изображения выравнивается равным 4.
Поскольку ширина изображения равна 450, что не делится на 4 (450/4 = 112,5), а формат - RGB ( 3 байта) необходимо изменить выравнивание.

Измените GL_PACK_ALIGNMENT (glPixelStore) перед чтением данных изображения:

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