Главная » Хабрахабр » [Перевод] О 3D-графике простыми словами

[Перевод] О 3D-графике простыми словами

Часть 1. Введение

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

Идея этой серии постов уже давно витала где-то на периферии моего сознания, и снова всплыла после прочтения интересной статьи с разбором последней Deus Ex.

Очень немногим людям любопытно глубоко погружатьcя во все её подробности, но я считаю, что есть темы, которые интересны каждому. Мне кажется, что графика, и в особенности та сложность, какой она достигает в современных играх — это интересная тема. Я думаю, что большинству людей, игравших в игры, было любопытно, как получаются те или иные эффекты, или с помощью какой технологии удалось создать такую потрясающую графику в какой-нибудь новой игре.

Есть много составляющих, необходимых для создания даже простой 3D-игры, не говоря уж о таком проекте, как Watch Dogs.

Тем не менее, основная идея заключается в том, чтобы создать общее описание того, что происходит внутри современной игры, не отпугнув при этом никого из читателей — я буду предполагать, что у вас нет знаний математики и программирования. У меня есть только общее представление о том, что нужно рассмотреть в этой статье, но это будет зависеть от того, какие темы вызовут интерес. Игра хорошо известна своей графикой (причём мнения о ней могут быть полностью противоположными), и в ней есть множество аспектов, которые можно рассмотреть на отдельных примерах. Если вы знаете разницу между ЦП и графической картой, и отличаете оперативную память от жёсткого диска, то этого будет вполне достаточно, а остальное я объясню.
Эта статья будет развиваться в соответствии с её видеопрохождением Chip & Ironicus's Let's Play of Watch Dogs, чтобы немного структурировать изложение. Возможно, я расскажу и о других играх.

Начну я с объяснения основ, которые почти одинаковы для каждой игры, но также я рассмотрю и некоторые техники и визуальные эффекты Watch Dogs.

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

В этой анимации показана часть постепенно создаваемого кадра в процессе его отрисовки графической картой.

В кино традиционно использовали 24 кадра в секунду (frames per second, FPS), в телевидении частота примерно такая же, около 24-30 кадров. Большинство людей знает, что компьютерная графика (да и графика любого другого видео) состоит из серии неподвижных кадров, каждый из которых отображается определённую долю секунды. Падение частоты ниже 30 нежелательно, хотя и случается довольно часто. В играх FPS может быть переменчивым, потому что в каждом кадре выполняется множество работы. Разработчики стремятся реализовать частоту 30 или 60, что зависит от целей игры. Обычно верхним пределом для консольных игр является 60 FPS. Причина этих конкретных чисел заключается в вертикальной синхронизации (vsync), о которой мы расскажем ниже. На PC при наличии дисплея с высокой частотой кадров можно добиться 90, 120 или даже выше.

Если мы хотим, чтобы игра работала с частотой 30 FPS, то на выполнение всей необходимой для кадра работы у нас есть только 33 миллисекунд. Мысленно мы можем взглянуть на эту задачу с противоположной стороны — вместо того, чтобы смотреть, насколько высока частота FPS, мы смотрим на то, насколько мало время, выделенное на каждый кадр. Даже для компьютера такой промежуток времени не очень велик, учитывая тот объём работы, который нужно выполнить. При 60 FPS время в два раза меньше — около 17 миллисекунд. Чтобы дать вам представление о величинах, то по приблизительным подсчётам пуля перемещается примерно на 1 метр в миллисекунду.

Всё равно в основном я буду рассказывать о том, что не сильно отличается на консолях, а если что-то всё-таки будет отличаться, то я подчеркну это. В основном мы будем говорить о PC, потому что эта платформа открыта, и я не могу рассказывать о консолях без опаски нарушить соглашения о неразглашении (NDA). Что касается мобильных платформ, то большинство различий между оборудованием PC/консолей и мобильного оборудования не относится к теме моей статьи.

Честно говоря, я поставил сюда эту картинку, чтобы вы понимали, что в статье будет не только текст.

Границы дисциплины под названием «программирование графики» довольно размыты, но я скажу, что программирование графики начается тогда, когда у нас есть вся информация, необходимая для построения кадра: мы знаем, что происходит, все текстуры и модели находятся в памяти (не на диске), анимации уже анимированы, физика посчитана, и нам осталось отрисовать готовый кадр для отображения на экране. Именно эта задача нас и интересует — мы не будем беспокоиться о том, как выполняет все свои вычисления ИИ, или как производится физическая симуляция для перемещения объектов.

