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 в рамках февральского фестиваля:

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

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

157157
70 комментариев
Комментарий удалён модератором

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

24
Ответить

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

5
Ответить

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

3
Ответить

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

2
Ответить

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

2
Ответить

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

2
Ответить