image

Создатель Papers, Please Лукас Поуп работает над новым трёхмерным проектом Return of the Obra Dinn, в котором пытается с помощью эффекта дизеринга воссоздать в игре ощущение старинной книги.

Для начала краткое объяснение: Obra Dinn выполняет внутренний рендеринг всего в 8-битной палитре в градациях серого, а затем на этапе постобработки преобразует конечные выходные данные в 1-битные значения. Преобразование из 8-битного в 1-битный цвет выполняется сравнением каждого пикселя исходного изображения с соответствующей точкой в тайловом паттерне дизеринга. Если значение пикселя изображения больше значения точки паттерна дизеринга, то выходному биту присваивается значение 1, в противном случае оно равно 0. Выходные данные упрощаются до 1-битных значений, а глаз зрителя объединяет пиксели, аппроксимируя из них больше битов.


Преобразование исходного изображения по шаблону дизеринга

Двумя компонентами этого процесса являются исходное изображение и паттерн дизеринга. В различных случаях Obra Dinn использует два отличающихся паттерна: матрица Байера 8x8 для более плавного диапазона оттенков и поле синего шума 128x128 для менее упорядоченного вывода.


Байер/синий шум

Результат внутри движка без каркасных линий. Байер на сфере, синий шум на всём остальном.

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

Двигаем сферу

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

Образец A. Для более приятного изображения контраст уменьшен.

Попробуйте сфокусироваться на каком-нибудь объекте, пока он двигается, и вы поймёте, в чём основная проблема Obra Dinn в полноэкранном режиме. Существуют способы исправить это, и чаще всего они сводятся к «этот стиль не работает, замени его». Я довольно далеко зашёл на этом пути, экспериментируя с различными стилями, но потом вернулся назад и задал себе вопрос — возможно, не стоит давать этим гадским пикселям мешать мне.

Стабилизируем дизеринг


Чтобы дать глазам возможность наилучшим образом всё рекомбинировать, дизеринг оптимальнее всего использовать с точками паттерна дизеринга, имеющими корреляцию 1:1 с выходными пикселями. Но если будет присутствовать корреляция «только» с выходными данными, то при применении постэффектов сцены не будет никакой связи между отрендеренной геометрией и пороговым паттерном. В каждом кадре у движущихся элементов сцены будет новое пороговое значение. Вместо этого я хочу, чтобы паттерн дизеринга был «приклеен» к геометрии и казался стабильным при движении вместе с остальной сценой.

Здесь возникает проблема наложения. Существует конфликт между «идеальным» наложением паттерна дизеринга (1:1 с экраном) и идеальным наложением на сцену (x:1 с геометрией), так что нужно быть готовым идти на компромиссы. БОльшая часть моей работы посвящена наложению входного паттерна дизеринга на различные пространства, которое обеспечивает наилучшее совпадение паттерна с геометрией сцены. Здесь всё выполняется на этапе до задания порогов.

Пространство текселов


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


Паттерн дизеринга в пространстве текселов

Ну, в целом ожидания себя оправдали. Наложение на все объекты выполнено по-разному, поэтому масштабы из паттернов не совпадают. Их можно унифицировать. Но настоящая проблема заключается в искажениях. Любой ресемплинг из одного пространства в другое приведёт к искажениям, а для паттернов дизеринга не так просто выполнить mip-текстурирование или фильтрацию, как для традиционных текстур. Однако доведём это до конца:

Применение к подвижной сцене

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

Деформация при движении


Если я хотел, чтобы паттерн дизеринга отслеживал движение геометрии под ним, то почему бы просто не деформировать паттерн на основании изменения позиции каждого отрендеренного пикселся в сцене? Действительно, почему бы не попробовать. Это немного похоже на motion blur, при котором каждый пиксель отслеживает своё движение относительно предыдущего кадра. В этом случае я обновляю текстуру дизеринга, чтобы её паттерн двигался вместе со сценой. Если пиксель сцены не присутствовал на предыдущем кадре, то в нём паттерн дизеринга перезагружается. Реализацию этой техники очень облегчила статичность игры — мне нужно было беспокоиться о движении камеры, а не отдельных объектов.