Также поясню (особенно программистам графики), что в первую очередь стремлюсь к пониманию, поэтому, возможно, буду использовать достаточно сомнительные объяснения, если они позволят мне достичь своей цели. Добавлю, что я буду рассматривать 3D-игру с достаточно традиционным рендерингом, наподобие Watch Dogs — многие из базовых принципов применимы и к 2D-играм, но на них демонстрировать концепции немного сложнее.

Часть 2. Из чего состоит кадр

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

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

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

Разные примеры промежуточных изображений, используемых при построении кадра Watch Dogs.

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

Каждое из этих промежуточных изображений тоже строится из ещё меньших фрагментов. Каждый объект в сцене или группа связанных объектов создаётся отдельно как текстурированная модель. При разработке игры художники строят эти модели в 3D-редакторе и создают для них все необходимые ресурсы. Затем эти модели размещаются в мире с помощью редактора уровней и из них постепенно строится виртуальный город.

На самых первых этапах становления графики наложение текстур было затратным процессом, и по возможности от него избавлялись, закрашивая объекты одинаковым цветом. Наверно, это известно почти всем, и если последние 20 лет вы наблюдали за развитием 3D-графики реального времени, то знаете, насколько более сложными стали модели сегодня. Текстуры оставляли только для таких элементов, как глаза или лица, которым действительно требовалась детализация.

Каждый треугольник имеет три точки, называемые вершинами, и поскольку треугольники связаны друг с другом, вершины могут быть общими для нескольких треугольников. 3D-модель целиком состоит из взаимосвязанных треугольников, образующих форму объекта. Также стоит помнить, что некоторые объекты, например, персонажей или деревья перед отрисовкой необходимо анимировать. Позже мы вернёмся к этому, потому что вершины и треугольники достаточно важны. К этому мы тоже вернёмся. Модель создаётся в стандартной статической форме, а анимации применяются в каждом кадре.

Это 3D-модель головы Эйдена Пирса после анимирования. Треугольники видны, потому что они отрисованы плоскими, а не сглажены, как это бывает обычно.

Текстуры — это обычные плоские файлы изображений, обычно квадратные или с простыми размерами, например, прямоугольники с соотношением 2:1. Чтобы добавить на 3D-модель больше деталей, накладываются текстуры. Вместо простого повторяющегося узора бумажной обёртки, изображение точно соответствует размеру обёртки. Текстуры накладываются на 3D-модель с помощью более сложного процесса, который я подробнее рассмотрю ниже, но концептуально он похож на процесс заворачивания подарка. Если вы видели бумажные модели для сборки при помощи клея, то принцип здесь такой же.

Такое разворачивание часто выполняется автоматически, но в случае особо сложных объектов может производиться вручную. Эта аналогия более уместна, чем можно подумать, потому что эти текстуры обычно создаются «разворачиванием» 3D-модели в плоскую заготовку, как это делается с бумажной моделью, после чего поверх неё рисуется текстура.

Это текстура, соответствующая показанной выше модели головы Эйдена Пирса. Тут есть части для зубов и языка. Заметьте, что область над его лбом не текстурирована, потому что постоянно закрыта Легендарной Бейсболкой Эйдена Пирса™.

Примечание

При разворачивании некоторые части объекта, требующие большей детализации, увеличиваются, а другие — уменьшаются.

Очень часто говорят о различных «скинах» моделей, особенно в случае настраиваемых персонажей. Сегодня то, что называют «скином», обычно относится к небольшим изменениям в модели — новый ремень или другая шляпа — но изначально этот термин возник, потому что использовалась одна и та же модель, но менялась текстура (или «скин» — буквально переводится как «кожа») для создания персонажа, выглядящего иначе. Даже сегодня с помощью таких текстур можно создать большую вариативность NPC или объектов, что экономит время и деньги — не нужно создавать множество уникальных 3D-моделей. Разная одежда, которую может надевать Эйден — это чаще всего просто различные текстуры одной и той же модели.
Вот краткий фрагмент поворота головы 3D-модели. Наложена только текстура, и ничего более.

