что такое meshes в играх
Unity: процедурное редактирование Mesh
Преобразование моделей «на лету» — нередкая практика в симуляции физики деформаций, а также в играх с динамически генерируемым и изменяемым контентом. В таких случаях удобно применять методы процедурного редактирования и создания геометрии. Последние часто позволяют сэкономить заветные байты при передаче подгружаемых из сети данных. Кроме того — это весело!
Статья направлена на прокачку навыков процедурной обработки мешей в Unity. Мы расскажем об операциях преобразования и генерации частей меша.
Наш джентльменский набор для процедурного редактирования 3D-моделей включает три базовые операции: триангуляцию, движение точек, выдавливание. Подробно поговорим о последних двух. Сначала рассмотрим простейшие операции движения — перемещение вершин, поворот и масштабирование ребер и треугольников. Затем разберемся с одним из способов генерации новой геометрии — операцией выдавливания (Extrude).
В предыдущей публикации мы описывали свою структуру для удобной работы с данными 3D-моделей.
Как можно заметить, здесь используется PLINQ. Это обусловлено тем, что алгоритмы вычислительной геометрии часто можно оптимизировать за счет многопоточности.
Конечно, во время выполнения LINQ-конструкций мусора создается больше, чем при выполнении «ручного» кода. Однако этот недостаток в значительной степени компенсируется лаконичностью таких конструкций, а также наличием в PLINQ встроенных средств управления ресурсами. Кроме того, переход между однопоточной и многопоточной реализацией осуществляется с помощью всего лишь одной команды, что сильно облегчает процесс отладки.
Кручу, верчу, запутать хочу
Приступим к операциям движения. В перемещении вершин ничего сложного нет. Только нужно не забывать о совпадающих вершинах: если требуется, их положение тоже должно меняться.
Алгоритм реализован через добавление вектора движения к позиции вершины. Смещение при этом происходит относительно начала координат модели (pivot). Стоит отметить, что положение полигонов при таких трансформациях может меняться, а нормали их вершин — нет. Однако для упрощения изложения мы не будем рассматривать этот нюанс.
В CAD-средствах есть функция перерасчета нормалей, которую обычно вызывают уже после применения требуемых трансформаций. Существуют разные способы выполнения такого перерасчета. Наиболее распространенный вычисляет нормаль к плоскости каждого треугольника, а затем каждой вершине присваивает нормаль как среднее от нормалей треугольников, которым эта вершина принадлежит.
В целом здесь нет веских причин усложнять код и применять матрицу трансформации. Результат добавления вектора движения к позиции вершины соответствует интуитивному представлению о ее перемещении.
Перемещение ребер и треугольников реализовано так же — добавлением вектора смещения.
А вот вращать и масштабировать удобнее при помощи матрицы преобразования. Результат выполнения этих операций относительно начала координат модели скорее всего окажется не таким, каким вы ожидали или хотели его увидеть. За опорную точку вращения и масштабирования обычно берется середина объекта — как наиболее понятная для человеков.
Роем себе аккуратную ямку
В 3D-моделировании часто применяется операция выдавливания (Extrude). Для ее выполнения должен быть известен вектор движения (смещения) и набор полигонов. Процесс выдавливания можно декомпозировать на два действия:
1. Смещение полигонов на заданный вектор движения (offset). При этом необходимо дублировать разделяемые граничными полигонами вершины, чтобы не нарушать положение тех элементов, которые не относятся к смещаемой части. Иначе говоря, нужно оторвать и передвинуть выбранный кусок. Если этот шаг выполнить первым, то модель, вероятно, развалится на части, которые придется соединять в будущем.
2. Добавление новой геометрии между границей смещенной части и границей, которая образовалась при выдавливании. Просвет между основной и сдвинутой частями модели заполняется полигонами, образующими стенку.
В реализации удобнее сначала выполнять построение стенки, поскольку до сдвига мы имеем исходное положение ребер на границе и можем использовать эти данные сразу. В противном случае пришлось бы либо инвертировать направление вектора сдвига, либо сохранять часть информации о начальном состоянии меша.
Модель и ее части, с которыми мы работаем, складываются из множеств попарно соседних полигонов (треугольников). Назовем каждое такое множество кластером.
Два выделенных на модели кластера в Blender
Сперва нам понадобится получить все ребра контуров, ограничивающих выбранные кластеры. Для этого достаточно последовательно добавлять ребра в список. Если встречается совпадающее ребро, то его необходимо удалять, не добавляя при этом текущее. Для правильности работы такого алгоритма нужно ввести ограничение: на выбранном множестве треугольников не существует больше двух совпадающих ребер. В кейсах, где используется Extrude, модели зачастую удовлетворяют этому условию, а более сложный алгоритм требует больших вычислительных ресурсов.
После получения всех ребер контура нужно построить соответствующие стенки. Вариантов реализации можно нафантазировать много, но мы решили пойти по пути наименьшего сопротивления — генерировать параллелограммы в направлении вектора движения на основе ребер по отдельности. Поскольку смещение у нас для всех одно, в результате этого действия параллелограммы будут образовывать сплошную и замкнутую стенку для каждого кластера. Остается определиться с ориентацией элементов стенки.
Стенка, как и весь меш, состоит из треугольников. По конвенции OpenGL обособленный треугольник рендерится на экране, если при проецировании его точек на плоскость экрана обход их по порядку соответствует обходу по часовой стрелке:
Так, треугольнику соответствует некоторый вектор нормали, определяющий лицевую сторону. Каждый треугольник ограничен выпуклым контуром, состоящим из трех ребер. У каждого ребра есть две вершины, в нашей структуре представленные как v0 и v1. Определим направление ребра так, что v0 — начало, v1 — конец. Теперь, если направление ребер треугольника задано в соответствии с обходом его вершин, то любой внешний контур кластера должен иметь обход либо по часовой стрелке, либо против, а любой внутренний — наоборот. Конструкторы CustomMesh и Triangle мы реализовали так, чтобы обход вершин всех треугольников соответствовал направлению хода часовой стрелки.
Имея направление обхода контура, можно точно сказать, с какой стороны от ребра находится внутренняя часть контура, а с какой — внешняя. Опираясь на эту информацию, мы будем выбирать ориентацию стенки. Пусть (v0, v1) — ребро, на основе которого нужно сгенерировать желаемый параллелограмм. Возьмем две точки v2 и v3 как позиции смещения v0 и v1. Затем построим два треугольника по следующей схеме:
И так для каждого ребра контура.
При таком подходе лицевая сторона генерируемых стенок будет корректной и для горок, и для ямок. Есть лишь одно существенное ограничение: множество треугольников, над которым выполняется операция Extrude, не должно заворачиваться под себя относительно вектора движения.

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

