Загрузка 4-канальных текстурных данных в iOS
Я хочу загрузить 4-канальные данные текстуры из файла в iOS, поэтому я рассматриваю текстуру как (непрерывную) карту
[0,1]x[0,1] -> [0,1]x[0,1]x[0,1]x[0,1]
Если я использую формат файла .png
, XCode/iOS рассматривают файл как изображение, и поэтому умножает каждый компонент rgb
с a
(предварительно умноженная альфа), искажающая мои данные. Как мне решить это? Примеры могут быть
- использовать две текстуры с компонентами
rgb
(3-канальный) - разделить альфа
- использовать другой формат файла
Из них я считаю лучшим решением использовать другой формат файла. Формат файла со сжатием GL (PVRTC?) Не является независимым от платформы Apple и, по-видимому, имеет низкое разрешение (4 бита) ( ссылка).
РЕДАКТИРОВАТЬ: Если мой собственный ответ ниже, это правда, невозможно получить 4-канальные данные PNG в iOS. Поскольку OpenGL предназначен для создания изображений, а не для их представления, должна быть возможность каким-либо образом загрузить 4-канальные данные. png - это формат файла для изображений (и сжатие зависит от всех 4 каналов но сжатие одного канала не зависит от других каналов), поэтому можно утверждать, что я должен использовать другой формат файла. Итак, какие другие форматы сжатых файлов я должен использовать, которые легко читать / интегрировать в iOS?
ОБНОВЛЕНИЕ: "комбинаторный" упомянул способ загрузки 4-канальных предварительно не умноженных текстур, поэтому мне пришлось дать ему правильный ответ. Однако это решение имело некоторые ограничения, которые мне не нравились. Мой следующий вопрос - "Доступ к необработанным 4-канальным данным из файлов png в iOS":)
Я думаю, что это плохой дизайн библиотеки, не позволяющий читать 4-канальные данные PNG. Я не люблю системы, пытающиеся быть умнее себя.
5 ответов
Поскольку вы рассматривали PVRTC, тогда использование GLKit может быть вариантом. Это включает в себя GLKTextureLoader, который позволяет загружать текстуры без предварительного умножения альфа. Используя, например:
+ (GLKTextureInfo *)textureWithContentsOfFile:(NSString *)fileName options:(NSDictionary *)textureOperations error:(NSError **)outError
и передача словаря опций, содержащего:
GLKTextureLoaderApplyPremultiplication = NO
Вы должны использовать libpng для загрузки PNG без предварительно умноженных цветов.
Он написан на C и должен компилироваться для iOS.
У меня были похожие проблемы с Android, а также мне пришлось использовать стороннюю библиотеку для загрузки файлов PNG без предварительно умноженных цветов.
Вы можете просто запросить, чтобы Xcode не "сжимал" ваши файлы PNG. Нажмите на свой проект в левом верхнем углу, выберите "Build Settings", найдите "Compress PNG Files" и установите опцию "No".
Что касается других ваших вариантов, пост-деление не является плохим решением, но очевидно, что вы потеряете общую точность, и я считаю, что поддерживаются и TIFF, и BMP. PVRTC специфичен для PowerVR, поэтому он не специфичен для Apple, но также не является полностью независимым от платформы и специально разработан для сжатия с потерями, которое легко распаковать с небольшим вводом в GPU. Как правило, вы увеличиваете разрешение текстуры, чтобы уменьшить количество бит на пиксель.
Это попытка ответить на мой собственный вопрос.
Невозможно загрузить файлы без предварительного умножения.png.
Опция kCGImageAlphaLast
является допустимой опцией, но не дает допустимой комбинации для CGBitmapContextCreate
( ссылка). Однако это действительный вариант для CGImageRef
"S.
Какие настройки сборки COMPRESS_PNG_FILES
в XCode, упомянутом выше, это преобразование файлов.png в другой формат, а также умножение каналов. rgb
с a
( ссылка). Я надеялся, что отключение этой опции позволит получить доступ к данным канала в моих реальных файлах.png. Но я не уверен, возможно ли это. Следующий пример представляет собой попытку доступа к данным.png на низком уровне, как CGImageRef
:
void test_cgimage(const char* path)
{
CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename(path);
CGImageRef cg_image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, NO,
kCGRenderingIntentDefault);
CGImageAlphaInfo info = CGImageGetAlphaInfo(cg_image);
switch (info)
{
case kCGImageAlphaNone: printf("kCGImageAlphaNone\n"); break;
case kCGImageAlphaPremultipliedLast: printf("kCGImageAlphaPremultipliedLast\n"); break;
case kCGImageAlphaPremultipliedFirst: printf("kCGImageAlphaPremultipliedFirst\n"); break;
case kCGImageAlphaLast: printf("kCGImageAlphaLast\n"); break;
case kCGImageAlphaFirst: printf("kCGImageAlphaFirst\n"); break;
case kCGImageAlphaNoneSkipLast: printf("kCGImageAlphaNoneSkipLast\n"); break;
case kCGImageAlphaNoneSkipFirst: printf("kCGImageAlphaNoneSkipFirst\n"); break;
default: break;
}
}
который дает "kCGImageAlphaPremultipliedLast" с COMPRESS_PNG_FILES
отключен. Поэтому я думаю, что iOS всегда конвертирует.png файлы, даже во время выполнения.
Есть лучшее решение, более быстрое (примерно в 3~5 раз) и кроссплатформенное
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
// force 4 channel rgba, force flipy
// image: first pixel is top left, OpenGL assume first is bottom left, so need flipy
bool flipy = true;
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(flipy);
unsigned char *imageData = stbi_load(path.c_str(), &width, &height, &nrChannels, 4);
// load data to your texture
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bytes);
free(imageData);
Не на 100%, что вы хотите, но я решил проблему, используя этот подход: поместите альфа-канал в отдельный черно-белый png и сохраните исходный png без альфы. Таким образом, занимаемое пространство примерно одинаково. Затем в моем загрузчике текстур загрузите оба изображения и объедините их в одну текстуру.
Я знаю, что это только обходной путь, но, по крайней мере, он дает правильный результат. И да, это очень раздражает, что iOS не позволяет загружать текстуры из PNG без предварительно умноженной альфы.