Некоторые из них будут одинаковыми моделями — такие объекты, как цветы в горшках и мусорные баки на самом деле никогда не создаются по отдельности, это одна модель или несколько моделей, размещаемых в разных местах. В рассматриваемом нами кадре есть примерно 1700 объектов, отрисовываемых в части основного рендеринга. Однако примерное количество отрисовываемых для завершения кадра объектов близко к 4700 — это даёт нам представление о том, сколько дополнительной работы необходимо выполнить кроме отрисовки всех этих моделей.

Давайте взглянем ещё на один пример объекта — бейсболку, которую носит Эйден.

Это текстура и затекстурированная модель бейсболки. Видно, что текстура построена из отдельных частей, которые соединяются на модели.

Примечание

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

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

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

Уже не очень похоже на Легендарную Бейсболку Эйдена Пирса.

Там, где был козырёк, на текстуре головы только волосы. Там, где на текстуре бейсболки был логотип, на текстуре головы расположены ухо и зубы. Разумеется, этот пример будет в игре ошибкой, но подумайте о том, что можно сделать, если анимировать текстуру или заставить её мерцать — в играх подобные вещи используются для различных эффектов, которые теперь вы сможете замечать. Наложение выполнено абсолютно так же, но использована другая текстура. В частности, игра Saint's Row 4 использует подобное для спецэффектов «симуляции».

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

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

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

Часть 3. Что рисовать не нужно

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

В ЦП выполняется вся остальная часть игры, поэтому он принимает решение о том, какие объекты должны отрисовываться в текущем кадре, куда смотрит камера, какие анимации воспроизводятся. Над отрисовкой кадра совместно трудятся центральный процессор и графическая карта. Графическая карта — это «рабочая лошадка», которая выполняет всю сложнейшую работу, связанную с отрисовкой пикселей, именно поэтому она является отдельным специализированным устройством.

Оказывается, и у ЦП, и у графической карты есть ограничения на скорость или объём вычислений, но это разные типы ограничений.

Насколько отличаются эти объекты — это 100 одинаковых фонарей, или 100 кустов/растений/деревьев? В целом ЦП больше интересует собственная часть работы: сколько объектов нам нужно отрисовать всего? Анимированы ли эти объекты, движутся ли динамически, и какие из них статичны или неподвижны?

Это кажется очевидным, но для реализации требуется тщательная работа. Первое, что мы делаем, чтобы как можно больше снизить нагрузку — отрисовываем только то, что видно на экране. Это значит, что в каждой игре есть чёрная пустота, повсюду следующая за игроком, и когда он не смотрит на объекты и людей, те перестают существовать. Не забывайте, что мы строим каждый кадр с нуля, поэтому в каждом кадре нам нужно смотреть на каждый объект и определять, видим он или нет.

В этой анимации мы поворачиваем камеру, показывая пустоту за игроком. Внимательные зрители заметят, что она не совсем пуста...

Однако если нужно отрендерить 5000 объектов, то стоит задуматься о применении трюков. Сложно выработать в этом случае какие-то практические правила, но в целом можно отрисовать в сцене около 1000 объектов, не заботясь о нехватке места. Не забывайте, что в большинстве игр, где игрок может управлять камерой, мы не можем знать, под каким углом он взглянет, поэтому нужно сохранять пространство для манёвра.

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

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

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

В этой области ещё много предстоит исследовать и необходимо применять различные сложные техники. На самом деле в этой сцене всё равно есть множество объектов, которые в результате окажутся невидимыми. Всегда приходится идти на компромиссы, но если можно потратить немного времени, или придумать очень умный способ, чтобы избежать отрисовки 100 объектов почти без лишних усилий, то мы сможем сделать сцену ещё более сложной или плотнее заполненной.

В некоторых случаях нам приходится отрисовывать здание, которое едва видно на экране и далеко выходит за его пределы. Также здесь есть ещё одна небольшая проблема. Мы всегда можем разбить эти объекты на несколько частей, тогда каждую часть можно отрисовывать или пропускать, то есть пустых трат будет меньше. Это тратит ресурсы впустую. Однако теперь мы увеличили общее количество отрисовываемых объектов, когда они находятся на экране, и создали противоположную проблему!

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

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

