Как стать автором
Обновить

Комментарии 38

Трава лишь немного изменила оттенок, с этим можно смириться, но вот камни пострадали критически.

Фиг с ними с камнями, там на небе вылезли адские артефакты!

На самом деле вопрос-то в другом. А какой смысл jpg использовать? В видеопамяти это всё-равно не будет храниться в jpg. Да и для пиксельарта png явно предпочтительнее будет.
На небо я специально попросил не смотреть.) У меня не стандартный загрузчик. было проще на всё формат поменять, поэтому небо пострадало. В рабочем коде небо выглядит как в версии RGB888.
Про jpg я даже придумал, зачем. Например, фотография человеки на прозрачном фоне в png может занять на диске больше. Мне не нужно, но вдруг.
Для этого есть формат DXT.
В своё время еще S3 компания придумала такую штуку как S3TC — сжатые в видеопамяти текстуры. Как раз для экономии видеопамяти, их сейчас вроде бы все карточки поддерживают.

Касаемо решаемой у вас проблемы — надо создавать текстуру в A1R5G5B5 формате и прямо при загрузке задавать альфу. То есть ваш шейдер применить при загрузке совтово. Вы и на памяти экономить будете и еще не будете ронять производительность ифами в шейдере.
А чтобы выровнять гамму, можно как раз таки создать палитру и в шейдере проводить цветокоррекцию, чтобы цвета были идентичны тем, что в 8888 версии.
DXT есть для DirectX. Речь об Андроид, а там из сжатия только ETC1 обязаны поддерживать. Либо так, либо сильно уменьшается число поддерживаемых устройств.
Вообще и камни должны бы выглядеть нормально. Если бы рисовались под такой формат изначально (что для пиксельарта логично вполне, он же канонично палитровый). Автоматика при сильном сжатии цветового пространства очень портит картинку.
В файле на диске всего 32 цвета и он занимает 20 килобайт. Но Андроид не умеет в палитры, поэтому 32 цвета из палитры в видеопамяти превращаются в 32 бита на пиксель. Вручную красивее перерисовать не получится, потому что банально нет другого цвета, т.е. нельзя оттенки использовать, потому что их нет. Всего 4 бита на канал.
Кажется дошло откуда проблема. Там не палитра, а просто обрезанная компонента.
А передавать в шейдер палитру отдельной текстурой нельзя?
Можно! Это уже тема для следующей статьи.) Там ещё вдвое можно использование памяти уменьшить, но несколько усложняется подготовка файлов. Нужно выгружать палитру в отдельный полноцветный файл, сохранять текстуру в градациях серого и сохранять во второй файл (андроид поддерживает однобайтные текстуры альфа-канала, который можно использовать не как альфа-канал). А потом уже в шейдере брать тексель из серой текстуры и по его значению брать цвет из текстуры-палитры.
Зато становятся доступны все фишки использования палитр и даже больше.
А это мысль! bool приводится к float в шейдерах? Я ж только учусь…
но лучше протестировать, как будет быстрее. возможно, отключение блендинга и discard с условием будет производительнее
Проверил. В GLESv2 такое не компилируется.(
Про discard отличная идея! Можно не умножать цвет текселя на цвет вершины, если она прозрачна. Спасибо, дополню статью.
Лучше замерить производительность в стресс-тестах (очень много спрайтов с прозрачностью, сотни тысяч) на мобильных устройствах в режимах с условием, без условия и discard без блендинга (отключение режима прозрачности). Тогда статья будет еще полезнее.
Отключение умножения не поможет — насколько я знаю, шейдер все равно выполняется весь, без разницы, в каком месте произойдет discard. Думаю, если я не прав, меня поправят.
Действительно, про discard пишут о критическом падении скорости, в зависимости от оборудования и вариантов использования. Всё сложно.) Если соберусь проверить производительность разных вариантов прозрачности, обязательно напишу статью.
можно же так, чтобы обе прозрачности учитывались
gl_FragColor.a = pixel.a * float(pixel.rgb != vec3(1.0,0.0,1.0));
Думаю, это — ответ победитель.) Слегка подправил, чтобы прозрачность вершины тоже учитывалась и всё встало на свои места. Даже немного стыдно, что до столь простого варианта сам не додумался.
Ничего страшного, все приходит с опытом. Есть некоторые вопросы, на которые не могут ответить бывалые программисты.
Ох, вы на меня потратили первый комментарий за 4 года… Я тут тоже почти бесправный, поэтому могу отблагодарить за подсказку только искренним Спасибо!
Я, честно сказать, недавно узнал, что мне теперь можно комментировать. Я хотел однажды написать статью для инвайта, но ее забрили без объяснения причин, по-свински, как это было принято на хабре много лет назад.
Все верно — discard ломает всю внутреннюю оптимизацию для любых «tile-based deferred rendering»-gpu, например, всех power sgx-ы (а это все девайсы мобильные девайсы apple). Поэтому тот же альфа-блендинг предпочтительнее по скорости, чем discard по условию. Почитать подробнее можно, например, вот тут: http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-TileBasedArchitectures.pdf.
Тут ситуация такая, что условие в любом случае присутствует. Т.е. либо блендинг, либо дискард, но только после условия. Но я понял, что надо экспериментировать.
К счастью, уже нашёлся вариант без условного оператора.
Внутри это все-равно условие, но в таком виде оно не вызывает бранчинг.
Не понял. А для чего это надо? Картинки в png с прозрачностью и все работает.
4 байта на пиксель, против 2-3 байт с использованием шейдера. Только для экономии видеопамяти.
Я не понимаю как вы экономите… формат 4444 у вас, так?
Значит вы резервируете по 4 бита на пиксель на прозрачность, которую не используете. И при этом урезаете по паре бит.
Нет. 4444 — это если 4 бита на альфа-канал и этот формат я использовать не хочу. Я использую формат 565, но один цвет используется в качестве «прозрачного», который в шейдере делается прозрачным. Т.е. вместо 4096 (в формате 4444) цветов можно использовать 65535 цветов + прозрачный цвет.
Попробуйте при конвертации из RGBA8888 в RGBA4444 или RGBA5651 использовать дизеринг, в вашем случае должно хорошо помочь.
Вот результаты (кликабельно):
image
Я просил не смотреть на небо.) В игре оно загружается в формат 565 и качество не страдает. Речь только о текстурах уровня, а там всего 32 цвета изначально, никакой дизеринг не поможет — разрядности не хватает.
Результат 4444 выглядит так, будто вы просто уменьшили количество цветов, а не уменьшили разрядность. В чём ужимали цвета?
Результат 4444 выглядит так, будто вы просто уменьшили количество цветов, а не уменьшили разрядность

