Эксперимент по разработке проекта от нуля до прибыли. 2.2.Распространенные ошибки рендеринга и где они обитают

Немного теории.

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

Продолжаем серию статей о разработке нашего экспериментального проекта.

Алиасинг - что это такое и почему это происходит

Алиасинг, или "Лесенки" появляются при растеризации 3D изображения в 2D картинку для вывода на экран. Каждый пиксель в картинке может быть только одного цвета, поэтому видеокарта (GPU) выбирает цвет пикселя, проецируя 3d фигуру на сетку. И, если фигура не проходит через центр пикселя на сетке - то он не окрашивается. Отсюда и появляются “лесенки” - это хорошо видно на примере растеризации треугольника.

растеризация
растеризация

Бороться с этим призвано сглаживание, в нашем случае - MSAA (Multisample anti-aliasing). С включенным MSAA, видеокарта (GPU) при растеризации определяет цвет пикселя, уже по нескольким точкам, а не только в центре, собирая средней цвет в конечно отображаемом пикселе, пропорциональный количеству покрытых точек.

Таким образом, для 2x MSAA проверяются две точки, чтобы увидеть, находится ли в них треугольник, а для 4x MSAA - 4 точки соответственно.

Эксперимент по разработке проекта от нуля до прибыли. 2.2.Распространенные ошибки рендеринга и где они обитают

Поскольку в “мобильном” VR, на который мы ориентируемся используется тайловый GPU, при растеризации изображение разделяется на множество прямоугольных блоков - тайлов (16х16 пикселей), и процесс растеризации выполняется уже для каждого из этих маленьких тайлов параллельно, MSAA сглаживания незначительно влияет на производительность и должно быть включено ВСЕГДА. Однако это, увы, не решение всех проблем.

Эксперимент по разработке проекта от нуля до прибыли. 2.2.Распространенные ошибки рендеринга и где они обитают

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

В интерьере нашего интерактивного музея будет писательский стол с лампами:

Эксперимент по разработке проекта от нуля до прибыли. 2.2.Распространенные ошибки рендеринга и где они обитают

Подставка этих ламп - отполированный металл, то есть на нем образуются блики. Проблема в том, что при игре в VR - изображение НИКОГДА не бывает статическим - подергивания от сокращений мышц шеи, дрожание головы и прочие биологический факторы создают микродвижения камеры, а значит, у блестящего предмета в месте его блика, из-за таких микродвижения и особенностей тайлового рендера, снова появится пресловутая “лесенка”. В дополнение к этому мобильный MSAA не сглаживает отражения вообще, что усугубляет ситуацию еще больше.

Бликующая тонкая подставка - при таком ракурсе на расстоянии уже 2-3 метров возникает алиасинг.
Бликующая тонкая подставка - при таком ракурсе на расстоянии уже 2-3 метров возникает алиасинг.

решаем этот вопрос следующими способами:

  • Стараемся вообще не использовать контрастных небольших предметов по отношению к фону.
  • В LODах лодируем не только сам меш, но и материал, убирая блики пока игрок не приблизится к предмету на достаточно близкое расстояние.
  • Убирать блики прямо в материале предмета
  • Использовать черную магию Unreal Engine - в настройках текстуры карты шероховатости (roughness) к ней можно добавить карту нормали и указать значение Composite Power. Тогда на всех кривых поверхностях, где будет супер гладкая поверхность и блики могут давать алиасинг на карте шероховатости будет добавлен белый цвет сглаживающий этот эффект.


Еще один пример - стол с красивой столешницей из отдельных досок:

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

Masked materials

На материалах с маской нужно примерять Alpha to coverage (включается в настройках материала). В таком случае по контуру добавляется несколько градиентных слоев (четыре при MSAAx4).

Раньше приходилось не использовать Masked материалы вообще, а обходится только прозрачными материалами и добавлять небольшой градиентный контур на текстуру маски самостоятельно. Теперь всё сделано за нас и сделано отлично!

Итак, алиасинг на окружающей игрока геометрии устранен переходим к алиасингу текстур и материалов.

Текстурный алиасинг, как и геометрический, происходит при растрировании в 2D-сетку пикселей. Если пиксели на текстуре не совпадают с 2D-сеткой, на которую мы растеризуем, выходное изображение после растеризации будет интерполировано примерно так:

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

Multum in Parvo

Бороться с этим беспорядком нам помогает Mip Mapping.

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

Именно это делает Mip Mapping - уменьшает разрешение текстуры при удалении её от игрока.

Цветная кодировка на гифке показывает, как изменяется разрешение текстуры при удалении. Каждый уровень mip уменьшает разрешение текстуры в 2 раза. То есть если белая текстура 256х256 пикселей, то красная 128х128, желтая 64х64 и т.д.

Результат уже гораздо лучше! Но всё же идеального совпадения размера текстуры с размером сетки растеризации не достигнуть – всегда будет небольшое различие. И при растеризации определить какой будет цвет конечного пикселя при смешивании нескольких пикселей текстуры помогает фильтрация.

Применяющиеся методы фильтрации следующие:

Расстояние, на котором срабатывает переключение на следующий mip уровень, то есть разрешение текстуры уменьшается в 2 раза, можно менять. Это может понадобится, если текстура слишком “мыльная” на близком расстоянии. Или слишком “четкая” на дальнем, что приводит к алиасингу.

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

Смена расстояния срабатывания mip levels называется Mip Bias и по умолчанию он равен 0.

Параметр меньше нуля заставит GPU выбрать текстуру с более высоким разрешением на большем расстоянии, а больше нуля приведет к обратному результату. По моему опыту, смещение в -0,7, хорошо помогает детализированным текстурам сохранять четкость на больших расстояниях, без появления алиасинга, но это только примерная цифра – параметр нужно подбирать индивидуально для каждого случая.

В Unreal engine выглядит это так:

Эксперимент по разработке проекта от нуля до прибыли. 2.2.Распространенные ошибки рендеринга и где они обитают

Интерфейсы:

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

Основная проблема в том, что при создании 3d виджетов меню, интерактивных подсказок и всякого такого, в мобильном рендере, что к ним не применяется mipmaping, не применяется сглаживания и даже пост процесс не применяется. Поскольку текстура формируется в реальном времени и перезаписывается каждый кадр - создавать ей mip уровни довольно затратное мероприятие. Выглядит всё это примерно вот так:

Происходит здесь ровно то же самое, что и при растеризации текстур.

Методы борьбы же следующие:

1. Подобрать расстояние, с которого игрок будет смотреть на виджет:

Если каждый пиксель виджета будет совпадать с сеткой растеризации, то изображения будет четким:

Эксперимент по разработке проекта от нуля до прибыли. 2.2.Распространенные ошибки рендеринга и где они обитают

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

2. Stereo Layers

Стереослои позволяют отправлять текстуру в VR композитор шлема, а не рендерить её в движке. Что даёт не только преимущество в производительности, но и композитор рендерит текстуру 1:1 - по сравнению с экраном на мобильном устройстве. То есть как раз все пиксели текстуры совпадают с сеткой растеризации! Идеально! Обязательно используйте стереослои.

Минусов же следующие - скудная документация.

При использовании ES3.1 рендера нельзя рассчитать глубину слоя - он будет всегда виден поверх всего остального, что есть на сцене. В Vulcan рендере глубину задать можно, но убирается вся прозрачность. Вообще вся.

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

С моей точки зрения стереослои - самый хороший способ.

3. Multisampling

Можно написать кастомный шейдер использующий деривативы (ddx, ddy), общая идея в том, что нужно создать блок 2x2 субпикселя, повторив в нем каждый пиксель оригинальной текстуры 4 раза создав таким образом кастомный мультисампллинг и упростив выборку для растеризации.

Эксперимент по разработке проекта от нуля до прибыли. 2.2.Распространенные ошибки рендеринга и где они обитают

Такой метод точно будет ухудшать производительность - учитываем при реализации. Результат же всё равно уступает стереослоям.

Give me the TL;DR

Если нет желания читать все буквы сверху, то вот выжимка:

  • Включить 4x MSAA, это обязательно!
  • На всех материалах с прозрачностью или маской используем Alpha To Coverage. В особых случая руками добавляем градиент по контуру.
  • Отключаем блики и отражения на расстоянии от игрока.

    Для всех текстур:
  • Включаем mipmapsВключаем trilinear filtering
  • Для текстур пола и стен включаем anisotropic filtering
  • На текстурах предметов повышенной четкости выставляем mipmap bias в значение -0.7
  • Используем стереослои для меню и прочих 3d виджетов.
  • Регулярно всё смотрим и тестируем непосредственно в VR на устройстве. Preview рендер в движке не даёт полного понимания.

Полезные ссылки:

Надеюсь такой поверхностный гайд поможет победить технические трудности при разработке. В следующий раз посмотрим, что уже непосредственно сделано, и начнем планировать маркетинг. Stay Tuned и успехов!

Оглавление:

Часть 0 - Поставили задачу разработать приложение с нуля до вывода прибыли.

Часть 1 - Концепт и Диздок. Создаем приложение для знакомства с творчеством Брайона Гайсина (Brion Gysin) и его Dreamachine в VR!

Часть 2.1 - Оптимизация.

Часть 2.2 - Алиасинг и рендеринг

Почта для всякого: artem@bulletheadgames.com

5858
10 комментариев

Захотелось вновь собрать себе дриммашину

1
Ответить

Супер) ты собирал по макету? пробовал использовать? Я еще планирую добавить разных интересных звуков для увеличения эффекта) напишу непосредственно про дрим машину и остальной интерактив в следующей заметке через неделю!

1
Ответить

Про стереослои применимо только к Oculus или в PCVR тоже есть такой интерфейс?

1
Ответить

Есть, и в нем можно задать проверку глубины, то есть корректно вписать в сцену! Прикреплю ссылку к документации. Но стоит отметить, что в PCVR к 3d виджету применяется сглаживание и выглядят они хорошо - гораздо лучше, чем в автономном, мобильном VR.

1
Ответить

1) У вас отличные статьи, пожалуйста продолжайте!
2) Насчет mipmaps - насколько знаю, их отключают в партиклах, поскольку частицы часто меняют размер, а живут недолго и могут создавать артефакты изображения. В частности, недавно бы кейс, когда при использовании radial uv появлялась полоска на текстуре.
3) Судя по тому, что вы используете Alpha to coverage в VR, жрет производительности он немного или терпимо, верно рассуждаю?

1
Ответить

Спасибо!
2) С частицами - целый отдельный, волшебный, мир VFX. Там очень многое будет зависеть от конкретных обстоятельств. Например у меня есть в сцене огонь в камине и искры - они тоже сделаны частицами и там mip maping нужен обязательно. В каких-то случаях может быть действительно, лучше отключать. С radial uv сам круговой градиент задавался текстурой? С моей точки зрения все градиенты (не только круговые) лучше задавать математикой, а не картинками. Вообще в подавляющем большинстве случаев, если маску или любой другой эффект можно задать вычислениями, а не текстурой - то это будет и лучше и дешевле в производительности. В unreal в этом плане всё отлично - материалы создаются в таком-же нодовом редакторе, как и блюпринты и всё очень наглядно. Для отладки есть специальные ноды, прикреплю видео, если интересно!
https://youtu.be/6SCgWC3jztY

3) Да, именно так. В 4.26 если правильно помню, он включён по умолчанию. Это немного удорожает материал, но не настолько, чтобы идти на компромисс и выключать. Но для mobile VR есть нюанс - я обычно работаю в кастомной версии движка от Oculus.
Вот здесь пишут (https://forums.unrealengine.com/t/alpha-to-coverage-oculus-go/440460/2), что для обычной версии движка включать нужно с бубном.

Ответить

Не знаю, будет ли видно на скриншоте, но полоска появляется при использовании Vector to radial value, когда надо использовать полярные координаты и двигать текстуру "по кругу"
Кстати, в 4.25 в настройках материала не могу найти ничего похожего на Alpha to coverage.

Ответить