Теперь мы отдалимся, чтобы показать видимую область перед камерой (простите за монтаж для экономии времени).

На изображение наложена примерна область видимости камеры. Ширина этого треугольника зависит от зоны видимости (Field of View) — иногда в играх это настраиваемая опция, иногда постоянное значение.

Вот каркасное отображение сцены, область видимости камеры ограничена белым.
Кто-нибудь из вас уже мог подумать о небольшом трюке, который бы позволил обойти ограничение отрисовки определённого количества объектов — почему бы не сделать объекты очень сложной комбинацией всего в небольшой области, вплоть до отдельных листьев? Тогда отрисовки 1000 объектов будет более, чем достаточно.

То есть даже один объект, если он достаточно сложен, может снизить частоту кадров игры до 20 FPS. Но здесь мы сталкиваемся с совсем другим набором ограничений — графические карты имеют ограниченную производительность, и чем сложнее объект, тем дольше будет происходить его отрисовка. В том числе и поэтому я говорил, что ограничение на количество объектов довольно нечёткое.

В том числе и поэтому игры, пытающиеся реализовать более сложную или изощрённую графику стремятся к использованию менее сложных и детализированных сцен — уравновешивание смещает весы в одну или другую сторону, поэтому можно обеспечить себе больше пространства для манёвра, пожертвовав тем, что не так важно для игры. Объём временных затрат на объект зависит от сложности и детализированности моделей и текстур, а также от изощрённости освещения и теней.

Это своего рода тепловая карта, демонстрирующая, в каких частях сцены есть особо сложные модели. Заметьте, сколько проблем могут вызывать деревья и растительность.

Аналогично тому, что мы можем оптимизировать количество объектов, отсекая всё ненужное, мы можем увеличить запас «сложности», устранив лишнее. Есть ещё один набор техник под названием «уровни детализации» (level of detail, LOD), который специально создан для решения подобных проблем.

Эта тема обычно пересекается со множеством других, поэтому я постараюсь объяснить её доступно. Один из приёмов на самом деле хорошо известен — это изменение разрешения текстур.

На то есть множество причин, но одно из преимуществ этого заключается в том, что можно взять текстуру размером 1024x1024 и запросто создать её уменьшенную версию размером 512x512. Текстуры в играх обычно являются прямоугольниками с размерами, равными степени двойки — 512, 1024, 2048, 4096.

То есть у текстуры размером 1024x1024 будут уменьшенные версии размером 512x512, 256x256, 128x128, 64x64, 32x32, 16x16, 8x8, 4x4, 2x2 и 1x1. По причинам, о которых я расскажу ниже, всегда необходимо, чтобы у текстуры были всевозможные версии меньшего размера. Мы можем сэкономить, использовав меньшие версии той же текстуры. Однако одно из преимуществ этого в том, что если далёкие объекты имеют на экране небольшой размер, то накладывать на них текстуру 1024x1024 — значит зря тратить ресурсы.

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

Примечание

Обычно, когда игрок приближается к объекту, используется наибольшая текстура, но как видело большинство людей в той или иной игре, такое происходит не всегда и текстуры перед загрузкой выглядят излишне размытыми. Обычно так происходит потому, что текстуры невозможно загрузить с DVD или жёсткого диска сразу в память графической карты для рендеринга. Чаще всего такое случается, когда игрок резко меняет своё положение, например, при респауне, загрузке нового уровня или очень быстром движении. Во всех остальных случаях текстуры обычно загружаются постепенно, в процессе перемещения игрока по миру.

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

Но в то же время он может быть огромной растратой ресурсов. Благодаря упрощённым объектам и обрезанным моделям можно сильно сэкономить и такой подход приходится использовать в любой игре наподобие Watch Dogs. Если их будет слишком мало, то вам или не удастся слишком много сэкономить, или будет заметны скачки качества при их смене. Необходимо принять очень продуманное и взвешенное решение о том, сколько потребуется упрощённых версий моделей. Если их будет слишком много, то вы впустую займёте память и потратите человеко-часы, необходимые на создание объектов.

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

