Капитан очевидность

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

Важность референсов

Представьте, что вы работаете над чем-то (новая фича? крупная оптимизация, которая «немного» жертвует качеством? новый пайпалайн?) в течение нескольких дней. У вас есть некоторые многообещающие результаты. Арт-директору нравится. Осталась только пара мелких изменений, и вы представите всем свой результат. Вы смотрите на свои результаты ежедневно (ну, на самом деле, вы смотрите на них всё время), но вдруг вы решили попросить другого художника или программиста помочь вам оценить вашу работу, и вы начинаете обсуждать/спорить/сомневаться, «должно ли оно выглядеть именно так»?

Что ж, это прекрасный вопрос. Не слишком ли яркое изображение? Или может непрямое освещение недостаточно сильное? Правдоподобна ли ваша аппроксимация площадных источников света и сохраняет ли она энергию? А как насчет математики: не забыл ли я (печально известное) деление на пи? Не был ли мой монитор декалиброван моим котом, или, может, арт-директор посмотрел ранее под другим углом?

Честно говоря, я перестаю замечать, что выглядит корректно, а что — нет, после пары итераций разработки  и возвращаюсь к частям концепт-арта, отзывам арт-директора или фотографиям.

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

НЕ автоматизированное тестирование

Просто для ясности. Я не говорю об автоматизированном тестировании. Это важный вопрос. Многие компании используют его, и это действительно необходимо, но мой пост совсем не про это. Относительно просто не сломать то, что уже сделано верно (или уже принято, как сделанное верно), но весьма трудно сделать что-то «правильно», когда вы не знаете, как должен выглядеть финальный результат.

Референсная версия

Итак, что подразумевается под этой референсной версией? Я имею в виду, что вам нужно «brute-force» решение для задачи, над которой вы работаете. Наивная, без каких-либо аппроксимаций/оптимизаций, требующая, возможно, даже нескольких секунд вместо целевых 16/33 миллисекунд.

Годами люди, работавшие над 3D графикой в играх, не использовали референсные версии, и для этого есть прекрасное объяснение. Игры были далеки от CGI-рендеринга, так что не было никаких причин для сравнения результатов. Хорошая графика в играх была результатом хитроумных хаков, трюков, оптимизаций и интересных арт-решений. Таким образом, многие программисты и художники «старой школы» всё ещё имеют привычку просто проверить, что нечто «выглядит норм», или довести это нечто до такого состояния. Хотя художественные решения всегда будут наиболее важной частью визуализации 3D, мы изучили всю мощь физически корректного шейдинга и начали использовать техники такие, как GI / AO / PBR / площадные источники света, и другие, и поэтому у нас нет пути назад, некоторые трюки должны быть переделаны в терминах физической/математической корректности. К счастью, мы можем сравнить их с эталонами.

Далее, я приведу несколько примеров, как использовать и реализовывать референсы для некоторых задач.

Площадные источники света

Вообще, именно тема площадных источников света была одной из причин для написания этого поста. Мы видели множество статей и презентаций по этому поводу, различные обсуждения сохранения энергии и формы финального отражения света, но многие ли из них имеют сравнение с эталоном? Я не говорю только о сравнении входящей энергии в Mathematica для конкретного вида источника света/BRDF. Это, конечно, важно, но я уверен, что сравнение результатов в реальном времени в вашем движке более полезно.

Только подумайте, насколько просто реализовать цикл хотя бы размера 64x64, который интегрирует площадный источник света, суммируя точечные источники. Он будет работать 10 секунд на вашей GTX Titan, но вы сможете незамедлительно сравнить вашу аппроксимацию с эталоном. Вы увидите краевые случаи, где есть расхождение с ожидаемым результатом, и сможете оценить ваше решение с помощью вашей системы источников света.

Вы даже можете провести вычисления на CPU и создать сетку из 64x64 источников света, поддерживающих тени, и проверить ошибку в (полу-) тенях у этих площадных источников света. Насколько это полезно для проверки ваших PCSS-полутеней?

(Анти) алиасинг

Это весьма важная часть, поскольку алиасинг сигнала — одна из базовых проблем компьютерной графики реального времени. Недавно было сделано много докладов об алиасинге геометрии, текстурном алиасинге, асиасинге шейдинга, проблемах с альфа-тестовой геометрией и т.д. Многие из презентаций и статей, к счастью, представляли сравнения с референсной версией, но делали ли вы сравнение в своём движке? Вы уверены, что всё правильно поняли?

Возможно, у вас есть какой-то баг в MSAA, может, ваш AA на основе изображения весьма плох в движении, или, может, ваши веса в темпоральном AA совсем неверные? Возможно, ваша реализация AA для диффуза или спекуляра не точна, или в ней есть опечатка? Может быть, вертексный или пиксельный шейдер, сделанный художником, добавляет некоторый «процедурный» алиасинг? А может, у вас есть алиасинг шейдинга, связанный с нормалями геометрии (распространённые техники, такие как Toksvig, работают только в пространстве карты нормалей)? Возможно, ваш алгоритм карты теней добавляет некоторое мерцание/темпоральную нестабильность?

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

