Pull to refresh

Dxt сжатие в играх

Reading time 6 min
Views 28K
В этой статье я хочу поделиться своим опытом разработки мобильной игры, поскольку я Windows Phone разработчик, я буду рассказывать про свой опыт применительно к этой системе.

Память и текстуры


Если Вы уже занимались разработкой мобильных игр, то основное зло не в нехватке ресурсов CPU/GPU, а в нехватке памяти. Именно о памяти нужно думать в мобильной разработке в первую очередь. В Windows Phone 7 ограничение было в 100мб, в Window Phone 8 стало получше, но не сильно:
Тип лимита Тип приложения Телефоны с маленьким количеством памяти 1- Гб телефоны 2-Гб телефоны
Default XNA или native 150 MB 150 MB 150 MB
Default XAML/.NET excluding XNA 150 MB 300 MB 300 MB
Higher All app types 180 MB 380 MB 570 MB

И если Вы разрабатываете игру, в которой довольно большое количество спрайтов (уложенных, конечно же, в атласы) — то вы рано или поздно задумаетесь о количестве этих самых атласов и сжатии текстур.
Стандартный атлас, с которым работают все более или менее уважающие себя устройства — это 2048х2048 пикселей. Что в несжатом виде (32 bits per pixel) будет занимать аж 2*2*4 = 16 Мб памяти. Тогда на выручку приходят форматы сжатия текстур, в нашем случае это DXT сжатие.
Сжатые текстуры не только требуют значительно меньше памяти видеокарты, но и вообще отображаются быстрее, чем несжатые текстуры, за счет снижения требований к пропускной способности. Но некоторые качества изображения могут быть потеряны из-за сжатия. Тем не менее, снижение объема памяти позволяет увеличить разрешение текстур, которые будут использоваться, что действительно может дать существенный выигрыш в качестве.

Dxt компрессия уже реализована в .XNA Frameworke, а также в Monogame.
Для наглядности, возьмем 256 изображений размером 128х128 пикселей, один текстурный атлас из этих текстур размером 2048*2048, и сожмем этот атлас.
Ходят слухи о том, что быстрее подгружаются текстуры размер, которых кратен степени двойки, для эксперимента изменим немного оригинальную текстуру, срезав один пиксель, доведя ей размер 2048*2047.
Чтобы показать действенность описанных методов сделаем контрольные замеры. Проводить их будем на телефоне Nokia Lumia 800. Для большей точности сделаем для каждого метода 10 замеров.
Сведем получившиеся результаты в таблицу 1 и подведем итог.

Таблица 1. Скорость загрузки изображений.
  256 текстур по 128*128 1 dxt 2048*2048 1 origin 2048*2048 1 origin 2048*2047
1 00:00:00.6460000 00:00:00.0330000 00:00:00.1510000 00:00:00.1200000
2 00:00:00.6440000 00:00:00.0330000 00:00:00.1510000 00:00:00.1180000
3 00:00:00.6470000 00:00:00.0410000 00:00:00.1870000 00:00:00.1570000
4 00:00:00.6400000 00:00:00.0330000 00:00:00.1490000 00:00:00.1190000
5 00:00:00.6420000 00:00:00.0330000 00:00:00.1500000 00:00:00.120000
6 00:00:00.6340000 00:00:00.0470000 00:00:00.1320000 00:00:00.161000
7 00:00:00.6340000 00:00:00.0500000 00:00:00.1590000 00:00:00.179000
8 00:00:00.6300000 00:00:00.0500000 00:00:00.1580000 00:00:00.179000
9 00:00:00.6330000 00:00:00.0480000 00:00:00.1580000 00:00:00.179000
10 00:00:00.6210000 00:00:00.0470000 00:00:00.1650000 00:00:00.1820000
Среднее 00:00:00.6371000 00:00:00.0412000 00:00:00.1558000 00:00:00.1514000

Для наглядности график зависимости различных методов загрузки от времени (рис. 1.)


Рисунок 1. График зависимости различных методов загрузки от времени.

Таблица 2. Размеры изображений
  Методы Размеры, Мб
1 Размер 256 текстур по 128*128 16
2 Размер текстуры 2048*2048 без сжатия 16
3 Размер текстуры 2048*2048 с сжатием 4

Как мы видим из представленных опытов Dxt сжатие очень эффективно. Рассмотрим его более подробно.
DXT сжатия (также иногда известный как сжатие S3 ) на самом деле очень простое. Вот как это работает:
  • Изображение делится на блоки 4х4
  • Для каждого блока, находится два самых важных цвета
  • Получившиеся два цвета хранятся в 16 битах, в формате RGB 5.6.5
  • Для каждого из 16 пикселей в блоке, хранится 2 бита значения, указывающее, как далеко он находится между двумя основными цветами

Эта простая схема, оказывается, работает удивительно хорошо для многих реальных изображений мира.

Есть пять вариантов DXT сжатия:
  • DXT1 работает, как я описал выше, плюс некоторая дополнительная магия для кодирования альфа-канала
  • DXT3 цвет кодируется также, как и DXT1, а также хранит 4 бита значения альфа-канала в пикселе
  • DXT5 цвет кодируется также, как и DXT1, а также подобная схема используется для кодирования альфа-канала
  • DXT2 и DXT4 были не очень-хорошо продуманной попыткой стандартизации и не чего дельного не вышло. Вы должны делать вид, что они не существуют

DXT1 использует 64 бита на блоке 4x4. По сравнению с 32-битной несжатой текстурой, это 8x кратная степень сжатия. DXT2-5 использует 128 бит в блоке 4x4, которая дает 4х кратная степень сжатия.

