Сразу хочу оговориться, что у меня нет художественных навыков, и я никогда не работал в области графки и/или дизайна.
Первые попытки повысить качество графики я предпринял еще в далеком 2013 году, попробовав растянуть текстуры, просто увеличив размер картинки в фотошопе. Эффект оказался ничтожным, если вообще был. Было грустно, но не критично. Игра продолжала нравиться.
Спустя несколько лет, в 2017 я пробовал вручную создать текстуры-аналоги простейших элементов, вроде ткани на шляпках и одежды, и результат оказался очень даже хорошим. Очень трудозатратным (на 5-6 файлов у меня ушло около 5-7 вечеров), но довольно качественным. Правда, имитировать текстуры материалов оказалось гораздо проще, чем перерисовывать сложные текстуры, например траву. Собственно, на сложных текстурах всё и закончилось: адаптированные текстуры из интернета смотрелись невпопад, хоть и в лучшем разрешении.
В прошлом году я наткнулся на замечательную статью на хабре, в которой была описана серебрянная пуля программа для повышения качества картинки на базе нейросетевых алгоритмов.
Я твердо решил довести дело до конца на этот раз. Кому интересен сразу финал:
Результаты до/после обработки (много картинок)
Поиск исходных текстур
Найти файлы с текстурами было просто — почти все они лежали в папке DataTextures. Но иногда повторялись в разных папках — m256, m128, m64 и m32. Вероятно, для разного уровня разрешения — свои файлы. Причем даже на максимальной графике некоторые небольшие текстуры брались из папок m128 и m64. Наверное, что бы сэкономить ресурс графического процессора во время масштабирования. Например, текстура дракона существовала в папках m256, m128 и m64, а текстура одежды гномов в m128, m64 и m32.
Так же был обнаружен специальный файл .txt, где был список всех уникальных текстур и три колонки — low, medium и high, где на пересечении стояли названия папок, из которых брать соответствующую текстуру. Забавно, что на максимальных настройках игра полностью игнорировала этот конфиг и брала первый файл с заданным названием, последовательно перебирая папки m256->m128->m64->m32.
Помимо текстур объектов, были еще папки с элементами интерфейса и текстурами окружающей земли. Надо сказать, что Гномы — игра про копание в вертикальной плоскости, и поэтому проходы и пещеры визуализируются в профильном срезе и являются фоном для всех остальных объектов. Поэтому для текстур грунта выделена целая отдельная папка со своей внутренней структурой, но об этом чуть позже.
Что бы убедиться, что найти удалось все нужные текстуры, я просто перекрашивал файлы в серый цвет и смотрел не осталось ли в игре чего разноцветного:
Поиск оптимальной трансформации
Перед массовой обработкой я провёл исследование, что бы определить максимально результативный способ растянуть текстуры.
Игра старая, и нужно было проверить допустимые границы размеров файлов. У меня получилось увеличить с 256 до 4096 пикселей, и игра это проглотила. Что бы убедиться, что игра не производит промежуточное сжатие файлов, я сделал текстуру с черно-белой сеткой шириной в 1-2 пикселя, и проверял, различима ли она в игре на разного рода объектах. Небольшие предметы казались серыми, но если игровой объект был достаточно большой, то и решетка была хорошо видна. Значит, нет предела совершенству, отличная новость!
Эксперимент так же показал, что разрешение файлов должно быть строго квадратное, и обязательно кратное степени двойки. То есть файлы 256×512 или 257×257 не прорисовываются, а на их месте появляются белые пятна.
Так же было обнаружено, что при слишком больших текстурах, игра периодически вылетала. Причем вылетала не сразу и не на конкретном объекте, а спустя непонятное количество времени. После часа тестов, у меня сложилось стойкое ощущение, что переполнялся внутренний буфер памяти. Игра вылетала, когда камера двигалась по слишком большому числу разных объектов. Нашел в реестре параметр TexVideoMemLimit, и увеличил его значение. Не помогло.
Остановился на опции увеличения стороны текстур х4 (256->1024, 128 -> 512 и т.д.) как наиболее оптимальным с точки соотношения видимого результата/риска вылетов. Стоить отметить, что увеличение стороны текстуры х4 приводит к увеличению площади квадратично, т.е. х16, а размер файлов растет пропорционально площади, а не стороне квадратной формы текстуры.
Все файлы находятся в формате TGA, и с этим пришлось повозиться. Программа Gigapixel AI не работает с таким форматом. Вначале я конвертировал файлы в JPEG, растягивал их, а потом сохранял обратно в TGA. Но внутри игры все текстуры теряли свою прозрачность после такого преобразования. Дело оказалось в том, что в файлах TGA активно используется альфа-канал, а при таком преобразовании — альфа канал полностью стирался.
Пришлось искать, как добавлять обратно прозрачность. Первое что пришло в голову — выделить черные области в файле с текстурами через Фотошоп и удалить их оставив прозрачный фон. Вариант сработал, но некоторые текстуры оказались полу-прозрачными, например зеленое стекло, и вариант не подошел. Жаль, что заметил я это очень поздно, когда уже был в процессе массовой трансформации.
В качестве решения проблемы альфа-канала остановился на том, что я делаю два файла: в первый я копирую черно-белым маску альфа-канала из TGA и растягиваю стандартными средствами фотошопа, а второй — это растянутый нейросетью JPEG. Потом я копировал черно-белое изображение маски в обработанный файл JPEG на слой с альфа-каналом, а сохранял всё как новый TGA. На выходе получалась красивая текстура нужного размера с подходящим альфа-каналом исходной формы.
Любопытно, что размер текстур напрямую не коррелирует с размером объектов игрового мира. Например, огромный камень на заднем фоне использовал текстуру 128×128, а не 256×256. Полагаю, это делалось из экономии ресурсов компьютеров тех времен, а предпочтение детализации отдавалось наиболее часто встречающимся объектам. Поэтому отдельные текстуры я потом растянул на х8, что было больше изначально запланированных х4, но создавало больший эффект.
Сама программа AI Gigapixel предоставляла 3 входных параметра:
1) Мультипликатор увеличения: х2, х4 и т.д.
2) Уровень подавления шумов: ползунок от 0 до 100%
3) Уровень снижения размытия (sharpness): так же от 0 до 100%
Поигравшись с этими параметрами, я пришел к выводу, что идеальной формулы нет. Для материалов шумоподавление имеет больше значения, а для чётко различимых образов, вроде одежды или шляпки гриба, чёткость выходит на первый план. Я разделил файлы на три категории: материало-подобные текстуры, индивидуальные текстуры зданий и одежды, а так же смешанные. Первые две категории обрабатывал массово по двум наборам заданных параметров (повышенное шумоподавление для одного и повышенная резкость для другого), а для смешанных задавал параметры индивидуально.
Повторить 500 раз
Играться с расширением и разного рода параметрами было весело. Вау эффект от обработки каждой конкретной текстуры подогревал интерес и приносил море удовольствия. Но даже в этом случае, на 5-ой итерации проб и ошибок начинается монотонная работа: открой файл в фотошопе, cохрани в JPEG, создай растянутую копию TGA, скорми JPEG в AI Gigapixel, перенеси альфа-канал, скопируй обратно в папку с игрой, запусти игру, загрузи сохранение, сделай скриншот, сравни.
Теперь же до меня дошло осознание — эту операцию нужно сделать не 5 и не 10 раз, а 500. И делать это вручную — значит сгореть раньше, чем работа будет закончена.
Гугл подсказал, что в Фотошопе есть макросы, которые могут записывать последовательность ручных действий, а потом их воспроизводить. Так же Фотошоп умеет применять этот макрос сразу ко всем файлам в целой папке, а результат сохранять в новое место. Что ж, у меня все шансы дойти до финала и сохранить рассудок.
Чего Фотошоп не умеет, так это искать пары файлов и совместно их обрабатывать. Если самостоятельно открыть два файла, то макрос вполне успешно сможет обработать их, перемещаясь от предыдущей вкладки к следующей. Но для этого нужно открыть два файла руками. Именно это мне и нужно было сделать для копирования альфа канала из грубо растянутой черно-белой маски из TGA в JPEG файл. Дело одноразовое на два-три часа, и искать решения по автоматизации я счел нецелесообразным. Пришлось изрядно попотеть, вручную скармливая пары файлов в Фотошоп-макрос.
Я решил структурировать работу, и все промежуточные результаты сохранять в отдельные папки, что бы не запутаться. Всего у меня вышло 7 папок. Некоторые из них были для бэкапа, некоторые промежуточными результатами, а некоторые — с финальным результатом. Место это кушало довольно много, но сильно упрощало работу.
Полученный результат я сохранил в github, что бы любой желающий мог воспользоваться моими наработками. В репозитории всё еще есть SuperHd версия, но из-за нее игра часто крашится. Я всё же оставил её на случай, если в будущем кто-то сможет побороть внутренний размер буфера текстур.
Непобежденный движок игры
Исходный размер текстур грунта — 64×64. Увеличение качества этих текстур даёт наибольший эффект, т.к. земля всегда находится очень близко к игроку и практически всё время в зоне видимости.
Земля бывает трех видов в игре: мягкий грунт, твердый гранит и глубинная мягкая почва. Все три имеют разный набор квадратных текстур, каждая из которых случайным образом укладывается в единую мозаику в рамках своего набора. То есть из 15 файлов мягкого грунта выбираются случайные и мозаикой укладываются на экране, а всего существует 45 файлов грунта, по 15 на каждый тип.
Сами текстуры земли отрисовались хорошо после масштабирования. Но на границе разного вида грунтов появился неприятный артефакт.
Если два типа грунта встречается на карте рядом, то движок на одном квадратном поле рисует сразу две текстуры разных грунтов, но добавляет к обоим маску альфа-канала, образующую переход. В итоге половина квадратной области остается от одной текстуры, а другая половина — от другой.
Т.к. мозаика укладывается в плоском срезе, то переходы могут быть половинками, уголками или наискосок. И для каждого вида перехода есть свой файл в папке Data/Ground/ с префиксом Msk_*.tga, который содержит черно-белую текстуру для наложения в виде альфа-канала.
Когда я первый раз растянул текстуры грунта, то увидел печальную картинку — маска перехода отрисована не полностью, а только на 1/4 ( это для увеличенных х4 текстур) по принципу с левого верхнего угла, построчно горизонтально. Дальнейший эксперимент показал, что для х8 текстур маска перехода отрисовывается на 1/8. Если один вид грунта был х1, а другой х2, то маску перехода начинало колбасить расщепляя её на фракталы.
За год я так и не смог победить эту напасть. Почти все игровые скрипты находятся в папке Data/Scripts в текстовых файлах *.tcl, но ни в одном из них нет ничего похожего на параметр, который бы отвечал за прорисовку альфа-канала перехода между типами грунта. Кажется, что где-то в движке игры (в exe-файле?) есть хардкод, который имеет ссылку на файлы с масками перехода и прямо предписывает остановить рендер после отрисовки 4096 пикселей (64×64).
Навыков декомпиляции и деассеблирования у меня нет, а диллетантские попытки ковырять exe-шник сторонними тулзами ни к чему не привели. На текущий момент у игроков есть выбор: низкое качество грунта и плавные перехода, или высокое качество грунта и резкие переход с артефактами.
Заключение
Для того, что бы переработать текстуры в целой игре, пусть даже и с помощью нейросети, без базовых навыков работы с графикой очень сложно. В самом начале это казалось не так, но сейчас я осознал, что первую половину времени я потратил на изучение, вроде бы, очевидных вещей, таких как работа с альфа-каналом и пакетная обработка изображений.
Совсем без этих навыков не получится переработать даже с помощью нейросети. В сумме я потратил около месяца, проводя вечера за перерисовкой текстур.
Кроме того, для такой задачи вполне могут понадобиться знания ассемблера, т.к. часть задач оказалось невозможно решить на уровне работы с графикой и файлами текстур. Я очень расстроен фактом, что так и не смог побороть напасть с переходами текстур земли, но даже с учетом этого игра стала выглядеть намного приятнее.
Надеюсь, мой опыт вдохновит кого-то на подобную затею в отношении старых и любимых игр. Делитесь в комментариях своими историями.