Неправильные цвета отображаются при загрузке 32-битного png с использованием stb_image и использованием GL_UNSIGNED_INT_8_8_8_8 в качестве параметра типа glTexImage2D

Я только учусь, как текстурировать в OpenGL, и меня немного смущают некоторые результаты, которые я получаю.

Я использую stb_image, чтобы загрузить следующее изображение png шахматной доски:

Черно-белое изображение шахматной доски

Когда я сохранил изображение PNG, я явно решил сохранить его как 32-битный. Это привело бы меня к мысли, что каждый компонент (RGBA) будет храниться как 8 битов в общей сложности 32 бита - размер целого числа без знака. Однако, используя следующий код:

unsigned char * texture_data = 
  stbi_load("resources/graphics-scene/tut/textures/checker.png", &w, &h, nullptr, 4);

// ...

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0,
  GL_RGBA, GL_UNSIGNED_INT_8_8_8_8,  texture_data);

выходы:

Черно-белая шахматная доска в двигателе

Если я вместо этого использую GL_UNSIGNED_BYTE для параметра типа, я получаю правильные результаты.

Кроме того, если это помогает, я также попробовал следующее изображение:

Цветное изображение шахматной доски

который дает

Цветная шахматная доска в двигателе

GL_UNSIGNED_BYTE дает правильный результат и в этом случае.

Я не уверен, что это случай, когда я неправильно понял glTexImage2D или stb_image (конвертирует ли он загруженные данные в 8-битные? Это мне кажется маловероятным).

РЕДАКТИРОВАТЬ: я только что нашел соответствующий пост (уже искал некоторые, но не повезло). Однако ответ ( /questions/35483548/chto-oznachaet-glunsignedbyte-dlya-glteximage2d/35483556#35483556) меня смущает. Если это так - то, что параметр типа указывает, сколько байтов на компонент - тогда, что именно означают такие вещи, как GL_UNSIGNED_BYTE_3_3_2 и GL_UNSIGNED_INT_8_8_8_8???

1 ответ

Решение

Если это так - то, что параметр типа указывает, сколько байтов на компонент - тогда, что именно означают такие вещи, как GL_UNSIGNED_BYTE_3_3_2 и GL_UNSIGNED_INT_8_8_8_8???

Это делает оба, в зависимости от того, что является фактическим типом.

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

GL_UNSIGNED_INT_8_8_8_8 означает, что OpenGL будет интерпретировать каждый пиксель как целое число без знака. Первый компонент будет старшими 8 битами, следующий будет следующими 8 битами и так далее.

Однако, из-за чего ваша проблема заключается в том, что STB-изображение не работает с целыми числами без знака. Каждый пиксель записывается в виде 4 отдельных байтов в порядке RGBA. В основном это делает это:

GLubyte arr[4] = {red, green, blue, alpha};

Теперь, это может звучать как то же самое. Но это не так. Причина, почему это связано с порядком байтов.

Когда вы делаете это в C/C++:

GLuint foo = 0;
foo |= (red << 24);
foo |= (green << 16);
foo |= (blue << 8);
foo |= (alpha << 0);

Типы данных OpenGL требуют GLuint быть целым без знака целым размером 32 бита. И при условии, что red, green, blue, а также alpha являются все GLubyte s (8-битные целые числа без знака), C ​​/ C++ говорит, что это упакует red бит в старший 8-битный байт, green в следующий и так далее. Стандарты C и C++ требуют, чтобы это работало.

Однако стандарты C и C++ не требуют, чтобы это работало:

GLubyte *ptr = (GLubyte*)&foo;
ptr[0] == ((foo >> 24) & 0xFF);

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

В порядке байтов с прямым порядком байтов младший байт 32-разрядного целого числа сохраняется первым, а не последним.

Когда OpenGL видит GL_UNSIGNED_INT это означает, что эти четыре байта будут интерпретироваться точно так же, как ваш процессор. Так GL_UNSIGNED_INT_8_8_8_8 будет делать эквивалент foo выше. Первый байт памяти, который он видит, будет интерпретироваться на машине с прямым порядком байтов как младший, а не старший.

STB-изображение не выводится GL_UNSIGNED_INT_8_8_8_8, Каждый пиксель обрабатывается как массив из 4 байтов, например arr выше. Следовательно, вы должны сообщить OpenGL, что именно так хранятся ваши данные. Итак, вы говорите, что каждый компонент - один байт. Который является то, что GL_UNSIGNED_BYTE делает.

Другие вопросы по тегам