Причина довольно проста — запасы производительности и ресурсов, о которых я постоянно твержу, не меняются в зависимости от того, есть у вас отражения или нет. В предыдущей части я сказал, что расскажу о том, почему очень сложно правильно реализовать отражения и другие подобные вещи. Кроме того, отражения очень сильно зависят от того, под каким углом мы на них смотрим, поэтому для получения точных отражений у каждого отражающего объекта они должны быть своими! Если вы хотите создать отражение, которое может снова отобразить всю сцену, то придётся заново выполнять всю ту работу, о которой я говорил.

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

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

Это пререндеренное изображение называется «кубической картой» (cube map). Оно не совсем точно относительно места, в котором стоит Эйден, но достаточно к нему близко.

Я не исследовал этот вопрос подробно, но полагаю, что они всегда рендерятся, когда персонаж находится на улице, и в основном используются для того, чтобы получать его точные отражения на автомобиле, в котором он сидит, чтобы улучшить изображение и придать ему лёгкое ощущение реальности. Watch Dogs выполняет рендеринг отражений в реальном времени. Так как игрок всегда сосредоточен на своей машине и ближайшем окружении, то факт неправильности отражений на других автомобилях едва заметен.

Например, в отражении рендерится намного меньше объектов, чем есть в настоящей сцене — всего около 350 — и из них многие очень упрощены по сравнению с полными версиями. Существует множество аппроксимаций для ускорения рендеринга этих отражений. Кроме того, на этих объектах нет теней, а освещение очень простое — только то, что поступает от солнца и неба. Я подозреваю, что сложные объекты, например люди, полностью отбрасываются вне зависимости от расстояния, но эту теорию я не проверял. Отражения рендерятся с земли как в объективе «фишай», то есть отражения самой земли невозможны, а то, что находится рядом с ней, имеет очень низкую детализацию.

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

Запас — величина постоянная, поэтому если оставить место для этих отражений, то нужно принести в жертву что-то ещё. Это решение было намеренным и принималось непросто.

Вот очень «фишайное» отображение сцены вокруг Эйдена с видом снизу, сделанное для отражений. Сориентироваться можно по двум фонарям и рельсам поезда.
Есть ещё одна часть работы, которую я хочу здесь упомянуть — тени. Позже я планирую рассказать о том, как работают тени, потому что это интересная тема, но сейчас самое важное — запомнить, что тени очень похожи на отражения. Каждый источник освещения, отбрасывающий тень, должен отрендерить изображение сцены со своей точки обзора. На этот раз способов упростить работу не так много — для правильного вычисления теней каждый источник освещения обязан иметь это изображение.

Примечание

Здесь я говорю только об освещении, вычисляемом во время игры. Раньше часть освещения вычислялась пререндерингом всей необходимой информации до запуска игры, то есть тени и освещение «запекались» в уровень, они были постоянными и их невозможно было менять или реагировать на них динамически. Такая техника в современных играх используется не так часто, и очевидно, что её нельзя применять к подвижным источникам света.

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

Расчёт теней в игре довольно сложен и, как мне кажется, специально оптимизирован для случая отбрасывания теней в городе. К сожалению, это один из случаев, когда Watch Dogs не может служить хорошим примером. Поэтому я лучше переключусь на Far Cry 4 и рассмотрю вычисление теней на примере кадра из этой игры.

Вот сцена из Far Cry 4, которую я использую просто для примера.

Вот изображение с информацией о тенях этой сцены — для каждого из них требуется совершенно новый рендеринг сцены.

Здесь тоже можно использовать некоторые из аппроксимаций, применяемых в случае отражений, только их намного меньше. Поэтому когда нам нужно добавить к источнику освещения отбрасывание теней, то придётся отрендерить сцену ещё один раз. Можно отрендерить изображение очень маленьким, но тогда тени будут зернистыми и низкодетальными. Можно пропускать мелкие или далёкие объекты, но нужно учесть, что эти объекты будут казаться не отбрасывающими тени. Обычно не получается использовать очень упрощённую версию объекта, потому что тогда будет казаться, что объект отбрасывает тень сам на себя, или появятся просветы между объектом и его тенью.

Во многих случаях удаётся упростить источники освещения, объединив их — в Watch Dogs такое происходит с фарами автомобилей. Ещё одно следствие, которое очень легко упустить — необходимость создания изображения затенения для каждого источника освещения.