Валидные подмножества полигонов
Стенка готова, осталось сместить треугольники. Этот шаг алгоритма прост в понимании, хоть реализация и получилась громоздкой.
В нашем случае нужно убедиться, что каждая вершина кластера принадлежит только его треугольникам. Если не выполнить условие, то за кластером могут потянуться некоторые соседние полигоны. Решение этой ситуации — продублировать каждую вершину, принадлежащую как кластеру, так и остальной части модели. Затем для всех полигонов кластера заменить индекс данной вершины на индекс дубликата. Когда условие выполнено, перемещаем все вершины кластера на вектор движения.
Готово. Теперь, сложив результаты всех шагов, получаем ямку или горку.
Пошаманив с координатами текстурной развертки и смещением точек контура, можно получить вот такое углубление:
И это еще не все
Помимо рассмотренных выше операций редактирования мы пользуемся и другими удобными методами работы с моделями.
Например, дополнительно мы написали метод Combine() для объединения двух CustomMesh. Ключевое отличие нашей реализации от UnityEngine.Mesh.CombineMeshes() в том, что если при объединении мешей какие-то вершины оказываются полностью эквивалентными, мы оставляем только одну из них, таким образом избегая лишней геометрии.
В том же модуле мы реализовали алгоритм плоской триангуляции Делоне. Используя его, можно, например, закрыть большую яму, созданную с помощью Extrude, плоской крышкой с текстурой воды и получить озеро:
Что же, с этим разобрались! В следующей статье рассмотрим особенности импорта .fbx в Unity и методы валидации моделей в проекте.
Оптимизация Unity UI
В этой статье разбираются вопросы оптимизации UI-элементов проектов, сделанных в Unity. На основании информации из официальной документации и личного опыта я постарался наглядно объяснить принципы работы UI-элементов. Также здесь вы найдёте практические советы, которые помогут улучшить производительность вашего проекта в том, что касается пользовательского интерфейса.
Терминология
UI-элементы — все элементы в Unity, относящиеся к построению пользовательского интерфейса. Сюда относятся, например: кнопка, текст, картинка, выпадающее меню и др.
Холст (canvas) — базовый элемент UI, являющийся контейнером для остальных элементов.
Меш (mesh) — совокупность параметров описывающих 3D-модели.
Квад (quad) — это меш, который представляет из себя четырехугольник.
Батчинг (batching) — объединение мешей объектов в один большой меш для более быстрой отрисовки.
Вызов отрисовки (draw-call) — команда на отрисовку от движка к графическому API (например, OpenGL или Direct3D).
Transparent queue — очередь отрисовки прозрачных объектов.
Альфа-смешивание (Alpha blending) — алгоритм смешивания пикселей по альфа-каналу для получения изображения с прозрачностью.
Атлас (Atlas) — вид ресурсов, который объединяет несколько текстур в одну.
Вводная
В оптимизация UI нет универсальных правил, работающих в любой ситуации. Всё сводится к нахождению баланса между стоимостью батчинга и количеством вызовов отрисовки. Можно выделить четыре основные проблемы:
Отрисовка Unity UI
Базовым элементом пользовательского интерфейса Unity является холст. Он отвечает за генерацию, сортировку и отрисовку мешей дочерних элементов интерфейса. Все элементы UI должны быть дочерними к какому либо холсту, в противном случае они не будут отображаться в игре.
Отрисовка происходит от самого дальнего к самому ближнему объекту от камеры (back-to-front) в Transparent queue с альфа-смешиванием.
Отдельно надо отметить, что прозрачность UI элементов НЕ влияет на производительность. Даже если элемент полностью состоит из “непрозрачных” пикселей, он все равно будет отрисован с использованием альфа-смешивания.
Также важно понимать, что при отрисовке обрабатываются все пиксели всех активных элементов. Это не зависит от того, видны они, перекрыты другими объектами или вообще полностью прозрачны.
Перестроение интерфейса пользователя
Перестроение интерфейса пользователя — это многоэтапный процесс, включающий в себя построение мешей каждого элемента UI, и попытка батчинга этих мешей для того, чтобы минимизировать количество отрисовок (draw calls).
Перестроение происходит в четыре этапа:
Перестроенный холст кешируется и переиспользуется до тех пор, пока один из элементов в холсте не помечается как изменённый.
Изменёнными (dirty) помечаются объекты, которые были активированы или деактивированы; у которых изменился материал, позиция, масштаб, поворот; изменилось текстовое значение у текстового компонента; производилось переназначение родителя и т.д.
В этом случае заново происходит перестроение холста, содержащего хотя бы один изменённый элемент. Правда, это относится только к тому холсту, в котором находится элемент. То есть изменения элементов в дочерних холстах не влияют на родительские.
Чем больше элементов в холсте, тем больше будут издержки на анализ и сортировку объектов.
Батчинг мешей
Объединение мешей, или батчинг, помогает снизить нагрузку на GPU, уменьшая количество вызовов отрисовки. В процессе батчинга меши сортируются по глубине и проверяются на перекрытие. При проходе от дальнего элемента к ближнему (или от верхнего элемента к нижнему в иерархии) в рамках одного холста объекты с одинаковыми материалами или текстурами объединяются в один меш. Для этого между ними не должно быть объектов с иными материалами. Также объекты с иными материалами не должны перекрывать запекаемые объекты своими габаритными контейнерами. Операция батчинга мультипоточная, её производительность значительно разнится в зависимости от количества ядер у процессора.
Текст может батчиться с другим текстом, если у них один и тот же шрифт. При этом неважно, выбраны одинаковые или разные настройки размера шрифта и стили. Если же шрифты разные, то текст не будет батчиться.
Также надо учитывать, что текст может перекрыть объект своим габаритным контейнером, и такое перекрытие можно легко пропустить.
Рассмотрим пример. Есть три объекта A, B и C, расположенные в иерархии таким образом:
На изображении слева объекты A и C будут объединены, т.к. имеют один и тот же материал и не пересекаются с объектом B. На изображении справа объекты A и C не будут объединены, т.к. имеется пересечение с объектом B.
Общие советы по оптимизации UI
Прежде чем начинать оптимизацию, настоятельно рекомендуется провести профайлинг UI. Это поможет определить узкие места, приводящие к потере производительности (если они есть). Для профайлинга существует множество инструментов, как встроенных в Unity (Unity Profiler), так и сторонних. Но вопросы профайлинга UI в этой статье мы разбирать не будем.
Вот что можно посоветовать для оптимизации UI в Unity:
Raycast Target у компонента изображения.
У TextMeshPro он спрятан во вкладке Extra Settings.
Элементы проходят проверку, когда:
Также имеет смысл убирать флаг Raycast Target с дочерних элементов, если корневой объект уже имеет его и полностью перекрывает своей геометрией дочерние элементы. Например, стандартная кнопка Unity UI.
Изображение своей формой полностью перекрывает текст, в этом случае можно снять Raycast Target с компонента текста.
Если все элементы холста не ждут событий ввода, то можно удалить компонент Graphic Raycaster с холста/вложенного холста.
Работа с текстом и его оптимизация
Текст в интерфейсе Unity состоит из сеток, в которых на каждый символ создаётся свой квад (quad). Меш перестраивается каждый раз, когда изменяется значение текста. Также перестроение происходит, если был выключен и повторно включён текстовый компонент или его родитель.
По умолчанию шрифты в Unity добавляются как динамические. Для каждого динамического шрифта, используемого в текстовом компоненте на сцене, создаётся свой атлас. В этот атлас включаются только используемые символы. Например, если текстовое поле содержит текст “New Text”, то созданный для него атлас будет содержать символы “N”, ”e”, “w”, “T”, “x” и “t”. Для каждого символа, отличающегося по размеру или стилю, будет создано своё представление в атласе.
Если в процессе выполнения программы содержимое текстового компонента изменится и там появятся символы, которые отсутствуют в атласе, будет вызвана перестройка всего атласа. В случае, если в текстуре атласа есть свободное место, то необходимые символы будут просто добавлены туда. При этом символы, которые в данный момент не используются, не удалятся. Если в атласе недостаточно места для новых символов, его размер будет удвоен и заполнен заново на основе используемых символов в активных текстовых компонентах.
Если для проекта задано строго определённое количество символов, например только латинский алфавит, то стоит использовать статичные шрифты, которые хранятся в памяти постоянно. Если в вашем проекте допускается большое количество символов, то лучше всё же остановиться на динамических шрифтах.
Можно также повысить производительность, если заменить текстовый компонент на спрайт. Например, появляющиеся в игре цифры (счёт) можно сделать, используя спрайты из одного атласа, содержащего набор только необходимых символов. В этом случае не будет издержек на перестроение холста и атласа шрифта.
Использование резервных шрифтов (Fallback fonts), перечисленных в поле Font Names в настройках шрифта, приводит к увеличению используемой памяти. Особенно это заметно на пиктографических шрифтах.
Не рекомендуется использовать Best Fit, т.к. эта опция приводит к быстрому переполнению атласа и вызывает его перестройку.
Best fit игнорирует настройки размера шрифта и пытается уместить текст в габаритный прямоугольник текстового компонента.
Теперь пара слов о TextMeshPro (TMP), популярной замене стандартному текстовому компоненту Unity. TextMeshPro также перестраивает свою сетку каждый раз, когда значение текста меняется. Однако TMP-текст не использует динамические шрифты. Для него заранее генерируются атласы шрифтов, в которые включаются все необходимые символы. Если для какого-то текста на сцене нет символа в назначенном на его компонент шрифте, то TMP начинает искать в резервных шрифтах. Если и там ничего не находится, то TMP будет пытаться найти этот символ во всех загруженных шрифтах.
Best Fit в TMP не создаёт проблем, как обычный текстовый компонент, так что его вполне можно использовать.
Большое количество шрифтов с разными локализациями или большие атласы шрифтов могут занимать много памяти. Поэтому лучше использовать предварительную загрузку только необходимых для конкретной локализации шрифтов.
В World Space рекомендуется использовать TextMeshPro вместо TextMeshProUGUI.
TextMeshProUGUI используется в холстах.
Атласы Спрайтов
Спрайтовые атласы — это вид ресурсов, который объединяет несколько текстур в одну. Они позволяют снизить количество отрисовок (draw calls) и повысить производительность.
Создание атласа.
Создаём атлас: Asset > Create > Sprite Atlas.
Выделяем атлас и помещаем необходимые sprite в Objects for Packing.
Нажимаем Pack Preview для предпросмотра атласа.
Нужно учитывать, что даже если на сцене используется один или несколько спрайтов из атласа, то атлас всё равно будет загружен целиком. Поэтому нет смысла пытаться внедрить все изображения в один гигантский атлас, который на устройствах с небольшой оперативной памятью может занять её ощутимую часть. Лучше разбить на какие-то более мелкие атласы, например на атлас пользовательского интерфейса меню игры и атлас интерфейса игрового режима.
Избегайте больших пустых мест в атласе, чтобы не занимать лишнее место в памяти. Для этого можно изменить размер атласа или добавить дополнительные изображения, чтобы максимально заполнить пустое пространство.
Для изображений, которые не попали в атлас, необходимо выбрать правильные настройки.
К каждому формату сжатия имеются требования, при соблюдении которых оно будет эффективно работать. Самые частые требования к изображениям:
При выборе формата сжатия Unity подскажет, если какие-то требования для выбранного формата не будут выполнены.
Также не забывайте, что форматы отличаются от целевого устройства. Дополнительную информацию и рекомендации по форматам можно посмотреть в официальной документации:
Оптимизация 3D-моделей для игровой сцены
Почти 2 года назад мы написали статью, в которой рассказали о варианте оптимизации 3D-геометрии в сцене с ограничениями на ракурс камеры и поворот соответствующих объектов. Не то чтобы много воды утекло с тех пор, однако возможность усовершенствовать решение, рассмотреть разные подходы и подглядеть за другими не дает покоя умам разработчиков. В этой статье мы опишем улучшенный вариант алгоритма, основанного на покраске полигонов, а также расскажем о попытках перенести часть такой работы в 3D-пакет.
Обрезка в сцене
Основной принцип этого алгоритма мы уже рассматривали в указанной выше статье: гасим все эффекты и прозрачные объекты, красим необрабатываемые полигоны одним цветом, а обрабатываемые — другими, рендерим, извлекаем результат. В старом варианте красили так, что все черное было лишним, а красным цветом метили только один треугольник.
В комментариях к той статье один из читателей указал на возможность оптимизировать алгоритм, установив взаимооднозначное соответствие между множеством полигонов и некоторым набором уникальных чисел. Тогда можно будет обрабатывать тем же способом больше одного треугольника. Рассмотрим такой вариант.
В этом случае, как и в прошлый раз, предполагается некоторая предподготовка, связанная с отключением всех свистящих объектов на сцене и объектов, гарантированно не влияющих на видимость целевой модели. Ракурсы камеры обрабатываются почти независимо, они связаны лишь общим буфером индексов видимых полигонов. Помимо этого, для каждого ракурса проводится предобработка геометрии, в процессе которой удаляются полигоны, повернутые к камере обратной стороной (backface). Так делается, потому что на определенном этапе алгоритма создается временный меш со значительно большим количеством вершин, чем у исходного. Это число может запросто превысить порог в 65 535, что потребует дополнительных телодвижений при вычислениях и приведет к снижению производительности. Эти полигоны в любом случае удалятся, так как их цвет не попадет в кадр. Однако из-за того, что каждый треугольник потенциально родит до трех мусорных вершин, устранение лишних полигонов заранее облегчает основной этап алгоритма и снижает затраты памяти.
Пусть имеется некоторая 3D-модель, геометрия которой представлена мешем. Чтобы покрасить конкретный полигон в уникальный цвет, нужно покрасить все его вершины в этот цвет. Поскольку в общем случае одна вершина может принадлежать разным полигонам, решить проблему в лоб не получится. Как бы мы ни покрасили какую-либо вершину, при рендеринге ее цвет переползет на все треугольники, которые ей владеют, в соответствии с алгоритмом интерполяции на стороне видеокарты.