Очевидно, что в данном случае поможет создание правильного референсного изображения в большем разрешении и его ресемплинг. Я бы порекомендовал здесь два возможных решения:

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

  • In-place суперсемплинг. Олдскульная техника, которая базируется либо на сшивании изображений/тайлов (тайловые скриншоты в Unreal Engine), либо на субпиксельных сдвигах (в игре Ведьмак 2 для скриншотов используется этот суперсемплинг, и кадр отрисовывается 256 раз!)

У меня довольно большой опыт со вторым методом (поскольку он хорошо работает с эффектами пост-процесса, которые требуют размытия, например, bloom), чтобы сделать всё правильно, надо не забывать маленький и простой трюк: добавьте отрицательное смещение MIP-уровней (~log2 от уровня суперсемплинга по одной оси) и смещение для LOD'ов геометрии. Факт, который я нахожу забавным: мы сделали этот вариант рендеринга в Ведьмак 2 в качестве настройки графики для игроков в будущем (мы по-настоящему гордились финальной графикой в игре и думали, что будет замечательно, если она будет выглядеть прекрасно и через 10 лет, верно?), но большая часть ПК энтузиастов возненавидели нас за это! Они выставляли все опции на максимум, чтобы протестировать свои ПК за 3-5k$ (и оправдать расходы), но эта опция «внезапно» (даже не смотря на предупреждение в меню) уменьшала производительность GPU, например, в 4 раза.

Глобальное освещение

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

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

Дела обстоят проще для тех, кто использует Maya/другой 3D редактор в качестве игрового движка, но, вероятно, это проблематично для всех остальных. Тем не менее, вы можете работать над этим шаг за шагом — сделать простой BVH/kd-Tree и вычислять AO с помощью трассировки лучей, это должно быть довольно просто для написания (максимум пара дней). Это поможет вам оценить ваш SSAO и алгоритмы AO большего масштаба. В будущем вы можете расширить своё решение для вычисления GI с несколькими отражениями света. С PBR и играми нового поколения, я думаю, это будет решающим фактором с позиции реального ускорения вашего отдела R&D и финального продакшена, поскольку художники, привыкшие работать с CGI/фильмами, будут получать те же правильные результаты прямо в игровом движке.

Функции ДФОС

Прекрасный пример представил Brian Karis на последнем курсе Physically Based Shading на конференции Siggraph 2013 по теме «ДФОС окружения». Интегрируя по полной полусфере значения ДФОС для входящего излучения от вашей карты окружения, вы можете увидеть, как оно действительно должно выглядеть. Я рекомендую делать это без какой-либо выборки по значимости для начала, потому что вы можете допустить ошибку или ввести погрешность/смещение таким методом!

Имея такую референсную версию, намного проще проверить вашу аппроксимацию: вы немедленно увидите краевые случаи и потенциальные отклонения данной аппроксимации. С таким инструментом в вашем движке вы проверите, правильно ли выбирается MIP-уровень, или что вы забыли умножить/разделить на какой-то коэффициент. Вы увидите, как много вы теряете, игнорируя анизотропную долю или разделяя компоненты интеграла. Сделайте это, это не займет больше нескольких часов со всем необходимым тестированием!

Реализация/удобство использования

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

  • Удобство реализации

  • Удобство сравнения

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

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

Каждый случай отличается от других: возможно написание референсного интегратора ДФОС (двулучевая функция отражательной способности) займёт минуты, а референсы глобального освещения (скриншоты/смена режима в реальном времени) могут занять недели для завершения. По этой причине, я только могу дать вам совет подходить к этому разумно.

Ещё одна вещь для размышления — наличие в движке или редакторе поддержки/фреймворка, который делает использование сравнения различных пассов проще. Только взгляните на такое приложение для фотографий, как прекрасный Adobe Lightroom. Там есть как «слайдер», чтобы разделить изображения с разными режимами, так и возможность расположить сравниваемые изображения на разных мониторах.

Также всегда доступна кнопка «предпросмотр». Она может быть полезна в других местах: представьте, как наличие такой кнопки для освещения/эффектов пост-процесса сделает проще жизнь ваших художников по освещению! Один клик для сравнения с тем, что он сделал 10 минут назад — прекрасная помощь для ответа на классический вопрос «двигаюсь ли я в правильном направлении?». Наличие такого инструмента в вашем цикле разработки, вероятно, не то что нужно сделать немедленно; вам понадобится помощь программистов, занимающихся инструментами, но я думаю, что это окупится довольно быстро.

Итог

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