Деформация паттерна дизеринга для сохранения покадровой согласованности со сценой

Это была довольно «быстрая и грязная» попытка, но стали очевидны некоторые факты. Во-первых, это в чём-то работает. Во-вторых, паттерну дизеринга нужно учитывать соседей — он не может быть просто отдельными пикселями. Если рассматривать каждый пиксель отдельно, как делается в этом способе, то очевидно, что мы получим разрывы и искажения в паттерне. В этой тестовой сцене я сдвинул камеру, чтобы показать это на примере сундука. Посмотрев на сам искажённый паттерн дизеринга, легче это заметить.

Задание порога сплошным серым цветом с деформируемым паттерном дизеринга

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

Наложение на экран со смещением


При составлении уравнений для деформируемого дизеринга из них выпало очень простое преобразование:

DitherOffset = ScreenSize * CameraRotation / CameraFov

Сдвиг наложенного на экран паттерна дизеринга на основании поворота камеры

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

Смещение паттерна дизеринга для отслеживания поворота ровно на один экран fov камеры

Заметьте: похоже, что подвергнувшиеся дизерингу пиксели стула в основном движутся с геометрией. То же самое относится и к сфере. Более перпендикулярные к полю зрения плоскости отображаются не очень хорошо — пол по-прежнему выглядит хаотичным.

Хотя подход и не идеален, простой сдвиг наложенного на экран дизеринга сохраняет общий паттерн и движение сцены, чтобы глазу удобнее было отслеживать вместе. Я был этим очень доволен. Занимаясь подчисткой кода и коммитами, выпустив один-два поста в devlog, я всё равно не мог избавиться от мысли об идеально прилепленном дизеринге:

Пространство мира — кубическое наложение


Предыдущие эксперименты показали, что любая корреляция между паттерном дизеринга и геометрией сцены должна игнорировать информацию глубины, получаемую от сцены. На практике это означает, что дизеринг можно прицеплять к геометрии во время поворота камеры, но не её перемещения. Это не так уж плохо для Obra Dinn, учитывая медленный темп игры и наблюдательную роль игрока. Обычно в игре он ходит по кораблю, останавливается и смотрит на объекты. При ходьбе на экране происходит так много изменений, что плавающий дизеринг не особо очевиден.

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


Паттерн дизеринга наложен на куб, центрированный относительно камеры


Вид из камеры, смотрящей в угол. Масштаб наложения для наглядности увеличен.

Наложение на куб работает неплохо, когда смотришь на стороны, но не так хорошо, когда камера направлена на угол. Паттерн дизеринга по-прежнему идеально зафиксирован в 3D-пространстве при повороте камеры. Даже при грубых проверках результат выглядит многообещающим.

Задание порога сцене с помощью наложенного на куб паттерна дизеринга

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

Пространство мира — сферическое наложение


… но благодаря сфере я подобрался достаточно близко.


Наложение паттерна дизеринга на внутренность сферы

Поиск этого конкретного сферического наложения потребовал определённого времени. Не существует способов идеального замощения сферы квадратной текстурой. Можно было бы переопределить матрицы дизеринга через сетку шестиугольников или чего-то подобного, что хорошо замощает сферу. Возможно, получилось бы, но я не пробовал. Вместо этого я «взломал» замощение сферы, добившись тщательной настройкой того, чтобы «кольцевое» наложение исходного паттерна дизеринга давало хорошие результаты.

Применённый к сцене эффект

Лучше, чем с кубом, но по-прежнему много искажений. Размер сферически наложенной точки очень похож на размер экранного пикселя — отличается ровно настолько, чтобы создавать муар. Я чувствовал, что близок к решению, и очень просто исправить такие искажения с помощью суперсемплирования: применить порог дизеринга при более высоком разрешении, а затем снизить его.


