Fake Racing — трёхмерный дизеринг в гоночной игре

Давно хотел применить эффект дизеринга (dithering) к трёхмерной сцене, превратив картинку в однобитную. Наконец, задумка реализована, и теперь мне есть что рассказать и показать.

О чём речь

Дизеринг — искусственное подмешивание шума, позволяющее создать иллюзию высокодетализированного изображения при фактически низкой глубине цвета. Наглядный пример: слева — оригинал, справа — 1-битное изображение, созданное при помощи алгоритма Флойда-Штейнберга.

Fake Racing — трёхмерный дизеринг в гоночной игре

Если с двумерными изображениями технология и алгоритмы давно известны, то при обработке анимированных трёхмерных сцен всё не так однозначно. Проблема в том, что паттерн должен быть привязан к объектам сцены и перемещаться вместе с ними, а во всех известных алгоритмах паттерн либо создаётся динамически (что непригодно даже для двумерной анимации), либо привязан к плоскости экрана.

Obra Dinn

Одна из самых известных игр с эффектом 3D-дизеринга это, конечно, Return of the Obra Dinn — детективная игра, созданная Лукасом Поупом. На «Хабре» есть перевод статьи о реализации дизеринга в Obra Dinn с анимированными гифками, демонстрирующими суть проблемы и пути решения.

Пример из статьи. Слева — обычный паттерн, привязанный к экрану, а не к объектам сцены

Лукас решил поставленную задачу следующим способом: он спроецировал паттерн на сферу с центром в точке обзора, получился эдакий «скайбокс» из паттерна. При вращении камеры вокруг статичной точки поворот сферы оставался привязанным к сцене, и от паразитного мелькания удалось избавиться.

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

Рендерим пиксели

Поскольку спроецированный размер текстуры меняется при приближении или отдалении, встал вопрос, как сохранить плотность пикселей для сохранения нужного оттенка. Изначально я хотел сгенерировать текстуру со сложным распределением пикселей и маской, «зажигающей» пиксели в правильном порядке, но оказалось, что это лишнее — с задачей прекрасно справляется обычный механизм мипмаппинга, нужно только сгенерировать текстуры для мип-уровней самостоятельно. Для этого был написан скрипт на Python.

Из-за меняющегося масштаба, точки, очевидно, не могут быть отдельными пикселями. В текстуре они сохраняются в виде квадрата 2х2 пикселя (экспериментировал с градиентными кружками разных размеров, результат неудовлетворительный), а получившаяся «клякса» превращается ровно в один пиксель с помощью несложного шейдера.

Результат.

Сразу видна проблема — чёткая граница мип-уровней, пиксели на которой еще и мелькают. Игры с ручным выбором мип-уровня по маске ни к чему хорошему не привели, поэтому я снова доверил всё автоматике — 4 выборки из текстуры с небольшим изменением масштаба, в итоге мип-уровень выбирается сам.

Вы могли заметить, что на последней гифке плотность пикселей возросла. Этого удалось достичь, размножив паттерн на 6 каналов двух RGB-текстур. Это нужно и для рисования полутонов. В зависимости от яркости, выбирается соответствующее количество каналов, в результате текстура становится более или менее плотной.

К сожалению, граница перехода от светлых пикселей на тёмном фоне к тёмным пикселям на светлом фоне получилась достаточно резкой.

У меня были сомнения, достаточно ли такого набора полутонов. Но все сомнения развеялись, как только я применил технологию к реальной картинке.

Позже я даже снизил число каналов до четырёх (одна RGBA-текстура) без заметного ухудшения качества картинки.

Что дальше

Проверяем на реальной сцене.

Fake Racing — трёхмерный дизеринг в гоночной игре

Чего-то не хватает. Наверное, выделения края.

Fake Racing — трёхмерный дизеринг в гоночной игре

Позже я немного поэкспериментировал с оттенками. Да, изображение перестало быть однобитным, но едва заметное различие цвета разных пикселей сделало картинку чуть-чуть сочнее. Главное, не переборщить с насыщенностью цвета.

Скриншот демоверсии Fake Racing
Скриншот демоверсии Fake Racing

Ложка дёгтя

В наши дни самым наглядным форматом представления своего продукта является видео. И вот тут беда пришла откуда не ждали: современные видеокодеки совершенно не рассчитаны на кодирование пикселизированной графики. В результате после загрузки на YouTube мы получаем вот это.

Fake Racing — трёхмерный дизеринг в гоночной игре

Пришлось добавить альтернативный режим, в котором «кляксы» паттерна остаются жирными и сглаженными. Из-за этого потерялся шарм и аутентичность, так что включать данный режим есть смысл только для стриминга и записи видео с экрана.

Бороться приходится и с сайтами, автоматически конвертирующими изображения в JPEG. Все скриншоты для этой статьи пришлось вдвое увеличивать без интерполяции, чтобы последующая перекодировка в JPEG не сильно портила картинку.

VR

Став счастливым обладателем VR-шлема, я решил добавить экспериментальную поддержку виара. Как и ожидалось, дизеринг-стиль с виаром не очень сочетается, так что в нём тоже пришлось включить сглаженный альтернативный рендерер из-за паразитных мельканий. Думаю, можно найти более изящное решение решение или изменить стилистику, но это будет уже другая история.

Поиграть и посмотреть живьём

Демоверсия игры Fake Racing доступна в Steam в рамках февральского фестиваля:

В демке пока только базовый геймплей, кое-что интересное уже запланировано и в процессе реализации. Релиз должен состояться в апреле. Думаю, напишу еще один пост с геймплейными фишками, если удастся всё сделать так, как задумал.

До скорых встреч!

1414 показов
7.6K7.6K открытий
22 репоста
70 комментариев
Комментарий удалён модератором

Вообще не понимаю прикола эффекта. Мне просто неприятно смотреть. Я тот расхайпленый квест на корабле не смог играть из-за этого эффекта. Глаза болят голова болит, смотреть на экран неприятно 

Ответить

Комментарий недоступен

Ответить

Поиграл немного, поскольку после этой статьи игра заинтересовала. Понравилась физика авто, столкновения с ботами приятны. Особенно прикольно, что игра не возвращает на трассу после вылета в кювет, но в то же время не заставляет ковыряться между елками, а проезжать сквозь них. Играл контроллером.
Не уверен, правда, что игра сможет надолго затянуть при таком визуале. Впрочем, автор, судя по всему, знает, что делает, надеюсь, есть какие-то козыри в рукаве.  

Ответить

Визуальный стиль очень крутой. Но кроме него, к сожалению, ничего нет. Как минимум нужна карта. Нужны числовые значения для тахометра. Нужно как-то обозначать повороты. Я вижу, что там есть знаки, но из-за специфичного визуального стиля они очень плохо читаются. Хорошо бы было, если бы вы добавили в цветовую гамму еще один контрастный цвет для привлечения внимания игрока к каким-то важным элементам в игре (например, к тем же поворотам на трассе).

Ответить

Почти всё перечисленное есть в планах.

Ответить

Хотел попробовать, но вылетает в самом начале гонки, жаль.

Ответить