Как использовать изображения NPOT в качестве текстур в libpng и OpenGL ES 1.1?
Я пытаюсь использовать PNG-изображения размером NPOT в качестве текстур в OpenGL ES 1.1 (поэтому нет GL_arb_texture_rectangle
) используя libpng 1.5. С SDL я мог бы просто скопировать изображение NPOT на текстуру NPOT, но я не могу понять, как сделать что-то подобное с libpng.
Когда я использую оригинальную текстуру (1280x720), я получаю только белую поверхность. Когда я изменяю размер файла до 1024x512 в файловой системе, он отображается нормально.
По какой-то причине текстура NPOT работает на 4.2 AVD с -gpu on
,
Вот код Это в значительной степени так, с некоторыми изменениями для чтения из APK с использованием libzip вместо fopen
Инг напрямую.
void png_zip_read(png_structp png, png_bytep data, png_size_t size)
{
zip_file* file = static_cast<zip_file*>(png_get_io_ptr(png));
zip_fread(file, data, size);
}
GLuint load_png_texture(const std::string& path, unsigned int& width,
unsigned int& height)
{
if (!apk_path.length())
throw "APK Path not set";
zip* apk = zip_open(apk_path.c_str(), 0, NULL);
if (!apk)
throw "Error loading APK";
zip_file* file = zip_fopen(apk, path.c_str(), 0);
if (file == 0)
throw path + ": " + strerror(errno);
png_byte header[8];
zip_fread(file, header, 8);
if (png_sig_cmp(header, 0, 8)) {
zip_fclose(file);
zip_close(apk);
throw path + " is not a PNG";
}
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
if (!png) {
zip_fclose(file);
zip_close(apk);
throw "Failed to create png struct";
}
png_infop info = png_create_info_struct(png);
if (!info) {
png_destroy_read_struct(&png, (png_infopp) NULL, (png_infopp) NULL);
zip_fclose(file);
zip_close(apk);
throw "Failed to create png info struct";
}
png_infop info_end = png_create_info_struct(png);
if (!info_end) {
png_destroy_read_struct(&png, &info, (png_infopp) NULL);
zip_fclose(file);
zip_close(apk);
throw "Failed to create png info struct";
}
if (setjmp(png_jmpbuf(png))) {
png_destroy_read_struct(&png, &info, &info_end);
zip_fclose(file);
zip_close(apk);
throw "Error from libpng";
}
png_set_read_fn(png, file, png_zip_read);
png_set_sig_bytes(png, 8);
png_read_info(png, info);
int bit_depth, color_type;
unsigned int temp_width, temp_height;
png_get_IHDR(png, info, &temp_width, &temp_height, &bit_depth,
&color_type, NULL, NULL, NULL);
width = temp_width;
height = temp_height;
png_read_update_info(png, info);
int row_bytes = png_get_rowbytes(png, info);
png_byte* image_data = new png_byte[row_bytes * height];
if (!image_data) {
png_destroy_read_struct(&png, &info, &info_end);
zip_fclose(file);
zip_close(apk);
throw "Could not allocate memory for PNG image data";
}
png_bytep* row_pointers = new png_bytep[height];
if (!row_pointers) {
png_destroy_read_struct(&png, &info, &info_end);
delete[] image_data;
zip_fclose(file);
zip_close(apk);
throw "Could not allocate memory for PNG row pointers";
}
for (int i = 0; i < height; i++)
row_pointers[height - 1 - i] = image_data + i * row_bytes;
png_read_image(png, row_pointers);
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
GLenum format = GL_RGBA;
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0,
format, GL_UNSIGNED_BYTE, image_data);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
png_destroy_read_struct(&png, &info, &info_end);
delete[] image_data;
delete[] row_pointers;
zip_fclose(file);
zip_close(apk);
return texture;
}
2 ответа
После еще нескольких проб и ошибок я вернулся к безумному поиску в Google и, наконец, нашел пример кода, который делает именно то, что я ищу.
Секрет в том, чтобы создать текстуру OpenGL следующим образом:
int texture_width = next_power_of_two(width);
int texture_height = next_power_of_two(height);
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if (texture_width != width || texture_height != height) {
glTexImage2D(GL_TEXTURE_2D, 0, format, texture_width,
texture_height, 0, format, GL_UNSIGNED_BYTE, NULL);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format,
GL_UNSIGNED_BYTE, image_data);
} else
glTexImage2D(GL_TEXTURE_2D, 0, format, texture_width,
texture_height, 0, format, GL_UNSIGNED_BYTE,
image_data);
Чтобы визуализировать изображение в исходном размере, координаты текстуры x/y должны быть рассчитаны следующим образом (вместо того, чтобы просто использовать 1
):
float x_coordinate = (width - 0.5f) / texture_width;
float y_coordinate = (height - 0.5f) / texture_height;
ES 1.1 официально не поддерживает текстуры NPOT без расширений. Это может случайно работать на некоторых реализациях, но это просто "удача".
Обычный подход состоит в том, чтобы упаковать изображения NPOT в более крупные текстуры POT и соответствующим образом скорректировать координаты текстуры. Вы можете сделать это программно во время выполнения ("упаковка") или просто сделать это в цепочке инструментов (скорее всего, в вашей художественной программе).