Пример интерполяции цвета при отображении полигонов с общими вершинами
Поэтому необходимо как-то получить разбиение меша на отдельные независимые полигоны, сохранив при этом топологию и геометрию объекта. Dictum factum. Преобразуем массивы треугольников и вершин таким образом, что для каждого треугольника будет создано 3 уникальных вершины, позиция которых определяется по соответствующим вершинам исходного меша. Стоит отметить, что в общем случае такой меш будет иметь ощутимо большее количество вершин в сравнении с оригиналом. И если это число превысило 65 535, то при создании меша необходимо указать соответствующий формат индексации.
Теперь нужно обозначить полигоны этого меша так, чтобы после операции рендеринга можно было определить, какой из них попал на экран. Как уже было сказано, генерируем уникальные цвета для полигонов и красим каждую тройку вершин в соответствующий цвет. В результате получается новый меш, который мы назвали Byte-Colored Mesh.

Byte-Colored Mesh
Запоминаем раскраску. Настало время рендерить. Осуществляем 3D-рендеринг для всех ракурсов камеры и при обработке каждого из них пополняем буфер уникальных индексов полигонов, чьи цвета были обнаружены в кадре. На время вычислений для камеры нужно отключить сглаживание, чтобы избежать появления новых цветов из-за интерполяции соседних пикселей.
Стоит упомянуть, что по причине дискретизации некоторые треугольники могут не отобразиться из-за особо малого размера их проекции на экран, а не потому, что их что-то перекрывает или они повернуты не той стороной. Мы реализовали консервативный вариант алгоритма. В этом случае вычисляется AABB проекции треугольника на экране, и если хотя бы одна из его сторон меньше стороны текселя в снимке, то такой полигон помечается как видимый. Этот подход защищает от артефактов при запуске алгоритма с разрешением, которое меньше, чем разрешение экрана целевого устройства. Если же игнорировать мелкие полигоны, то результат будет также приемлем при условии, что разрешение используемой рендер текстуры больше разрешения экранов предполагаемых устройств.
Этот алгоритм обрезки мы реализовали в Unity и используем для оптимизации статических объектов, модели которых встречаются в сцене более одного раза в самых разных положениях. Это в основном декорации: камни, деревья, статуи, вазы и прочее, что ссылается на часто используемый префаб. Мы бы хотели оптимизировать такие объекты раньше, на этапе создания в 3D-пакете, но кто знает, в какую фантасмагоричную позу дизайнер уровней захочет поставить любимый канделябр.
Обрезка множества однотипных объектов таким инструментом уменьшает размер сцены, поскольку при static batching данные общего меша префабов в любом случае копируются на этапе билда столько раз, сколько активных отрисованных объектов с этим мешем представлено в сцене. Также наш метод позволяет освободить место в текстурных атласах, таких как lightmap. Сэкономленное пространство мы используем для повышения детализации тех частей моделей, которые пережили чистку.
Обрезка в 3D-пакете
И все же лучше, если художник может обрезать все лишнее у себя в редакторе, сокращая таким образом число этапов подготовки контента. Это оправдано, когда модель используется в сцене только с одним заранее определенным поворотом относительно камеры. Раньше объекты, которые точно будут повернуты к пользователю одной стороной, часто упрощались вручную перед интеграцией в проект. Важно отметить, что осуществлять такое упрощение программно в Unity значительно труднее из-за сложности упаковки UV-развертки, поэтому автоматизация на этапе 3D-пакета подчас облегчает жизнь художника.
Один из инструментов для работы с 3D-моделями в нашей компании — Blender. В него мы и залезли. Вроде бы такой «взрослый» софт, как Blender, должен иметь подобный функционал. Однако оказалось, что не должен. Пришлось пилить свой собственный велосипед.
Первой идеей было использовать всем знакомый инструмент выделения — по сути повторить часть ручной работы художника для одного ракурса камеры: выделить видимые полигоны, инвертировать выделение, удалить. План был такой: перемещать камеру, в каждой позиции определять AABB проекции модели, затем запросить результат выделения полигонов области, соответствующей AABB, получить объединение множества полигонов текущего ракурса с предыдущими и в конце удалить невыделенные полигоны.
Однако во время реализации скрипта был обнаружен существенный недостаток с точки зрения поставленной задачи. Инструменты выделения в Blender (rectangle select, circle select) теряют точность с возрастанием количества выделяемых элементов на единицу площади экрана (некоторые полигоны остаются невыделенными), что делает их использование в наших средствах автоматизации невозможным. Интересный факт: в том же 3ds Max такой проблемы не наблюдается.