Если у фар есть тени, то это сделать уже не так просто, и будет гораздо заметнее — когда игрок пройдёт перед автомобилем, то свет будет идти откуда-то между двумя фарами. Когда включены обе фары, то отрисовывается только один источник освещения, но имеющий особую форму, из-за которого он выглядит как два луча. Возможно, тогда придётся разделить фары, но при этом не только появятся дополнительные затраты на вычисление теней, но и придётся отрисовывать новое освещение.

Основное, что я хотел подчеркнуть всем этим — это компромиссы. Мы совершенно точно можем избавиться от всех этих аппроксимаций, но придётся потратить на это наши запасы ресурсов, то есть пожертвовать чем-то другим. Каждый разработчик игр должен решать, над чем ему важно сосредоточиться в игре, и что будет наиболее впечатлять или сильнее всего раздражать игрока.

Часть 4. Двигаем вершины

В этой части я подробнее расскажу о технических деталях анимирования объектов в сцене.

3D-графика немного напоминает сборочный конвейер с чётко заданным перемещением от одного этапа к другому, однако она не работает одновременно только с одним объектом. Программисты графики часто говорят о «графическом конвейере» (graphics pipeline).

Разумеется, между разными производителями и семействами графических карт есть множество вариаций, но обычно нам не нужно беспокоиться о том, как они работают на этом уровне. Все современные графические карты имеют приблизительно одинаковый конвейер, у них есть специальное оборудование и ПО, прошитое непосредственно «в кремнии», для максимального ускорения работы конвейера.

Примечание

Если вам интересно, как всё работает на абстрактном аппаратном уровне, то рекомендую серию статей Фабиана Гисена о графическом конвейере. Эта серия статей намного подробнее и требует гораздо большего понимания, чем мой пост.

Я буду пропускать многие подробности, чтобы объяснить интересные и важные принципы. В этой части мы рассмотрим первую часть конвейера, которая называется «вершинным шейдером» (Vertex Shader).

Чтобы объяснить, что такое шейдеры, и сравнить их с тем, что было раньше, я расскажу о той работе, которую они выполняют. Шейдеры получили широкое распространение около 16 лет назад, после выпуска DirectX 9, в котором появились вершинные и пиксельные шейдеры.

Каркасная голова Эйдена снова с нами.

Эти модели состоят из отдельных точек, называемых вершинами, которые соединены в треугольники. Напомню, что в части 2, мы рассматривали модели, из которых создаётся игровой мир. Я сказал, что подробнее расскажу о них позже, и теперь выполняю своё обещание.

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

Для начала возьмём простой случай, не персонажа. Давайте рассмотрим простой пример того, чем нам предстоит заниматься — перемещение и расположение объектов в мире.

Эти объекты не создаются сразу в антураже Чикаго, скорее их окружение похоже на «пустую белую комнату» из «Матрицы». При создании объектов в 3D-редакторах наподобие Maya и 3D Studio Max художники всегда строят из в собственном отдельном мире. Каждый объект располагается в центре абсолютной пустоты.

Вот какой-то светофор, находящийся в игре где-то под рельсами, и он расположен в собственном мире.

Это значит, что когда наступает время отрисовки объектов, нам нужно переместить её из собственного мира в сцену, которую мы отрисовываем. При сохранении на диск модель не имеет никакого представления о том, где будет находиться в мире и когда она будет загружена и передана в графическую карту. Это происходит с каждым отрисовываемым объектом, и даже такие неподвижные объекты, как здания и мосты тоже в каждом кадре перемещаются из собственного мира в сцену.

Здесь мы видим несколько светофоров, уже размещённых в частично построенной финальной сцене.

Мы не можем просто сказать графической карте: «Можешь поместить этот светофор под мост? Как я упоминал выше, единственно, с чем мы можем работать — это вершины. Отлично!» А потом поставить ещё один чуть дальше?

Оказывается, что в таком случае всё становится очень просто. Поэтому вместо того, чтобы говорить графической карте, что ей нужно делать с самим объектом, нам нужно сказать, что делать со всеми его вершинами. Всё, что нам нужно — это разобраться с тем, что такое «изменение», которое называется преобразованием. Если мы меняем все вершины абсолютно одинаково и они остаются относительно друг друга неподвижными, то это аналогично перемещению объекта как целого.

Примечание

Математические расчёты всего этого не особо сложны, но выходят за рамки статьи. Если вы уже изучали линейную алгебру, то, вероятно, знаете всё необходимое — в основном всё сводится к матричному умножению векторов.

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

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

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

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