Сферически наложенный паттерн дизеринга при увеличении 2x и со сниженным до 1x разрешением

Задание порога при 2x, с последующим снижением разрешения до 1x

Это пока самый лучший из полученных мной результатов. Тут есть несколько компромиссов:

  1. Точки паттерна дизеринга становятся больше в размерах и менее эффективными по краям экрана
  2. Паттерн не выравнен по направлениям «верх-низ-лево-право» для большинства поворотов камеры
  3. Выходные данные больше не являются 1-битными из-за конечного снижения разрешения

Но преимущества очень велики:

  1. Дизеринг отлично прикрепляется ко всем поворотам камеры. В игре это ощущается немного странно.
  2. Дискомфорт от плавающего дизеринга совершенно пропал, даже в полноэкранном режиме.
  3. Сохраняется пикселизированный стиль игры

Можно полностью избавиться от недостатка 3, снова ограничив выходные данные 1-битными значениями с помощью простого порога в 50%. Результат по-прежнему лучше, чем без суперсемплинга (ниже представлены три примера для сравнения).

Сравнение трёх подходов

В игре с палитрой по умолчанию

Подводим итог


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

Наложение в экранном пространстве со смещением работает лучше всего при масштабе 1x, а сферическое наложение — при 2x. Вся сцена сейчас рендерится в разрешении 800x450 (поднял разрешение с 640x360), что повышает разборчивость, при этом не требуется жертвовать стилем low-res. В готовой игре будет два режима отображения:

ЦИФРОВОЙ — дизеринг в пространстве экрана со смещением, 1-битный вывод.