Выделение издалека в Blender

Результат выделения
Следующая попытка была направлена на решение задачи в лоб: мы пускали лучи из камеры через каждый пиксель вьюпорта и смотрели, какие полигоны первыми пересекутся с хотя бы одним лучом. Мы не надеялись на точные результаты при таком подходе, но попробовать стоило. Итог очевиден: очень низкая производительность при обработке на CPU или те же дырки при небольшом количестве лучей.
Тем не менее мы сделали плацдарм для воплощения более продвинутого подхода. Идея состояла в том, чтобы выбрать энное количество случайных точек на каждом полигоне и затем пустить в их направлении лучи из камеры. Этот подход хорошо себя зарекомендовал, но у нас возникали граничные случаи: обрезались также полигоны, у которых угол между лучом и их нормалью был приблизительно равен π/2. Таким образом, при зуме камеры из-за перспективных искажений могли открыться вырезанные участки.
Этот способ оказался, по мнению художников, слишком агрессивным, поэтому мы решили остановиться на обрезке только backfaces.
Заключение
Не секрет, что бережное отношение к ресурсам устройства при создании игр — это важнейший фактор, влияющий на качество конечного продукта. Особенно это касается мобильных платформ, капризных к активному использованию ОЗУ. Сокращение количества полигонов позволяет более эффективно заполнять пространство текстурных атласов и немного снижает вычислительную нагрузку.
Также не стоит забывать о затратах человеко-часов и о цене ошибки при применении инструментов, описанных выше, и им подобных. Предложенный подход предполагает хорошо отлаженный пайплайн работы арт-отдела, в особенности сотрудников, занимающихся интеграцией моделей в проект.
Таким образом, имея условия и инструменты, рассмотренные в этой статье, мы придерживаемся следующих правил. Если предполагается, что создаваемая модель будет всегда повернута одной стороной к пользователю, а также если с этих ракурсов величина перекрытия одних частей модели другими довольно мала, то художник пользуется нашим инструментом обрезки backfaces в 3D-редакторе, проверяет корректность и приступает к упаковке UV-развертки. Если же модель часто используется в разных положениях или имеет более сложную геометрию, то уже после импорта в проект мы запускаем описанный в первой части статьи алгоритм, обрабатывая им все статичные объекты в сцене.