Это возвращает нас к принципу «если переместить каждую вершину, то это аналогично перемещению всего объекта». На показанной выше анимации видно, что персонажи как-то странно искажены. Внесённое в вершинный шейдер изменение работает только для одной вершины за раз, но поскольку все они работают с одинаковым искажением, эффект в результате применяется ко всему объекту.

Это не симулируется с помощью физики, поэтому никак не связано с гравитацией или коллизиями с другими объектами — при желании мы можем заставить всех парить в воздухе. Можно также внести изменения в часть преобразования, ответственное за «простое перемещение на место», чтобы вместо расположения персонажа в нужном месте она поднимала его над землёй.

В этой анимации мы заставили вершинный шейдер постепенно поднимать и опускать объект.

Только вместо поднятия и опускания они бы качались в направлении ветра. Очевидно, что всё это не слишком конструктивно, однако даёт нам общее представление о том, как работают вершинные шейдеры — если бы теперь мы хотели анимировать листья на деревьях, чтобы они раскачивались, то сделали бы примерно то же самое. Затем мы можем менять силу ветра, чтобы деревья раскачивались сильнее или слабее.

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

Исключение составляют все анимированные объекты — люди, животные, а также такие предметы, как качающиеся верёвки, развевающаяся одежда и т.д. Я немного упрощаю, но в основном задачей большинства вершинных шейдеров и является «перемещение объектов на место».

Анимирование людей связано с идеей применения скелета и «скина». Скелет — это простое описание подвижного персонажа. На этом этапе нас волнуют не анимированные пряжки, шляпы или джинсы — нам важны только важные движения, наиболее соответствующие скелету человека.

Здесь показан простой скелет фигуры человека. Он взят не из Watch Dogs, потому что скелет сложно визуализировать вне пределов 3D-редактора.

Изображение лицензировано по Attribution-ShareAlike CC BY-SA © MakeHuman team 2001-2014

Это поза привязки или T-поза, демонстрирующая, как выглядит персонаж без применения анимаций.

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

Во времена Half-Life 1, когда начала использоваться эта технология, каждая вершина связывалась только с одной костью. После создания скелета каждая вершина привязывается к одной или нескольким костям в этой статичной позе, и эта связь называется «скиннингом». Благодаря этому можно получать более плавную анимацию, позволяя костям пересекаться в разных областях без создания резких углов при движении рук или ног. В наши дни могут присоединяться даже к четырём костям, при этом каждой кости придаётся вес, от которого зависит степень влияния кости на вершину.

Это показанный выше скелет с повёрнутой костью бедра. Цвета показывают веса кости бедра относительно вершин модели.

Изображение лицензировано по Attribution-ShareAlike CC BY-SA © MakeHuman team 2001-2014

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

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

Если модель не соответствует используемому ею скелету, то анимации будут совершенно неверными.
Если мы сделаем персонажа раза в два шире, то получим эффект, похожий на «объевшегося пончиков Дрейка», но заметно, что ближе к кистям анимации становятся неправильными, потому что именно здесь они дальше всего от своих исходных позиций.

Обычно они вызываются тем, что к скелету применены неправильные анимации, или в модели используется неверный скелет. Первая анимация может показаться вам странно знакомой — подобные глитчи анимации возникают в играх очень часто. Как и в случае с текстурами и моделями, скелет, скиннинг и модель нужно очень тщательно сопоставлять, иначе результаты быстро окажутся печальными.

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


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Стартовал самый большой проект по уборке мирового океана

Его не увидеть из космоса и сложно разглядеть человеческим глазом. Несмотря на мнения многих скептиков (например, Артемия Лебедева), Большое тихоокеанское мусорное пятно реально существует. Но от этого вред экосистемам не уменьшается. Это не плавучий остров из мусора, который можно ткнуть ...

Любая интернет-компания обязана тайно изменить программный код по требованию властей

6 декабря 2018 года парламент Австралии принял Assistance and Access Bill 2018 — поправки к Telecommunications Act 1997 о правилах оказания услуг электросвязи. Говоря юридическим языком, эти поправки «устанавливают нормы для добровольной и обязательной помощи телекоммуникационных компаний правоохранительным органам и ...