2013-08-05 4 views
0

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

Я использую stb_image загрузить следующую Checkerboard PNG изображение:

Black and white checkerboard image

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

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); 

выходы:

Black and white checkerboard in engine

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

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

Colored checkerboard image

, который дает

Colored checkerboard in engine

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

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

РЕДАКТИРОВАТЬ: Я, наконец, нашел связанное сообщение (уже искал некоторых, но не повезло). Однако ответ (https://stackoverflow.com/a/4191875/2507444) меня смущает. Если это так, то параметр типа указывает, сколько байтов на компонент - то что именно делают такие вещи, как GL_UNSIGNED_BYTE_3_3_2 и GL_UNSIGNED_INT_8_8_8_8?

ответ

3

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

Он делает то же самое, в зависимости от того, какой тип.

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

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

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

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

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

Когда вы делаете это в 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++ требуют, чтобы это работало.

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

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.