Как устроена графика в современных играх. Часть 3
Перевод заключительной части статьи о том Как работает трехмерная компьютерная графика в играх, как она связана с графическим конвейером и какие основные знания нужны программисту графики. Текст простой, не требующий предварительной подготовки.
Предыдущие части
Начало серии статей доступно по ссылке: Как устроена графика в играх. Начало
Вторая часть здесь: Как устроена графика в играх. Продолжение
Свертывание вершин
Графические программисты часто ссылаются на «графический конвейер». 3D графика немного похожа на сборочную линию с потоком от одного этапа к другому, хотя она работает не только над одним объектом за раз, как на традиционной сборочной линии.
Конвейер в современных графических картах всегда примерно один и тот же. Карты имеют специальное аппаратное и программное обеспечение, зашитое прямо в чипе, чтобы максимально ускорить конвейер. Очевидно, что существует много различий между поставщиками и семействами видеокарт, но вам обычно не надо думать как все работает на таком уровне.
Примечание
Если вам интересно, как все работает на уровне абстрактных аппаратных средств, советую почитать “Путешествие по графическому конвейеру” Фабиана Гизена. Эта более продвинутая и сложная для понимания серия статей, чем моя.
Я собираюсь осветить много важных деталей и рассказать об интересных и важных вещах. Сейчас я коснусь первой части конвейера, которая называется Вершинным Шейдером.
Шейдеры стали распространены около 13 лет назад, когда вышел DirectX 9. Он добавил вершинный и пиксельный шейдеры. Чтобы объяснить, что такое шейдеры, и сравнить их с тем, что было раньше, я собираюсь рассказать о том, что они делают.
Второе появление каркаса головы Эйдена
Мы уже говорили о моделях, из которых составлен мир. Эти модели состоят из индивидуальных точек, называемых вершинами, которые соединены в треугольники. И я обещал подробнее остановиться на этом.
Поскольку все в игре состоит из вершин, все что мы хотим сделать с моделями, мы можем проделать с вершинами. Графическая карта видит только длинный список вершин. Она не понимает таких вещей, как анимация на экране или качающиеся листья дерева.
Давайте посмотрим на простой пример, что мы бы могли сделать: передвигать и помещать объекты в игровом мире. И давайте возьмем простой объект, не персонажа.
Объекты, которые дизайнеры создают в пакетах наподобие Maya и 3D Studio Max, вседа находятся в своем собственном мире. Они создаются не прямо в том месте, где они будут в Чикаго, а в “пустой белой комнате”, как в Матрице. Каждый объект помещается в центре абсолютной пустоты.
Какая-то лампа под мостом электрички, нарисованная в собственном мире
Когда модель сохраняется на диск или загружается и передается графической карте, она не имеет представления, куда попадет в игровом мире. Это значит ,что когда нам надо нарисовать объект, мы перемещаем его из собственного мира на сцену, которую рисуем. Это происходит всегда. Даже совершенно неподвижные объекты вроде зданий и мостов тоже перемещаются на сцену из своего мира.
Здесь видно, как несколько ламп размещены на частичной реконструкции итоговой сцены
Как я уже сказал, мы можем работать только с вершинами. Мы не можем прямо сказать графической карте: “Можешь поставить эту лампу прямо под мостом? И еще одну чуть дальше? Отлично!”
Вместо этого мы говорим карте, что мы хотим сделать с вершинами объекта. Сейчас кажется, что все просто. Если мы одинаково перемещаем все вершины и сохраняем их фиксированными относительно друг друга, это то же, что и перемещение объекта целиком. Потому каждая его часть перемещается одинаково. Нам только остается выяснить, что необходимо изменить. Это называется трансформацией.
Примечание
Стоящая за этим математика слишком ложна и не относится к предмету нашего обсуждения. Если вы изучали линейную алгебру, то, скорее всего, уже знаете все нужное. В основном все сводится к матричному умножению векторов.
До появления вершинных шейдеров ваши возможности были сильно ограничены теми трансформациями вершин, которые может позволить графическая карта. Давалось передвижение и вращение объектов, другие простые вещи, но ничего преувеличенного или сложного.
Вершинные шейдеры — это небольшие компьютерные программы, которые выполняются прямо в графической карте. Они берут одну вершину, делают с ней что нужно, и отправляют результат. Они не только перемещают вершины, но еще поднимают и опускают их, отодвигают от ближайшей вершины, раскачивают как от ветра вперед и назад, анимируют и так далее.
Я взял один из вершинных шейдеров, который используется в Watch Dogs для персонажей. Надеюсь, эти забавные эффекты помогут вам лучше понять работу шейдеров. Также вы можете заметить, что для визуализации кожи на лицах и руках используется другой шейдер.
Я сделал очень простое изменение, чтобы исказить персонажей, а все остальное работает как обычно.
На анимации вы можете видеть, как странно искажены персонажи, словно по ним пробежала волна. Выше я писал, что если переместить каждую вершину, переместится весь объект. Я сделал в шейдере изменение, которое работает только с одной вершиной, но поскольку все вершины связаны в одном движении, в итоге эффект отражается на всем объекте.
Так же просто можно изменить часть трансформации, которая относится к помещению объекта на присущее ему место. Теперь объекты парят над землей. Это не физическая симуляция, поэтому она никак не связана с гравитацией или столкновениями, но если мы хотим, все будет летать.
В этой анимации мы заставляем вершинный шейдер медленно поднимать и опускать объекты.
Все это не очень красиво, но дает представление о работе вершинных шейдеров. Если бы мы теперь хотели анимировать листья на дереве, мы могли бы использовать что-то не слишком отличающееся. Вместо того, чтобы подниматься и падать, листья качались бы туда-сюда по направлению ветра. Затем мы можем сделать ветер сильнее или слабее, чтобы дерево сгибалось больше или меньше.
Здесь можно увидеть, что произошло бы при увеличении объектов в 7 раз.
Я немного упрощаю, но по большому счету «передвижение объектов на место» является целью большинства вершинных шейдеров. Исключением являются любые анимированные объекты — люди и животные, а также качающиеся канаты, ткань на ветру и т.д.
Анимация людей происходит вокруг скелета и «кожи». Скелет — простое представление движущегося человека. Вместо того, чтобы беспокоиться в этот момент об анимации поясов, шляп или джинсов, мы рассматриваем только важные движения, которые обычно очень близки к движению человеческого скелета.
Простой скелет для человеческой фигуры. Он взят не из Watch Dogs, потому что такой тип информации крайне трудно визуализировать вне дизайнерского пакета.
Изображение лицензировано в соответствии с Attribution-ShareAlike CC BY-SA (c) команда MakeHuman 2001-2014
Это поза привязки (bind pose) или Т-поза, показывающая, как выглядят персонажи совсем без анимации.
Анимации — бег, ходьба, прыжки, скачки — все применяются к этому скелету. Процесс прост, потому что нам нужно работать только примерно с сотней костей, а не тысячами и тысячами вершин.
Каждая вершина затем связывается с одной или несколькими костями в данной статичной позе, и это связывание называется «скином». Во времена Half-Life 1, когда техника только приобретала популярность, каждую вершину связывали с одной костью. В наши дни вершины связывают с 2-4 костями, с назначением весов для каждой кости, поэтому перемещение вершины в соответствии с перемещением кости может быть больше или меньше. Так получается более гладкая анимация. Когда вы сообщаете перемещение разным участкам фигуры, кости могут перекрывать друг друга и движение рук и ног изображается плавными линиями.
Здесь показан приведенный выше скелет с повернутой костью бедра. Цвета показывают веса кости относительно вершин модели.
Изображение лицензировано в соответствии с Attribution-ShareAlike CC BY-SA (c) команда MakeHuman 2001-2014
Данная техника имеет ограничения, в особенности, для тех мест, где ткань или человеческая кожа собирается, сжимается или растягивается. Например, на плечах или локтевых сгибах. Перед нами не идеальный способ анимации, но весьма эффективный. Самое большое ограничение в том, достоверно анимировать лица таким способом очень трудно. Вы можете сделать на лице много «ложных» костей, например, для бровей и вокруг рта, но это лишь грубое приближение для движения мускулов и кожи.
Другая важная вещь заключается в том, что связывание вершин и костей очень специфично, и зависит от того, каким образом было выполнено. Вполне возможно распространить одну анимацию на множество разных моделей. Но каждая отдельная модель, которая должна иметь скелет, должна быть соединена скелетом с вершинами. Так должно быть потому, что анимация работает только путем перемещения вершин из их исходного положения относительно скелетов. Если вершины не находятся в ожидаемых положениях, тогда все идет … плохо.
Если модель не совпадает со скелетом, который она использует, анимации будут сделаны очень-очень неправильно.
Если сделать персонажей в два раза шире, вы получите эффект ‘Donut Drake’ («Пончик Дрейк», это скин, который делал очень толстого Натана Дрейка в Uncharted: Drake’s Fortune, Uncharted 2: Among Thieves и Uncharted 3: Drake’s Deception). Но также можно заметить, что анимации рук исказились сильнее всего, потому что они больше отдалились от первоначального положения.
Первая анимация выглядит странно знакомой. Анимационные глюки, подобные этому, случаются в играх время от времени. Это обычно вызвано неправильной анимацией, применяемой к скелету, или неправильным скелетом, используемым для модели. Как в описанном ранее в случае с текстурами и моделями: скелет, кожа, анимация и модель должны качественно соответствовать друг другу, или все быстро портится.
Надеюсь, теперь у вас есть некоторое представление, чем занимаются вершинные шейдеры, и как анимации превращают статическую модель в живой дышащий персонаж.
Источник: https://renderdoc.org/blog/Graphics-in-Plain-Language/Part-4.html