Под капотом: объяснение SSAO
Техника имеет свои ограничения и причуды. В последние годы она использовалась в нескольких играх ААА, и даже если она не идеальна, она помогает системе человеческого восприятия лучше понимать сцену, и мы надеемся, что ее добавление в набор технологий наших симов грузовиков будет полезным. Без сомнения, мы хотим ввести дополнительные способы теневого вычисления, которые улучшат или даже вытеснят его.
Мы находимся под постоянным давлением, чтобы улучшить внешний вид нашей игры с помощью вокальной базы наших фанатов. В то же время всегда есть желание заставить игры работать быстрее. Вдобавок к этим иногда конкурирующим запросам, исходящим от базы игроков, наш собственный художественный отдел всегда стремится заполучить новые графические игрушки, чтобы сделать нашу игру богаче и лучше. Всякий раз, когда мы вводим в игру новую графическую функцию, мы стараемся сделать это так, чтобы это не повлияло на производительность игроков со старыми компьютерами, мы не хотим делать игру несовместимой с нашими существующими клиентами. Вот почему есть возможность полностью отключить SSAO и несколько настроек его качества / производительности.
Работа наших программистов над новыми технологиями SSAO / HBAO также потребовала изменений в нашем конвейере искусства и создания произведений искусства. Все 3D-модели в наших играх должны были быть повторно рассмотрены отделом по художественным работам, и любые случаи, когда любые искусственные тени и затемнения уже применялись к модели художником, были изменены. Для некоторых более сложных игровых моделей это было упрощением, которое фактически уменьшило количество треугольников в них, чтобы повысить производительность рендеринга. В какой-то степени мы обменяли часть предполагаемых будущих ручных усилий, которые потребовались бы для создания отдельных красивых 3D-моделей для алгоритмического прохода рендеринга, который объединяет теневое представление для всей сцены, помогая «укоренять» такие объекты, как здания, фонарные столбы и растительность на местности. Что такое SSAO и как это работает?
Поэтому вместо того, чтобы запекать статическую информацию (которая также занимала бы много времени и места для хранения, учитывая масштаб нашей карты мира), мы хотим вычислять ее на лету, во время выполнения. Таким образом, мы можем рассчитать его также для взаимодействия с транспортными средствами, открытия мостов, анимированных объектов и так далее. Хотя есть одна загвоздка. Для такого вычислительного подхода у нас есть только данные, которые видны на экране (вспомним «экранное пространство»), поэтому, как только какая-то часть игрового мира выходит из видимой рамки, ее нельзя использовать для оценки окклюзии. Это ограничение создает различные артефакты, такие как исчезновение окклюзии на стене, первоначально вызванное объектом, который только что оказался за краем экрана и, таким образом, стал невидимым не только для вас, но и для алгоритма, поэтому он перестал вносить вклад в вычисление окклюзии.
Мы упоминали, что методика нашего выбора основана на горизонте. Это означает, что мы не исследуем окружающую среду, снимая лучи в трехмерном мире, вместо этого мы анализируем полусферу выше / вокруг каждого пикселя, чтобы увидеть, как далеко он открывается, пока не заблокирован, сколько света пропускает окружающая геометрия используя z-буфер в качестве нашего прокси. Полушарие фактически аппроксимируется несколькими прогонами вдоль линии, повернутой вокруг данного пикселя. Если мы сможем полностью следовать вдоль этого полушария, то нет преграды. Если алгоритм выбирает значение в z-буфере, которое блокирует входящий свет, он определяет уровень окклюзии. Алгоритм оптимизирован для производительности, но его ограничение заключается в том, что как только он попадает во что-либо, даже, возможно, в небольшой объект, он прекращает дальнейшее исследование. Это может вызвать «чрезмерную окклюзию» проблема и может быть замечена как визуальный артефакт, когда какой-то относительно тонкий объект, такой как столб дорожного знака, вызывает сильную окклюзию на соседней стене. Вы можете попытаться обнаружить такие маленькие объекты и пропустить их, что, в свою очередь, может привести к «недостаточной окклюзии» на тонких уступах. Мы выбрали первое.
Так что, как видите, идея не такая уж сложная, для опытного программиста в любом случае;), но здесь требуется много вычислений, что создает некоторую нагрузку на 3D-ускоритель. Итак, мы создали несколько профилей производительности, каждый из которых использует сочетание методов оптимизации:
Мы надеемся, что вся эта информация была интересной и полезной для вас. Мы отправим вам виртуальную пятерку, если вы прочитаете эту статью до этого момента. Вы заслуживаете печенье и большую чашку горячего шоколада! Если вы все еще хотите получить более подробную информацию по этой теме, см., Например, эту ссылку [www.cse.chalmers.se].
Спасибо за ваше время и поддержку, и мы еще увидимся в некоторых следующих статьях из раздела «Под капотом», которые мы время от времени приносим для нашего #BestCommunityEver.
Также не забывайте, что летняя распродажа Steam скоро заканчивается! Посетите нашу страницу для разработчиков.
Графика в играх: окклюзия, сглаживание, фильтрация — Как и с чем её едят
Приветствую всех Стопгеймеров! Давайте начистоту, вы ведь тоже заходите в только купленную игру, но сперва кликаете на графические настройки? Кто ради чего, кому-то ради самоутверждения надо глянуть на ультра-автонастройку благодаря своему мощному «железу», а кто-то просто лезет туда ради интереса.Однако, задумывались ли вы, чем отличаются FXAA и TXAA, или 8х и 16х анизотропная фильтрация? Как-раз в этом блоге, группа Abuse Reviews сейчас вам расскажет и покажет, что же это за фильтрации такие, как они работают и с чем их едят. Поехали!
P.S.
прошлом блоге количество материала в ролике было урезано, здесь эта ошибка была учтена, очень старался для вас.Приятного просмотра)
Давайте начнём с самого-самого простого
Разрешение экрана
Мало кто не знает, что разрешение — это количество отображаемых пикселей по горизонтали и вертикали. От этой настройки также зависит качество картинки и то, как сильно будут выражены «лесенки» в переходах между разными плоскостями\поверхностями. Но почему же возникает этот графический артефакт? Дело в том, что все графические элементы в играх состоят из пикселей, но таких проблем с прямыми линиями не происходит, но стоит только чуть её наклонить, как появляются «лесенки». Возникает это из-за отсутствия плавного перехода между цветами, которое обеспечивает сглаживание, вот о нём мы сейчас и поговорим.
Сглаживание
Самое главное его предназначение — борьба с теми самыми «ступеньками», которые все так не любят. Сглаживание обеспечивает нам плавный переход между цветами, за счёт чего изображение получается куда комфортнее, устраняя «ступеньки». Да, картинка однозначно становится красивой, но всегда приходится чем-то жертвовать, а именно производительностью. За счёт появления новой задачи, процессору и видеокарте приходится рендерить(обрабатывать) все эти дополнительные оттенки, которое даёт нам сглаживание. Но, к счастью, существует много видов сглаживания, которые предоставляют нам разработчики в настройках. Их то мы сейчас и рассмотрим:
Этот вид сглаживания не слишком сильно нагружает процессор, потому что он обрабатывает лишь те части кадра, которые выглядели бы неровными, а выбирает он эти части независимо от того, где и как они располагаются. Это самый быстрый и менее затратный в плане ресурсов метод сглаживания. Отличие от прошлого метода сглаживания заключается в нескольких аспектах. В первую очередь, FXAA применяется к изображению в том разрешении, в котором вы играете, также размывает картинку сильнее, что выглядит совсем не лучше, чем MSAA, зато расходует на порядок меньше ресурсов, из-за чего этот вид сглаживания почти не вредит вашему FPS
Пожалуй, это лучший вид сглаживания, который сильно похож на MSAA, но с некоторыми дополнениями. Дело в том, что TXAA учитывает и берёт в расчёт предыдущие кадры и сглаживает последующие путём усреднения цветов.
Да, это не вид сглаживания, но избавляется от лесенок этот способ довольно неплохо, но при одном условии, которое свойственно не каждому пк. Ведь не у всех есть 2\4К мониторы, которые позволяют увеличить разрешение больше 1920х1080. За счёт уменьшения пикселей «лесенки» остаются, но становятся куда меньше, однако это влияет на производительность больше всего из перечисленных способов. Так что этот метод подойдёт только обладателям мониторов с очень высоким разрешением и мощным железом. Забавно слушать легенды о том, что если поставить 2к или 4к разрешение в игре на FullHD мониторе, то картинка станет лучше. Решил я это проверить на примере GTA V и что-то не увидел разницы до и после, ни в фреймрейте, ни качестве.
Проблем никогда не бывает мало. В этом случае нет никаких исключений, ведь кроме «ступенек» встречается такой артефакт, как разрыв картинки. Это происходит, когда ваши монитор и видеокарта пытаются работать синхронно, но по какой-то причине эти парни не могут этого сделать, причиной является частота кадров и частота обновления монитора. К примеру, вы находитесь в какой-то загруженной локации, а ваша видеокарта старается держать стабильную частоту, в то время как монитор обновляет изображение на одной и той же частоте. Если они не синхронизируются между собой, то как раз и появляется такой разрыв. И для решения этой проблемы предназначен следующий параметр:
Вертикальная синхронизация 
Этот параметр заставляет работать видеокарту на той же частоте, что и монитор, однако из-за этого возникают уже другие проблемы, к примеру, частота кадров может сильно падать из-за того что в игре появляется слишком много объектов, которые приходится обрабатывать. Но и для этой беды есть решение, которое называется — горизонтальная синхронизация. Принцип действия заключается в том, что модуль, встроенный в монитор заставляет экран обновляться сразу же при получении нового кадра, что способствует идеальному совпадению частот видеокарты и монитора. Благодаря всему этому, производительность компьютера не уменьшается, а монитор и видеокарта работают максимально слаженно.
На этом о проблемах картинки и артефактах — всё
Тесселяция
Тут стоит обратить внимание на контур головы 47-го
А вот она создана не для того чтобы исправлять косяки в картинке, а улучшать её и делать более насыщенной и реалистичной. Многие из нас знают, что 3д-объекты в играх состоят из полигонов (мелких частиц). Тесселяция подразумевает разбиение полигонов на более мелкие части, чтобы генерировать больше деталей у объекта. Это особенно удобно для выделения высоты и глубины объектов. Также она способствует созданию более закругленных объектов без острых форм и углов.
Окклюзия окружения (Ambient Occlusion)
Лично я занимаюсь созданием 3д-моделей в Cinema 4D и довольно хорошо знаком с этой фичей. 
Для начала стоит разобраться с освещением в играх. В них источником света является естественное освещение, которое является упрощённой версией глобального освещения, где расположение теней зависит от того, есть ли перед источником естественного освещения какое-либо препятствие, но это даёт нам более плоские тени в меньшем количестве, чем хотелось бы. Тут и наступает триумф окклюзии окружения, ведь она определяет расположение дополнительных теней с поммощью трассировки лучшей, а именно вычисляет, сколько солнечных лучшей блокируется рядом со стоящими объектами. То есть, если один объект загораживает другой, то поверхность второго объекта, разумеется, будет находиться в тени. Впадины, углубления и тому подобное начинает больше выделяться с помощью окклюзии.В огромном большинстве случаев этот параметр уже «вшит» в графические настройки, что не позволяет включать и выключать его. Но это всё окклюзия окружения в общем. Наверняка вы все сталкивались с такими параметрами освещения как SSAO,HBAO и HDAO?
Она взяла своё начало со времён первого Crysis, благодаря компании Crytek, по-сути оно заключается в вычислении глубины каждого пикселя и пытается вычислить количество преград от каждой из выбранных точек. Алгоритм SSAO призван упростить вычислительную сложность алгоритма Ambient occlusion и сделать его подходящим для работы на графических процессорах в режиме реального времени. Вместе с тем качество результирующего изображения у SSAO является худшим, чем в первоначальном Ambient occlusion, так как SSAO использует упрощённые методики рендеринга(обработки изображения).
Имеет тот же принцип работы, что и SSAO но несколько усовершенствованный. Просто вычисления глубины производятся с большим числом выборок, но приходится жертвовать производительностью.
Одно основывается на другом. Таким же образом как SSAO отличается от HBAO, HDAO от HBAO отличается точно тем же, ну и ещё эта окклюзия была представлена нам компанией AMD.
Ну а что по кинематографичности?
Глубина резкости
Неплохо так нагружает вашу систему, но и так же неплохо придаёт картинке кинематографичности, а всё благодаря фокусу на конкретных объектах, благодаря чему, остальные объекты размываются. Но это может привнести неудобства, как например при игре в PUBG, во время выглядывания из окна (ну вы знаете, когда упираешься лицом в стену как идиот и видишь всё что происходит за ней) иногда замыливается вид в окне, а фокус идёт на стену или оконную раму. Очень раздражает. Однако кинематографичность, опять же, дарит нам положительные впечатления об игре.
Ну и последнее о чём хотелось бы рассказать
Анизотропная фильтрация
А вот этот параметр уж точно видел каждый, но далеко не все понимают как это работает. Объясню быстро и просто. Во имя сохранения FPS разработчики используют нехитрый трюк с понижением качества текстур и моделей по мере отдаления от них. Зачастую мы можем наблюдать размытие текстуры пола вдали от себя, но если мы включим фильтрацию, то границы между различными уровнями детализации размываются. Плюс такой фильтрации в том, что вы можете со спокойной душой ставить значение 16х, ведь этот параметр почти не оказывает давления на процессор и видеокарту.
Ну а на этом всё. Если вам понравился этот блог и вы узнали что-то новое, обязательно жмите на плюс, а также интересно узнать, нравится ли вам качество видеоформата, если вы его глянули? Большое спасибо вам за внимание, всем удачных каток и стабильного FPS!
SSAO в играх, зачем нужен и что это
Что такое ssao в играх — этот вопрос появляется у игроков при просмотре графических настроек в современных проектах. Однако не все понимают, за что отвечает данный параметр и какой объем ресурсов расходует. Причем зачастую в настройках игры нет определения этой аббревиатуры.
За что отвечает
Ssao в играх расшифровывается как “ screen space ambient occlusion ” и отвечает за естественное распределение света на объектах и в пространстве.
При включенном параметре свет от источника освещения будет останавливаться на объекте. Предметы будут подсвечиваться естественным образом и отбрасывать тень. Это делает изображение более качественным и реалистичным. Ssao дает следующие преимущества:
Впервые ssao начало использоваться на движке Cry Engine 2 от создателей серии Crysis.
Стоит ли включать
Включать или нет данный параметр, игрок должен решить сам. Ssao является довольно затратным эффектом, и на слабом ПК игра может тормозить. Тогда лучше эту графическую опцию отключить.
Также ssao можно выключить, если игрок не обращает внимание на тени и детализацию при прохождении игр, зато ПК будет работать производительнее.
Откройте для себя мир, охваченный войной, где только вы надежда на спасение!
Learn OpenGL. Урок 5.10 – Screen Space Ambient Occlusion
Тема фонового освещения была затронута нами в уроке по основам освещения, но лишь вскользь. Напомню: фоновая составляющая освещения – суть постоянная величина, добавляемая во все расчеты освещения сцены для имитации процесса рассеяния света. В реальном же мире свет испытывает множество переотражений с разной степенью интенсивности, что приводит к столь же неравномерной засветке косвенно освещенных участков сцены. Очевидно, что засветка с постоянной интенсивностью не очень правдоподобна.
Одним из видов приближенного расчета затенения от непрямого освещения является алгоритм фонового затенения (ambient occlusion, AO), который имитирует ослабление непрямого освещения в окрестности углов, складок и прочих неровностях поверхностей. Такие элементы, в основном, значительно перекрываются соседствующей геометрией и потому оставляют меньше возможностей лучам света вырваться наружу, затемняя данные участки.
Ниже представлено сравнение рендера без и с использованием алгоритма AO. Обратите внимание на то, как падает интенсивность фонового освещения в окрестности углов стен и прочих резких изломов поверхности:
Стоит отметить, что алгоритмы расчета AO являются довольно ресурсоемкими, поскольку требуют анализа окружающей геометрии. В наивной реализации можно было бы просто в каждой точке поверхности выпустить множество лучей и определить степень её затенения, но такой подход весьма быстро достигает допустимого для интерактивных приложений предела ресурсоемкости. К счастью, в 2007 году компания Crytek опубликовала работу с описанием собственного подхода в реализации алгоритма фонового затенения в экранном пространстве (Screen-Space Ambient Occlusion, SSAO), который использовался в релизной версии игры Crysis. Подход рассчитывал степень затенения в экранном пространстве, используя лишь текущий буфер глубины вместо реальных данных об окружающей геометрии. Такая оптимизация радикально ускорила алгоритм по сравнению с эталонной реализацией и при этом давала по большей части правдоподобные результаты, что сделало данный подход приближенного расчета фонового затенения стандартом де-факто в индустрии.
Принцип, на котором основан алгоритм довольно прост: для каждого фрагмента полноэкранного квада рассчитывается коэффициент затенения (occlusion factor) на основе значений глубины окружающих фрагментов. Вычисленный коэффициент затенение далее используется для уменьшения интенсивности фонового освещения (вплоть до полного исключения). Получение коэффициента требует сбора данных о глубине от множества выборок из сферической области, окружающей рассматриваемый фрагмент, и сравнения этих значений глубины с глубиной рассматриваемого фрагмента. Число выборок, имеющих глубину бОльшую, нежели текущий фрагмент непосредственно и определяют коэффициент затенения. Посмотрите на данную схему:
Здесь, каждая серая точка лежит внутри некоторого геометрического объекта, а потому осуществляет вклад в значение коэффициента затенения. Чем больше выборок окажутся внутри геометрии окружающих объектов, тем меньше будет остаточная интенсивность фонового затенения в этой области.
Очевидно, что качество и реалистичность эффекта прямо зависит от числа сделанных выборок. При малом числе выборок точность алгоритма падает и приводит к появлению артефакта бэндинга (banding) или «полошения» из-за резких переходов между областями с сильно отличающимися коэффициентами затенения. Большое же число выборок просто убивает производительность. Рандомизации ядра выборок позволяет при схожих по качеству результатах несколько снизить число требуемых выборок. Подразумевается переориентация поворотом на случайный угол набора векторов выборок. Однако внесение случайности тут же приносит новую проблему в виде заметного шумового узора, что требует использования фильтров размытия для сглаживания результата. Ниже приведен пример работы алгоритма (автор – John Chapman) и его типичные проблемы: бэндинг и шумовой узор.
Как видно, заметное полошение из-за малого числа выборок неплохо убирается внесением рандомизации ориентации выборок.
Конкретная реализация SSAO от Crytek обладала узнаваемым визуальным стилем. Поскольку специалисты Crytek использовали сферическое ядро выборки это сказывалось даже на плоских поверхностях типа стен, делая их затененными – ведь половина объема ядра выборки оказывалась погруженной под геометрию. Ниже – скриншот со сценой из Crysis, изображенной в градациях серого на основе значения коэффициента затенения. Здесь хорошо виден эффект «серости»:
Для избегания такого влияния мы перейдем от сферического ядра выборки к полусфере, ориентированной вдоль нормали к поверхности:
Осуществляя выборку из такой полусферы ориентированной по нормали (normal-oriented hemisphere) нам не придется учитывать в расчете коэффициента затенения фрагменты, лежащие под поверхностью прилегающей поверхности. Такой подход убирает излишнее затенение в, в целом, дает более реалистичные результаты. Данном урок будет использовать подход с полусферой и немного доработанный код из блестящего урока по SSAO от John Chapman.
Буфер с исходными данными
Процесс вычисления коэффициента затенения в каждом фрагменте требует наличия данных об окружающей геометрии. Конкретно, нам потребуются следующие данные:
Поскольку SSAO является эффектом, реализующимся в экранном пространстве, то непосредственный расчет возможно выполнить отрендерив полноэкранный квад. Но тогда у нас не будет данных о геометрии сцены. Чтобы обойти такое ограничение, мы осуществим рендер всей необходимой информации в текстуры, которые позже будут использованы в шейдере SSAO для доступа к геометрической и прочей информации о сцене. Если вы внимательно следовали данным урокам, то уже должны узнать в описанном подходе облик алгоритма отложенного затенения. Во многом поэтому эффект SSAO как родной встает в рендер с отложенным затенением – ведь текстуры, хранящие координаты и нормали, уже доступны в G-буфере.
В данном уроке эффект реализуется поверх несколько упрощенной версии кода из урока об отложенном освещении. Если вы еще не ознакомились с принципами отложенного освещения – настоятельно советую обратиться к этому уроку.
Поскольку доступ к пофрагментной информации о координатах и нормалях уже должен быть доступен за счет G-буфера, то фрагментный шейдер стадии обработки геометрии достаточно прост:
Поскольку алгоритм SSAO является эффектом в экранном пространстве, а коэффициент затенения вычисляется на основе видимой области сцены, то есть смысл вести расчеты в видовом пространстве. В данном случае переменная FragPos, полученная из вершинного шейдера, хранит положение именно в видовом пространстве. Стоит удостовериться, что данные о координатах и нормалях хранятся в G-буфере в видовом пространстве, поскольку все дальнейшие расчеты будут осуществляться в нем же.
Существует возможность восстановления вектора положения на основе лишь известной глубины фрагмента и некоторого количества математической магии, что описано, например, у Matt Pettineo в блоге. Это, конечно, требующий бОльших затрат на расчеты способ, однако он избавляет от необходимости хранить данные о положении в G-буфере, что занимает уйму видеопамяти. Однако, ради простоты кода примера, мы оставим этот подход для личного изучения.
Текстура буфер цвета gPosition сконфигурирована следующим образом:
Данная текстура хранит координаты фрагментов и может быть использована для получения данных о глубине для каждой точки из ядра выборок. Отмечу, что текстура использует формат данных с плавающей точкой – это позволит координатам фрагментов не быть приведенными к интервалу [0., 1.]. Также обратите внимание на режим повтора – установлен GL_CLAMP_TO_EDGE. Это необходимо для устранения возможности не нарочно осуществить оверсэмплинг в экранном пространстве. Выход за пределы основного интервала текстурных координат даст нам некорректные данные о положении и глубине.
Далее займемся формированием полусферического ядра выборок и созданием метода случайной его ориентации.
Создание ориентированной по нормали полусферы
Итак, стоит задача создать набор точек выборки, расположенных внутри полусферы, сориентированной вдоль нормали к поверхности. Поскольку создание ядра выборки для всех возможных направлений нормали вычислительно недостижимо, то мы используем переход в касательное пространство, где нормаль всегда представляется как вектор в направлении положительной полуоси Z.
Предполагая радиус полусферы единичным процесс формирования ядра выборки из 64 точек выглядит так:
Здесь мы случайным образом выбираем координаты x и y в интервале [-1., 1.], а координату z – в интервале [0., 1.] (будь интервал таким же, как для x и y, мы бы получили сферическое ядро выборки). Результирующие вектора выборок окажутся ограничены полусферы, поскольку ядро выборки в конечном итоге будет сориентировано вдоль нормали к поверхности.
В данный момент все точки выборки случайно распределены внутри ядра, но в угоду качеству эффекта выборкам, лежащим ближе к началу координат ядра, стоило бы вносить больший вклад в расчете коэффициента затенения. Это можно реализовать за счет изменения распределения сформированных точек выборки, увеличив их плотность около начала координат. Такую задачу легко выполнить с помощью функции интерполяции с ускорением:
Функция lerp() определена как:
Такой трюк дает нам модифицированное распределение, где большинство точек выборки лежат вблизи начала координат ядра.
Каждый из полученных векторов выборки будет использован для смещения координаты фрагмента в видовом пространстве для получения данных об окружающей геометрии. Для получения приличных результатов при работе в видовом пространстве может потребоваться внушительное количество отсчетов, что неизбежно ударит по производительности. Однако, внесение псевдослучайного шума или поворота векторов выборок в каждом обрабатываемом фрагменте, позволит значительно снизить требуемое число выборок при сравнимом качестве.
Случайный поворот ядра выборки
Итак, внесение случайности в распределение точек ядра выборки позволяет значительно снизить требование к числу этих точек для получения достойного качества эффекта. Можно было бы создать случайный вектор поворота для каждого фрагмента сцены, но это слишком затратно по памяти. Эффективней создать небольшую текстуру, содержащую набор случайных векторов поворота, а затем просто использовать её с установленным режимом повтора GL_REPEAT.
Создадим массив 4х4 и заполним случайными векторами поворота, сориентированными вдоль вектора нормали в касательном пространстве:
Поскольку ядро выровнено вдоль положительной полуоси Z в касательном пространстве, то компонент z оставляем равным нулю – это обеспечит поворот только вокруг оси Z.
Далее создадим текстуру размером также 4х4 и зальем туда наш массив векторов поворота. Обязательно используйте режим повтора GL_REPEAT для тайлинга текстуры:
Что ж, теперь у нас готовы все данные, необходимые для непосредственной реализации алгоритма SSAO!
Шейдер SSAO
Шейдер эффекта будет исполняться для каждого фрагмента полноэкранного квада, вычисляя коэффициент затенения в каждом из них. Поскольку результаты будут использованы в еще одной стадии рендера, создающей итоговое освещение, нам потребуется создание еще одного объекта фреймбуфера для хранения результата работы шейдера:
Поскольку результат работы алгоритма – единственное вещественное число в пределах [0., 1.], то для хранения будет достаточно создать текстуру с единственной доступной компонентой. Именно поэтому в качестве внутреннего формата для буфера цвета ставится GL_RED.
В целом процесс рендера стадии SSAO выглядит примерно следующим образом:
Шейдер shaderSSAO принимает нужные ему текстуры G-буфера как входные данные, а также шумовую текстуру и ядро выборки:
Обратите внимание на переменную noiseScale. Наша маленькая текстура с шумом должна быть затайлена по всей поверхности экрана, но поскольку текстурные координаты TexCoords заключены в пределах [0., 1.] этого не произойдет без нашего вмешательства. В этих целях мы вычисляем множитель для текстурных координат, который находится как отношение размера экрана к размеру шумовой текстуры:
Поскольку при создании шумовой текстуры texNoise мы установили режим повтора в GL_REPEAT, то теперь она будет повторяться множество раз на поверхности экрана. Имея на руках величины randomVec, fragPos и normal мы можем создать матрицу TBN трансформации из касательного пространства в видовое:
Используя процесс Грамма-Шмидта мы создаем ортогональный базис, случайно наклоненным в каждом фрагменте на основе случайного значения randomVec. Важный момент: поскольку в данном случае нам неважно, чтобы матрица TBN была точно сориентирована вдоль поверхности треугольника (как в случае с parallax mapping’ом, прим. пер.), то нам не нужны предрасчитанные данные о касательных и бикасательных.
Далее мы проходим по массиву ядра выборки, переводим каждый вектор выборки из касательного пространства в видовое и получаем его сумму с текущим положением фрагмента. Затем сравниваем величину глубины получившейся суммы со значением глубины, полученной выборкой из соответствующей текстуры G-буфера.
Пока звучит запутанно, разберем это по шагам:
Здесь kernelSize и radius являются переменными, контролирующими характеристики эффекта. В данном случае они равны 64 и 0.5 соответственно. На каждой итерации мы переводим вектор ядра выборки в видовое пространство. Далее прибавляем к полученному значению смещения выборки в видовом пространстве значение положения фрагмента в видовом пространстве. При этом значение смещения умножается на переменную radius, которая управляет радиусом ядра выборки эффекта SSAO.
После этих шагов нам следует преобразовать полученный вектор sample в экранное пространство, для того, чтобы мы могли осуществить выборку из текстуры G-буфера, хранящей положения и глубины фрагментов, используя полученное спроецированное значение. Поскольку sample находится в видовом пространстве, нам потребуется матрица проекции projection:
После преобразования в клиповое пространство мы вручную осуществляем перспективное деление простым делением компонент xyz на w компоненту. Полученный вектор в нормализованных координатах устройства (NDC) переводится в интервал значений [0., 1.] дабы его можно было использовать как текстурные координаты:
Используем компоненты xy вектора sample для выборки из текстуры положений G-буфера. Получим значение глубины (z компоненты), соответствующее вектору выборки при взгляде с позиции наблюдателя (это первый не заслоненный видимый фрагмент). Если при этом полученная глубина выборки оказывается больше, чем сохраненная глубина, то мы увеличиваем коэффициент затенения:
Обратите внимание на смещение bias, которое добавляется к исходной глубине фрагмента (в примере установлена в 0.025). Это смещение не всегда является обязательным, но наличие переменной позволяет управлять тем, как выглядит эффект SSAO, а также, в определенных ситуациях, убирает проблемы с рябью в затененных областях.
Но и это еще не все, поскольку такая реализация приводит к заметным артефактам. Он проявляется в тех случаях, когда рассматривается фрагмент, лежащий вблизи края некоторой поверхности. В таких ситуациях алгоритм при сравнении глубин неизбежно захватит и глубины поверхностей, которые могут лежать очень далеко позади рассматриваемой. В этих местах алгоритм ошибочно сильно увеличит степень затенения, что создаст заметные темные ореолы по краям объектов. Лечится артефакт введением дополнительной проверки на расстояние (пример за авторством John Chapman):
Проверка будет ограничивать вклад в коэффициент затенения только для значений глубины, лежащих в пределах радиуса выборки:
Также мы применяем функцию GLSL smoothstep(), которая реализует плавную интерполяцию третьего параметра в пределах между первым и вторым. При этом возвращая 0, если третий параметр меньше или равен первому, либо 1, если третий параметр больше либо равен второму. Если разница глубин оказывается в пределах radius, то её величина будет плавно сглажена в интервале [0., 1.] в соответствии с данной кривой:
Если бы мы использовали четкие границы в условиях проверки глубины, то это добавило бы артефакты в виде резких границ в тех местах, где значения разницы глубин оказываются вне пределов radius.
Последним штрихом мы нормализуем величину коэффициента затенения, используя размер ядра выборки и записываем результат. Также мы инвертируем итоговое значение, вычитая его из единицы, дабы можно было использовать конечное значение напрямую для модуляции фоновой составляющей освещения без дополнительных действий:
Для сцены с лежащим знакомым нам нанокостюмом, выполнение SSAO шейдера дает следующую текстуру:
Как видно, эффект фонового затенения создает неплохую иллюзию глубины. Одно только выходное изображение шейдера уже позволяет различить детали костюма и убедиться, что он действительно лежит на полу, а не левитирует на некотором расстоянии от него.
И все же эффект далек от идеала, поскольку шумовой узор, привнесенный текстурой случайных векторов поворота, легко заметен. Для сглаживания результата расчета SSAO мы применим фильтр размытия.
Размытие фонового затенения
После построения результата SSAO и перед финальным сведением освещения необходимо провести размытие текстуры, хранящей данные о коэффициенте затенения. Для этого мы заведем еще один фреймбуфер:
Тайлинг шумовой текстуры в экранном пространстве обеспечивает вполне определенные характеристики случайности, которые можно использовать в свою пользу при создании фильтра размытия:
Итого у нас на руках есть текстура с данными фонового затенения для каждого фрагмента на экране – все готово для стадии финального сведения изображения!
Применение фонового затенения
Этап применения коэффициента затенения в итоговом расчете освещения на удивление прост: для каждого фрагмента достаточно просто умножить значение фоновой составляющей источника света на коэффициент затенения из подготовленной текстуры. Можно взять готовый шейдер с моделью Блинна-Фонга из урока по отложенному затенению и немного его подправить:
Серьезных изменений здесь всего два: переход к расчетам в видовом пространстве и умножение компоненты фонового освещения на значение AmbientOcclusion. Пример сцены с единственным синим точечным источником света:
Полный исходный код лежит здесь.
Проявление эффекта SSAO сильно зависит от параметров типа kernelSize, radius и bias, зачастую их тонкая подстройка – само собой разумеющееся занятие художника при проработке той или иной локации/сцены. Нет каких-то «лучших» и универсальных сочетаний параметров: для одних сцен хорош малый радиус ядра выборки, другие выигрывают от увеличенного радиуса и числа выборок. В примере используется 64 точки выборки, что, откровенно говоря, избыточно, но вы всегда можете отредактировать код и посмотреть, что получится при меньшем числе выборок.
Кроме перечисленных юниформов, которые отвечают за настройку эффекта, существует возможность явно регулировать выраженность эффекта фонового затенения. Для этого достаточно возвести коэффициент в степень, контролируемую еще одним юниформом:
Советую потратить некоторое время на игру с настройками, поскольку это даст лучшее понимание о характере изменений в итоговой картинке.
Подводя итог, стоит сказать, что хотя визуальный эффект от применения SSAO и достаточно слабозаметный, но в сценах с хорошо расставленным освещением он неоспоримо добавляет заметную толику реализма. Иметь такой инструмент в своем арсенале безусловно ценно.


