А теперь плохая новость: DXT сжатие – это сжатие с потерями. Иногда они могут быть очень большими. На самом деле это работает очень хорошо для некоторых изображений и совсем не подходит для других.
Итак, когда Вы хотите использовать Dxt компрессию скажем в XNA, когда Вы устанавливаете свойство текстуры Texture формат, параметр DxtCompressed, Content Pipeline автоматически выбирает между DXT1 и DXT5, в зависимости от того, имеет ли ваша текстура альфа-канал. Если она не содержит альфа-канал или содержит однородный альфа-канал будет использовать DXT1, чтобы получить наилучшую степень сжатия, но если текстура содержит дробные значения альфа-канала, он выберет DXT5 вместо этого.

Рассмотрим более подробно каждый из способов сжатия:
DXT1

DXT1 формат предназначен для декомпрессии в реальном времени аппаратными средствами на видеокарте во время рендеринга. DXT1 является форматом сжатия с потерями, с фиксированным коэффициентом сжатия 8:1. DXT1 сжатие является одной из форм кодирования блока усечениями (BTC), где изображение разбивается на непересекающиеся блоки, и пикселей в каждом блоке квантуются на ограниченное число значений. Значения цвета пикселей в блоке 4x4 пиксель аппроксимируются с равноудаленных точек на линии, проходящей через цветовое пространство RGB. Эта линия определяется двумя конечными точками, и для каждого пикселя в блоке 4x4 2-битный индекс хранится в одном из равноудаленных точек на линии. Концы линии, проходящей через цветовое пространство, кодируются в 16-битный формат 5:6:5 RGB и одна или две промежуточные точки генерируется за счет интерполяции. Формат позволяет хранить 1-битный альфа-канал, путем переключения на другой режим, основанный на порядке конечных точек, где создается только одна промежуточная точка и один вывод дополнительного цвета указывает, что он черный и полностью прозрачный.

Посмотрим на рисунок 2 представленный ниже:
Левое изображение является оригиналом. Правое иллюстрирует сжатие в формате DXT1.


Рисунок 2. Пример Dxt1 сжатия.

Визуально сжатое изображение не отличается от оригинала, что делает результаты сжатия приемлемыми для большинства пользователей. Сжатие, однако, значительно уменьшает размер текстуры.
В данном случае с 256 КБ до 32 КБ.

Однако все не так радужно с этой текстурой (рисунок 3):


Рисунок 3. Пример Dxt1 сжатия.

Основной проблемой является появление шума внутри текста, a также видны отчетливые полосы на фоне градиента.


Рисунок 4. Шумы внутри текста.

На рисунке 5 показано, как сжатие влияет на цвет. Слева Вы видите 16 оттенков красного, от чистого красного до чистого черного. Справа Вы видите четыре цвета, которые получились в результате DXT сжатия, из этих 16 оттенков.


Рисунок 5. Влияние сжатия на цвет.

Рисунок 6 показывает, что происходит, когда разные цвета не находятся на одной линии в цветовом пространстве. В этом случае были использованы все крайности палитры RGB (красный, зеленый и синий). Очевидно, в результате интерполированные цвета не совпадают с оригиналами. Обычно в области пикселей 4x4 не такой широкий выбор цветов, но это показывает, что текстуры с различными цветами страдают больше.


Рисунок 6. Влияние сжатия на цвет.

DXT1 сжатия обычно хорошо работает для шумных текстур и не так хорошо для чистых изображений, а также градиентов. Хитрость заключается в том, чтобы использовать его везде, где это возможно и не использовать его только на тех текстурах, где артефакты сжатия слишком нежелательны.

DXT5

DXT5 формат, отличается от DXT3 формата, тем что он хранит информацию об альфа канале, подобно тому как хранить информацию о цвете.
Для информации об альфа-канале он использует палитру подобную той, как храниться цифровая информация. Эта палитра содержит минимальное и максимальное значение альфа-канала. Различают два варианта с 6 и 4 опорными точками.
6 других значений альфа интерполируются между этим минимумом и максимумом. Таким образом, это позволяет более постепенные изменения значения альфа.
Второй вариант делает интерполяцию только для 4 других значений альфа-канала между минимальной и максимальной величиной, но также добавляет альфа-значения 0 и 1 (для полностью прозрачными и не прозрачными). Для некоторых текстур это может дать лучшие результаты.


Рисунок 7. Пример Dxt5 сжатия.

Как видим, края не очень хорошо обрабатываются в некоторых частях.


Рисунок 8. Рванные края при использовании Dxt5 сжатия.

Размер текстуры при этом уменьшился с 256 КБ до 64 КБ.
Потери качества на реальных изображениях не столь значительны и ими можно для большинства изображений пренебречь.
Использование Dxt сжатия позволяет:
  • Уменьшить размер установочного пакета
  • Уменьшить размер использования оперативной памяти
  • Увеличить скорость «отрисовки» изображений

В своем проекте, после создания текстурного атласа, я получаю на выходе .jpg/.png/.bmp и описание атласа в .xml/.txt/.json. Поскольку я использую XNА/Monogame для сжатия в .xnb я использую XNA 4.0 Content Compiler в целом это очень понятное и простое решение, только для использования Dxt сжатия необходимо дописать в ContentBuilder’e при создании buildProject еще одно свойство:
buildProject.SetProperty(“XnaCompressContent“, “True”);

Источники:
S3 Texture Compression
DXT compression explained
DXT Compression Techniques
Real-Time YCoCg-DXT Compression
Tags:
Hubs:
+13
Comments 16
Comments Comments 16

Articles