АНАЛОГОВЫЙ — полноэкранный наложенный на сферу дизеринг, сглаженный вывод.

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


  1. perfect_genius
    02.12.2017 12:02

    Мне и оригинал вполне себе.

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


    1. Atterratio
      03.12.2017 11:00

      Я сначала не понял какую он проблему решал. Но по конечному результату понял. Есть такое ание «Граф Монте-Кристо» и там похожая ситуация. Большинство моих знакомых его хвалят, а я дольше 5 минут просто не могу смотреть у меня начинают глаза отваливаться. В чём же дело? А дело в том что часть движущихся персонажей представлено статическими задниками, т.е. персонаж перемещается, и допустим на поделадке его плаща так же перемещается узор. С одной стороны это очень интересный эффект, с другой для некоторых людей он является вырвиглазным. В изначальном варианте дизеринг очень похож на этот эффект, только если в аниме это фича, то тут это баг.


  1. GeMir
    02.12.2017 12:32

    Раз уж нет аналога на русском, почему бы не использовать dithering не пытаясь его транслитерировать?


    1. QDeathNick
      02.12.2017 12:39

      Тогда уж и ???????? пишите и translitteration.


      1. GeMir
        02.12.2017 14:55

        Аналог, транслитерация. Разница заметна?

        О ужас, оно действительно существует.


        1. QDeathNick
          02.12.2017 15:01

          Да если бы его и не было, стоило бы вписать.


  1. lorc
    02.12.2017 13:03

    Если игрок не движется, а только вращает камерой, то можно было бы просто отрендерить один раз сферическую панораму, а потом просто двигать окно по ней. Или я что-то упустил?


  1. hdfan2
    02.12.2017 14:46

    По-моему, дизеринг и близко не подходит для имитации иллюстраций из старых книг. Если я правильно понял, имелось в виду нечто вроде:
    image
    Там совершенно другой тип искажения. С дизерингом ничего общего не имеет.


    1. KoCMoHaBT61
      02.12.2017 14:52

      Это гравюра. Её автоматом пока делать никто не умеет.
      Есть только полуручной инструмент Strokes Maker, который плотность штриха уменьшает/увеличивает относительно яркости подложки. И то большое достижение.


    1. VaalKIA
      02.12.2017 16:56
      +1

      Не соглашусь, вообще, то о чём говориться в статье, больше походит на элементарную пастеризацию, то есть уменьшение цветов, при этом её можно сдлеать чанками и вместо оттенков применять шаблоны разреженных точек (нечто вроде jpeg), при этом разрешение катастрофически падает. А под дизерингом всегда понимался алгоритм, переноса ошибки (в процессе пастеризации) на соседние пиксели. Так вот, переносить ошибку можно только на чётные/нечётные строки, что даст эффект полосатости, как черезстрочная развёртка телевизора. Вроде бы, этот эффект можно было наблюдать в формате видео под DOS smk с маленькой палитрой, который позже стал bik но уже полноцветный. А автору, я думаю, подошла бы элементарная заливка поверхностей по патерну, без учёта глубины, о чём он, в общем-то упоминает:

      Sentinel ZX Spectrum


      1. mwizard
        03.12.2017 04:49

        Постеризация, от слова "постер", т.е. плакат. Пастеризация — это процесс обеззараживания пищевых продуктов, названный в честь Луи Пастера.


    1. VaalKIA
      02.12.2017 17:25

      Примеры в интернете найти тяжело, но выглядело это вроде такого:

      неудачный пример, просто что бы было понятно как это выглядело
      типа черезстрочный дизеринг
      типа черезстрочный дизеринг

      чанки
      чанки


  1. amarao
    02.12.2017 19:08

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

    Правила рисования:
    1. Штрих по форме. Внезапно, 3D модели знают свою форму, так что мы можем для любого ракурса реконструировать штрих по форме.
    2. Линия должна быть живой — её толщина должна показывать «энергичность» движения в данном месте. «Движение» в рисовании — направление изгиба основной оси объекта, игнорируя мелкие детали. В первом приближении — изгиб центра симметрии. Чем сильнее изгиб, тем больше движение. Энергичность движения — его значимость для зрителя (художника). Машинно определить это сложно, но довольно просто разметить на 3D-модели.
    3. Штрихи могут менять яркость (нажим), толщину, расстояние между ними, количество слоёв (в контексте гравюры — не больше двух).
    4. Если у художника mad skill, то он может использовать штрихи для передачи характерных элементов текстуры (волос, ткани, волокон дерева).
    5. При переломе формы лучше менять направление штриха. Перелом формы — это место, где резко уменьшается радиус кривизны. Вычисляется на 3д-модели в пол-пинка.

    Это всё вполне в рамках машины. Даже если пренебречь «энергичностью» движения, всё остальное вычисляется. Неужели никто не пробовал?


    1. staticlab
      02.12.2017 21:09

      А по правилу 2 можете привести наглядные примеры? А то что-то не воспринимается.


      1. amarao
        02.12.2017 22:12

        Я быстро набросал с вакомом (я им хуже владею, чем карандашом). Слева — штрих без формы, в центре — по форме, справа — рисунок по форме.


        (https://snag.gy/wMZpXL.jpg)

        Показать вопросы с движением и т.д. будет много сложнее, т.к. это уже потребует настоящего рисунка.


        1. staticlab
          02.12.2017 23:47

          Эх, жаль. Про штрих по форме я и так сам прекрасно понял. А вот про движение как раз хотел пояснений...


          1. amarao
            02.12.2017 23:57

            А, движение — это сложно.

            Олег хорошо объясняет. Это видео и остальные смежные:

            www.youtube.com/watch?v=BRW9rUTnSa8


    1. KoCMoHaBT61
      02.12.2017 22:11

      На эту тему есть несколько диссертаций, но результаты очень убогие.


      1. amarao
        02.12.2017 22:14

        то есть вы хотите сказать, что если сделать что-то такое — это будет новое и неизведанное, а не изобретение велосипеда?


        1. KoCMoHaBT61
          02.12.2017 22:51

          Будет новое и неизведанное. Пока никто ничего толкового не сделал по этой теме.
          Самое первое приближение из коммерческих 3D рендеров это Modo NPR Kit.


        1. khim
          03.12.2017 05:11

          Скорее всего будет и то и другое. То есть с одной стороны вы сможете опубликоваться в престижном журнале, с другой стороны — через NNN лет обнаржится что кто-то эту проблему уже давно решил в какой-нибудь трешовой игре. Как в XKCD.


          1. KoCMoHaBT61
            03.12.2017 08:30

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

            А с анимацией всё будет значительно хуже — будет конкретное мельтешение штриховки, мельтешение дизеринга и мельтешение вообще всего вокруг.


          1. amarao
            03.12.2017 15:46

            Внезапно: www.youtube.com/watch?v=g6Ta35AiE1I 50% того, про что я говорил.


      1. DrZlodberg
        03.12.2017 15:30

        1. DrZlodberg
          03.12.2017 15:36

          Ещё вот такую штуку нашел


        1. KoCMoHaBT61
          03.12.2017 15:51

          Хм… Этой не видел… Хотя, вроде 2001 год, а я бросил этим заниматься в районе 2004.
          А вторая — забавная штукенция.


    1. AngReload
      03.12.2017 08:23

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


    1. DrZlodberg
      03.12.2017 15:27

      Почему не пробовали? 2001 год вроде бы.
      Ну и ещё куча примеров по запросам типа «real-time hatching shader»


      1. amarao
        03.12.2017 15:45

        Божественно. Это как раз то, про что я говорил. Дальше вопрос только файнтюнинга, а принцип — именно он.


  1. vitaliy91
    02.12.2017 19:28

    Хотелось бы попробовать поиграть например в третьего ведьмака с таким эффектом как на образце А


  1. shaman4d
    02.12.2017 23:25

    Еще бы кода автор занес бы…


    1. Bookvarenko
      04.12.2017 08:24

      Там, по ссылке на оригинал статьи, можно найти и остальные статьи о напилинге игры. Из них следует что одного кода было недостаточно для достижения этого обалденного эффекта. Там ещё изрядно дизайна уровней — светильники слои сцены и всё такое. Впрочем можно код из демки наковырять. Оно же C# и Unity. Ну, или автору написать, ежели языками иноземными владеешь.


  1. maniacscientist
    03.12.2017 00:11

    Мнда… Движущийся паттерн убивает всю идею дизеринга как ретроизврата. При статичном паттерне мельтешение пикселей происходит потому, что синий шум — это хлам. Оттенок изменился незначительно, а паттерн — до мерцания. Целлшейдинг врежьте в середину, чтоли — сразу поприятнее станет.


  1. xFFFF
    03.12.2017 00:36

    напомнило игры времен ZX Spectrum


  1. Bookvarenko
    03.12.2017 11:02

    Этой статье не помешали бы ссылки на собственно проект или ютубчик.
    Например https://youtu.be/ElrXAHogqBM
    Впрочем, как и комментаторам навык беглого гугления по картинке хотя-бы.
    Игра шикарная получилась — 3d c эффектом книжной иллюстрации. Спасибо переводчику!


  1. NikitaBogdanov
    03.12.2017 11:02

    Возможно проблема в том что человеческий глаз резко реагирует на искажение горизонтальных линий между кадрами (прямая линия при повороте камеры становиться ступенчатой), если уменьшить ступенчатость при смене кадров возможно станет лучше. Гравюры делались на основе оттисков, в которых иглой соскребалось наиболее тёмная часть, так что реализовать алгоритм тоже можно, сложность только в том, что каждый художник использовал свой стиль.


  1. 1eqinfinity
    05.12.2017 10:56

    Мне оригинал все-таки больше нравится, но учитывая цели, которые разработчик ставил, результат конечно отличный.


  1. aureliano_b
    06.12.2017 07:52

    Эстетически, первый вариант выглядит лучше, на мой взгляд. Статичная, по отношению к зрителю, «сетка» дизеринга — это, на мой взгляд, то, что ожидает сознание от такого рода картинки. В этом смысле, первый вариант выигрышнее, как мне видится.