Уменьшил именно разрядность, но сохранил для простоты в обычный RGB8888. Использовал свой варварский метод:

	for (int i = 0; i < pixCount; i++)
	{
		// RGBA5651
		int r = pix[i].r & 0xF8;
		int g = pix[i].g & 0xFC;
		int b = pix[i].b & 0xF8;
		pix[i].r = qrand() % 8 > pix[i].r - r ? r : r + 7;
		pix[i].g = qrand() % 4 > pix[i].g - g ? g : g + 3;
		pix[i].b = qrand() % 8 > pix[i].b - b ? b : b + 7;

		// RGBA4444
//		int r = pix[i].r & 0xF0;
//		int g = pix[i].g & 0xF0;
//		int b = pix[i].b & 0xF0;
//		int dr = pix[i].r - r;
//		int dg = pix[i].g - g;
//		int db = pix[i].b - b;
//		pix[i].r = qrand() % 16 > dr ? r : r + 15;
//		pix[i].g = qrand() % 16 > dg ? g : g + 15;
//		pix[i].b = qrand() % 16 > db ? b : b + 15;
	}

Вот! Вы вот правильно округлили до ближайшего, а автоконверт округляет до меньшего, поэтому в данном случае искажения цвета получились сильнее. В оригинале цвет rgb(42;30;37) был округлён до rgb(34;17;34). Странные цифры из-за float. Если нормально округлить до (48;32;32), то выглядит более прилично даже без дизеринга.
Но принципиально ничего не изменилось: RGB444 — 4096 цветов, RGB565 — 65535 доступных цветов.
Способ округления и формулы дизеринга — это достойно отдельной статьи.
В вашем случае можно вообще написать свой конвертер, благо это просто и быстро. 32 цвета — это 5 бит, ну пусть 6 вместе с битом прозрачности. Хотя можно пожертвовать одним цветом для хромакея. В итоге один конвертер будет переводить картинку в массив пикселей по 5 бит на штуку (на этапе разработки и компоновки ресурсов игры), а другой (в клиенте игры) разжимать все это обратно в любой удобный формат. Вполне вероятно, что такой массив данных удачно сожмется каким нибудь RLE.
Но это, конечно, не изменит размер данных в оперативке, просто уменьшит вес клиента.
Статья как раз об уменьшении данных в оперативке. С занимаемым местом на диске проблем как раз нет. Файл из примера на диске занимает 20 килобайт, а в памяти 1 мегабайт. Использование 565 с прозрачным цветом уменьшает до 512 килобайт. Если текстур много, то для не самых новых девайсов это будет существенная разница. В данный момент на диске все текстуры занимают 400 килобайт (и можно ещё ужать), а в памяти уже 5 мегабайт. С данным шейдером они уменьшаются до 2.5 мегабайт. Некоторые текстуры будут пережаты в ETC1, чтобы в памяти ещё меньше занимать.
Немного не правильно посчитал, забыл умножить на 4 байта. 400КБ на диске в памяти занимают 20 мегабайт. В формате 565 с colorkey занимают 10 мегабайт.
Я, может быть, скажу сейчас какую-нибудь глупость… Но если цветов всего 20, может быть имеет смысл упаковать текстуру в любой формат, самый наименьший. А шейдеру подсовывать палитру для коррекции, одномерную текстуру. Или просто настроить коррекцию по палитре. Будет идеальный цвет на выходе и минимальная разрядность тестуры в памяти. В вашем случае в 1 8888 текстуру можно было бы упаковать в каждый канал по 1 спрайту, у которого будет по 256 цветов + палитра. Экономия выйдет больше, точность идеальная.
Совершенно верно! Так и сделано в самой игре, но это тема для второй статьи. Плюс, там рассказывается, как именно подготовить текстуру, чтобы она занимала 1 байт на пиксель. Просто это намного сложнее, чем кажется (нет нативной поддержки палитризованных изображений), поэтому это будет отдельная статья. Вернее, с точки зрения программирования сложностей никаких, но вот подготовить текстуру индексов и текстуру с палитрой не так просто, как хотелось бы.
Плюс в игре реализованы некоторые визуальные эффекты за счёт динамического изменения палитры. Но я сейчас доделыванием игры занят и не хочу отвлекаться на статью, но она обязательно будет. Позже.)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории