Привет, меня зовут Тони Элбрект (Tony Albrecht), я один из разработчиков новой команды Render Strike Team под управлением Sustainability Initiative в League of Legends. Моей команде поручили внести усовершенствования в движок рендеринга LoL, и мы с радостью принялись за работу. В этой статье я расскажу, как движок работает сейчас. Надеюсь, она заложит хороший фундамент, на основании которого я позже смогу рассказывать об вносимых нами изменениях. Эта статья станет для меня хорошим предлогом самому поэтапно изучить процесс рендеринга, чтобы мы, как команда, полностью понимали, что же происходит внутри.
Я подробно объясню, как LoL выстраивает и отображает каждый отдельный кадр игры (не забывайте, что на самых мощных машинах это происходит более 100 в секунду). Рассказ в основном будет техническим, но я надеюсь, что его легко будет усвоить даже тем, кто не имеет опыта в рендеринге. Для ясности я пропущу некоторые сложные моменты, но если вы захотите узнать подробности, то напишите об этом в комментариях [к оригиналу статьи].
Сначала я немного расскажу об имеющихся у нас графических библиотеках. League должна работать как можно эффективнее на широком диапазоне платформ. На самом деле, сейчас Windows XP является четвёртой по популярности версией ОС, в которой запускают игру (популярнее только Windows 7, 10 и 8). На Windows XP ежемесячно играют в десять миллионов сессий игры, поэтому для сохранения обратной совместимости нам нужно поддерживать DirectX 9 и приходится использовать только функции, которые он предоставляет. Также мы используем сопоставимый набор функций OpenGL 1.5 на машинах с OS X (скоро положение изменится).
Итак, давайте приступим! Для начала мы узнаем, как же компьютеры на самом деле отрисовывают изображения.
Рендеринг для начинающих
В большинстве компьютеров есть ЦП (центральный процессор) и ГП (графический процессор). ЦП выполняет логику и вычисления игры, а ГП получает данные треугольников и текстур от ЦП и отображает их на экране как пиксели. Небольшие программы ГП, называемые шейдерами, позволяют влиять на способ выполнения рендеринга. Например, можно изменить способ наложения текстур на треугольники или дать ГП команду выполнять расчёты для каждого тексела в текстуре. Таким образом, мы можем просто накладывать текстуру на треугольник, добавлять или умножать несколько текстур на треугольнике, или выполнять более сложные процессы, такие как рельефное текстурирование, расчёт освещения, отражений или даже высокореалистичных шейдеров кожи. Все видимые объекты рисуются в неотображаемом буфере кадра, который отображается только после завершения всего рендеринга.
Давайте рассмотрим пример. Вот изображение Гарена (Garen), состоящее из 6 336 треугольников, составляющих «проволочный» каркас и сплошную бестекстурную модель. Эта модель создана нашими художниками и экспортирована в формат, который движок League может загружать и анимировать. (Заметьте, что у Гарена неплоское затенение: это ограничение приложения, используемого для исследования рендеринга).
Эта модель без текстуры не только скучная, но и не отображает узнаваемого Гарена. Чтобы вдохнуть в Гарена жизнь, нужно нанести текстуру.
Перед загрузкой текстуры Гарена хранятся на диске в виде файлов DDS или TGA, которые сами по себе выглядят как сцена из ужастика. После правильного наложения на модель у нас получится вот такой результат:
У нас уже начинает что-то получаться. Шейдер, рендерящий наши сетки со скиннингом, не просто наносит текстуру, но мы рассмотрим это позже.
Это были основы, но LoL нужно рендерить гораздо больше, чем модель и текстуру персонажа. Давайте рассмотрим этапы, составляющие рендеринг следующей сцены:
Этап рендеринга 0: туман войны
Прежде чем начинать прорисовку частей сцены, нужно сначала подготовить туман войны и тени (у-у-у, «туман и тени», как зловеще!). Туман войны хранится центральным процессором как сетка размером 128x128, которая потом масштабируется до квадратной текстуры 512x512 (подробнее об этом можно почитать в статье «A Story of Fog and War»). Затем мы размываем эту текстуру и наносим её для затемнения соответствующих областей игры и мини-карты.
Этап рендеринга 1: тени
Тени — неотъемлемая часть 3D-сцены. Без них объекты будут казаться плоскими. Для создания теней, которые выглядят, как отбрасываемые миньоном или чемпионом, нам нужно рендерить их из точки источника света. Расстояние от источника света до отбрасывающего тень персонажа хранится для каждого пикселя в компонентах RGB, и мы обнуляем компонент альфа-прозрачности. Это можно увидеть ниже. Слева у нас есть поле высоты теней в RGB осаждаемой башни, миньонов и двух чемпионов. Справа у нас есть только компонент альфа-прозрачности. Эти текстуры обрезаны для более чёткого отображения деталей теней — миньоны внизу, башня и чемпионы — наверху.
В конце мы размываем тени, чтобы придать им красивую плавную границу (вместе с недавно добавленной оптимизацией, повышающей частоту кадров). В результате мы получаем текстуру, которую можно наложить на статичную геометрию для получения эффекта теней.
Этап рендеринга 2: статичная геометрия
Имея подготовленные текстуры тумана войны и теней, мы начинаем отрисовывать в кадре остальную часть сцены. В первую очередь статичную геометрию (она называется так, потому что неподвижна). Эта геометрия сочетает информацию тумана войны и теней со своей основной текстурой, что даёт нам следующую сцену:
Заметьте, что тени миньонов и туман войны заползают на края сцены. Рендерер Ущелья призывателей (Summoner's Rift) не рендерит динамических теней для статичной геометрии. Поскольку основной источник света не перемещается, мы запекаем тени статичных сеток на их текстурах. Это даёт художникам больше контроля над внешним видом карты, а также позволяет повысить производительность (не требуется рендеринг теней статичных сеток). Тени отбрасывают только миньоны, башни и чемпионы.
Этап рендеринга 3: сетки со скиннингом
Итак, у нас есть рельеф и тени, поэтому мы можем начать накладывать на них объекты. Сначала накладываются миньоны, чемпионы и башни, т.е. все объекты с подвижными шарнирами, которые должны реалистично двигаться.
Каждая анимированная сетка состоит из скелета (каркаса из иерархически соединённых костей) и из сетки треугольников (см. выше изображение Гарена). Каждая вершина каждого треугольника привязана к одной-четырём костям, поэтому при перемещении костей вершины перемещаются с ними как кожа (skin). Поэтому их называют «сетками со скиннингом». Наши талантливые художники создают анимации и сетки для всех объектов, а потом экспортируют их в формат, который загружается в League при запуске игры.
На изображениях выше показаны все кости сетки Гарена. На изображении слева показаны все его кости (с названиями). На изображении справа голубым показаны выбранные вершины, а жёлтыми линиями показаны связи с костями, управляющие их положением.
Шейдеры сеток со скиннингом не просто рисуют сетки со скиннингом в буфер кадра, они также рендерят в другой буфер их отмасштабированную глубину, которую мы позже используем для отрисовки контуров. Кроме того, шейдеры скиннинга выполняют расчёт отражений Френеля, излучаемого освещения, вычисляют отражения и изменяют освещение для тумана войны.
Этап рендеринга 4: контуры (очерчивание)
По умолчанию очерчивание для сеток со скиннингом включено, что обеспечивает более чёткие контуры. Это позволяет выделить сетки со скиннингом на фоне, особенно в областях с низким контрастом. На изображениях ниже очерчивание отключено (слева) и включено (справа).
Контуры создаются получением отмасштабированной глубины из предыдущего этапа и её обработкой оператором Собеля для извлечения грани, которую мы рендерим на сетке со скиннингом. Эта операция выполняется отдельно для каждой сетки. Также существует метод возврата, использующий буфер шаблонов для графических процессоров, которые не могут выполнять рендеринг нескольких объектов одновременно.
Этап рендеринга 5: трава
Чтобы определить, что задействуется при рендеринге воды и травы, давайте посмотрим на другую сцену.
Вот кадр без воды и травы, просто статичная фоновая геометрия и несколько сеток со скиннингом.
Заметьте, что тени травы уже являются частью текстуры статичного рельефа и не рендерятся динамически. Затем мы добавляем траву:
Пучки травы на самом деле являются сетками со скиннингом. Это позволяет нам анимировать их при прохождении по ним персонажей и придать приятное колыхание от ветерка в Ущелье призывателей.
Этап рендеринга 6: вода
После травы мы рендерим воду с помощью полупрозрачных сеток со слегка анимированными текстурами воды. Затем мы добавляем листья кувшинок, рябь вокруг камней и у берега, насекомых. Все эти объекты анимированы, чтобы внести в сцену ощущение жизни.
Для усиления эффекта воды (он может быть слишком слабым) я сохранил прозрачность воды и проигнорировал геометрию под ней. Это подчеркнуло эффекты воды, чтобы мы могли лучше учитывать их в анализе.
Выделив всю рябь как «проволочные» каркасы, мы получим:
Теперь мы чётко можем видеть эффекты воды по берегам реки, а также вокруг камней и кувшинок.
При нормальном рендеринге и анимации вода выглядит следующим образом:
Этап рендеринга 7: декали
После наложения травы и воды мы добавляем декали — простые геометрические элементы с плоским текстурированием, которые накладываются поверх рельефа, например, индикатор дальности действия башни на рисунке ниже.
Этап рендеринга 8: особые контуры
Здесь мы имеем дело с более толстыми контурами, включаемыми через события мыши или особыми состояниями активации, как в случае контура башни на рисунке ниже. Это делается почти так же, как создавались контуры сеток со скиннингом, но здесь мы ещё и размываем контуры, чтобы сделать их более толстыми. Такое выделение заметно ещё сильнее, потому что выполняется в процессе рендеринга позже и может перекрывать уже наложенные эффекты.
Этап рендеринга 9: частицы
Следующая стадия — одна из самых важных: частицы. Я уже писал о частицах в этой статье. Каждое заклинание, бафф и эффект — это система частиц, которую нужно анимировать и обновлять. В рассматриваемой нами сцене не так много действия, как, например, в командном бою «5 на 5», но всё равно здесь довольно много отображаемых частиц.
Если мы рассмотрим только частицы (отключив всю фоновую сцену), то получим следующую картину:
Отрендерив треугольники, составляющие частицы, фиолетовыми контурами (без текстур, только геометрию), мы получим следующее:
Если отрисовывать частицы нормально, то мы получим более знакомый вид.
Этап рендеринга 10: эффекты постобработки
Итак, базовые части сцены уже отрендерены и мы можем придать ей немного больше «блеска». Делается это в два этапа. Сначала мы выполняем проход сглаживания (anti-alias, AA). Он помогает сгладить зазубренные края, делая весь кадр более чётким. В статичном изображении этот эффект почти незаметен, но он сильно помогает в устранении «мерцания пикселей», которое может возникать при перемещении высококонтастных граней по экрану. В LoL мы используем алгоритм сглаживания с быстрой аппроксимацией Fast Approximate Anti-Aliasing (FXAA).
Изображение слева — это миньон до FXAA, а справа — после сглаживания. Заметьте, как сглаживаются края объекта.
После завершения прохода FXAA мы выполняем проход гамма-коррекции, позволяющий отрегулировать яркость сцены. В качестве оптимизации мы недавно добавили эффект снижения насыщенности экрана смерти в проход гамма-коррекции, что позволило избавиться от необходимости замены всех шейдеров текущих видимых сеток для вариантов смертей, у которых раньше насыщенность снижалась отдельно.
Этап рендеринга 11: урон и полоски здоровья
Затем мы рендерим все игровые индикаторы: полоски здоровья, текст урона, экранный текст, а также все полноэкранные эффекты, не относящиеся к постобработке, такие как эффект урона на изображении ниже.
Этап рендеринга 12: интерфейс
И, наконец, отрисовывается интерфейс пользователя. Все тексты, значки и предметы отрисовываются на экране как отдельные текстуры, перекрывая всё, находящееся под ними. В анализируемом нами случае на отрисовку интерфейса потребовалось примерно 1 000 треугольников — около 300 на мини-карту и 700 — на всё остальное.
Собираем всё вместе
И мы получаем полностью отрендеренную сцену. Во всей сцене содержится около 200 000 треугольников, 90 000 из них используется под частицы. 28 миллионов пикселей отрисовываются за 695 вызовов отрисовки. Чтобы в игру можно было играть, вся эта работа должна выполняться как можно быстрее. Чтобы достичь 60 и более кадров в секунду, все этапы нужно пройти менее чем за 16,66 миллисекунд. И это только расчёты на стороне графического процессора: вся игровая логика, обработка ввода игрока, столкновения, обработка частиц, анимации и отправка команд на рендеринг тоже должны выполняться за это же время в центральном процессоре. Если вы играете с 300 fps, то всё происходит меньше чем за 3,3 миллисекунды!
Зачем выполнять рефакторинг рендерера?
Теперь вы должны представлять сложности, связанные с рендерингом единственного кадра игры League. Но это только сторона вывода данных: то, что вы видите на экране — это результат тысяч вызовов функций нашего движка рендеринга. Он постоянно изменяется и эволюционирует, чтобы лучше соответствовать современным потребностям рендеринга. Это привело к тому, что в базе кода League сосуществуют разные формы кода рендеринга, потому что нам нужно учитывать новое и поддерживать старое оборудование. Например, Ущелье призывателей (Summoner’s Rift) выполняет рендеринг немного иначе, чем Воющая бездна (Howling Abyss) и Проклятый лес (Twisted Treeline). Существуют части рендерера, оставшиеся от старых версий League, и части, которые пока так и не раскрыли весь свой потенциал. Задача команды Render Strike Team — взять весь код рендеринга и произвести его рефакторинг, чтобы весь рендеринг выполнялся через один и тот же интерфейс. Если мы хорошо выполним свою задачу, то игроки совершенно не заметят разницы (кроме, возможно, небольшого увеличения скорости в разных моментах). Но после того, как мы закончим, у нас появится отличная возможность вносить одновременные изменения во все игровые режимы League.
Надеюсь, эта экскурсия по процессу рендеринга League of Legends была интересной. Я упомянул в начале статьи, что не хотел вдаваться в подробности. Я стремился к тому, чтобы как можно больше людей начало лучше понимать процессы, происходящие в каждом кадре каждой игры в League. Если у вас возникли вопросы, задавайте их в комментариях [к оригиналу статьи], и мы постараемся ответить как можно подробнее.
Комментарии (19)
Hellsy22
22.01.2017 11:49+11Это совершенно типовое описание, которое можно наложить на любую игру с 3d-движком за последние 15 лет.
saboteur_kiev
23.01.2017 02:36-1В скольких 3д играх за последние 15 лет, поддерживается сразу 4-5 графических движков только для того, чтобы она запускалась на разных машинах?
В статье пусть не много, но достаточно интересных, малоизвестных фактов.
MonkAlex
22.01.2017 13:13Во всей сцене содержится около 200 000 треугольников, 90 000 из них используется под частицы...
Кто то может сказать, много это или мало? Ну и время интересует, есть что-то реально сложное в отрисовке за такое время или нет?arabesc
22.01.2017 23:57+1Нормально для быстрого рендера времён DX9.
Тут интересно:
28 миллионов пикселей отрисовываются за 695 вызовов отрисовки.
28 миллионов — большая цифра. Если принять экран за 2 мегапикселя, то он перерисовывается, условно, 14 раз, но, само собой, что-то уходит на тени и отражения. Видимо, много полноэкранных постэффектов.
А 695 вызовов отрисовки хороший показатель.
Nonameface
22.01.2017 13:16+2А мне, например, было интересно, поскольку я не очень разбираюсь в графике.
И то, что это сделано на примере популярной игры, только добавило ясности. Поэтому не очень понимаю претензии к тексту. Думаю, именно из-за общедоступности его выложили тут, а не на Хабре.Hellsy22
22.01.2017 23:16Это типа как камингаут, но для IT-компании. Заявление типа: «Да, мы, компания с многомиллионными доходами, зачем-то сделали свой движок, безнадежно отстающий во всем и ото всех, но мы гордимся этим!»
Не удивлюсь, если они еще и модели с анимацией хранят в колладовском XML — только это может объяснить столь чудовищное время загрузки.
Впрочем, по сравнению с лобби, написанном под Adobe AIR…Neris
23.01.2017 03:00Медленная загрузка это проблема серверной части. Попробуйте зайти в кастомную игру без других игроков, загрузится почти моментально.
Лобби они тоже уже переписали с нуля, доступно в открытой бетке всем желающим.Hellsy22
23.01.2017 03:21Медленная загрузка это проблема серверной части
Раньше даже отдельно показывался прогресс загрузки для каждого участника. При чем тут серверная часть?
Лобби они тоже уже переписали с нуля
На чем?Neris
23.01.2017 10:24Раньше даже отдельно показывался прогресс загрузки для каждого участника. При чем тут серверная часть?
Этонаенеправильная загрузка. Стоят два компа рядом, каждый показывает на своем экране, что уже загрузился, а сервер отдает ~10%. Просто видимо серверные ресурсы готовятся медленно и чтобы народ не нервничал, они показывают, что у кого-то из участников деревянный комп.
mirrr
22.01.2017 16:37Довольно странно выглядит гифка, где расходятся круги от статичных камушков, но не от чувака, болтыхающегося в ручье. Сколько лет этой игре?
FirExpl
23.01.2017 07:37Этой игре уже больше 7 лет. Собственно, видимо именно по этому они взялись за глубокий рефакторинг движка игры, чтобы было проще обеспечивать современное качество картинки.
saboteur_kiev
23.01.2017 16:45Вообще-то там достаточно регулярно переделывают движок, буквально в позапрошлом году было глобальное обновление всей графики, после чего стали обновлять модельки чемпионов. Новые модельки действительно богаты на эффекты и возможности.
Kalobok
26.04.2017 17:48+1Надо сравнивать не «у меня есть холодильник, который работает 50 лет, а новый сломался через день», а «какой процент холодильников разного года выпуска способен проработать 50 лет». Правда, современные холодильники мы сможем оценить еще не скоро, но, думаю, принципиальной разницы не увидим.
Vsevo10d
26.04.2017 17:49Я не аллергик, поэтому подскажите мне, пожалуйста: инсулинкой за 10 центов уколоть лекарство — не? Или фишка только в том, что лекарство кроме как в такие шприцы не расфасовано?
kosmos89
26.04.2017 17:48>И РФ достаточно часто пользуется этим правом.
Теперь пользуется. А в 2012 — не очень. В 2012 Россия воздержалась по этому вопросу. Но это не значит, что она согласна. Это могла быть политическая игра или просто недостаточность информации о том, что там на самом деле произошло. Не опровергнуто — не значит, что доказано.foxin
26.04.2017 17:49Нету там лока. Для того же ми бэнда есть куча сторонних прилаг. Был бы лок, если бы встроили шифрование и проприетарный протокол. Но этого нету.
foxin
26.04.2017 17:48Потому что нативное приложение умеет работать с нотификациями. С вебмордой всё плохо: закрыли браузер — связи нету.
iig
26.04.2017 17:49Цена определяется рынком, а не себестоимостью.
И рынком и себестоимостью.
green_worm
26.04.2017 17:52Более того, для таких процессов еще в Германии перед второй мировой (могу ошибаться, возможно, первой) придумали крекинг углеводородов чтоб было топливо для завоеваний на танках. Процесс требует высоких температур и очень дорогих катализаторов (на сколько помню, платина и палладий). Очень дорогой процесс. Так что, если и такие организмы появятся, расщепляющие и только слегка окисляющие исходные компоненты, то будет очередной нефтяной кризис, как в случае со сланцевыми залежами и появлением технологии их добычи.
Только этого не случится, т.к. все-таки гусеницы — это аэробные организмы, а там где нефть залегает с воздухом не очень. ;)oxothuk_uae
26.04.2017 17:54респект, за «тёплый ламповый»
появление проигрывателя привело к необходимости добавить входы к усилителю и удобную коммутацию, что в свою очередь вызвало необходимость чуть-чуть переделать конструктив, ну а там — одно неосторожное движение, и усилитель опять в виде набора железок и дощечек, надеюсь ненадолго.
dimm_ddr
26.04.2017 17:49Вы уверены, что недолговечность современных устройств из-за планового устаревания, а не из-за того, что надежность принесли в жертву другим параметрам? Неоднократно, например, натыкался на мнение, что ту же легендарную неубиваемую нокию сейчас не повторить потому что с железом того времени она никому не нужна, даже если бы его кто-то производил, а с современным настолько неубиваемой ее просто не сделать. Я не говорю что именно так, но современную ситуацию это описывает достаточно хорошо.
Более того, как довод против запланированного устаревания можно привести современные светодиодные лампы, которые служат дольше современных же ламп накаливания. Выгоднее было бы продавать лампы накаливания и дальше.
Marwin
26.04.2017 17:49+1А что им мешает выпускать совместимые версии своего антивируса? Бета версии Винды доступны всем желающим. Я вижу только жмотство и нежелание тратить бабло на доп. усилия по адаптации продукта к бета ветке винды, чтобы при последующем апдейте заранее быть совместимым.
SuperZveruga
26.04.2017 17:51-2Описанные вами события не отменяют факта несогласия иностранных государств работать с нашими законами.
Факт остаётся фактом, иностранные государства прячут воров укравших миллиарды у российских граждан. По этой причине никаких договорённостей об обоюдной борьбе с преступностью не может быть и речи, пока иностранцы не пойдут на двухстороннее соглашение.
dartraiden
26.04.2017 17:51+6Пресс-служба «Лаборатории Касперского» разослала опровержение:
Слова Евгения Касперского в интервью Reuters были неверно истолкованы. Евгений лишь выразил надежду, что Microsoft продолжит вносить соответствующие изменения и в дальнейшем. Вместе с тем намерения «Лаборатории Касперского» подать жалобу в Европейскую комиссию остаются в силе. Рассмотрение дела в ФАС России также продолжается.
dartraiden
26.04.2017 17:52Они утверждали, если мне не изменяет память, что времени на адаптацию не хватает. Тем более, что от беты к бете вносятся различные изменения.
Altaisky
26.04.2017 17:49ИИ, по сути, не понимает, что происходит
дальше можно не читатьvvatest
26.04.2017 17:51Мы ушли сильно в сторону, но да ладно. 99-ФЗ говорит о том, что детали лицензирования определяются какими-то другими документами. Конкретно по крипте и защите информации — это документы ФСБ и ФСТЭК. И там эти детали расписаны. Я не вижу смысла сейчас углубляться дальше в диспут. Суть в том, что 99-ФЗ регулирует деятельность ЮЛ и ИП в процессе ведения ими предпринимательской деятельности, больше никогда и никому лицензии требовать не разрешает. ФЗ «О связи» предусматривает предоставление лицензий только операторам связи, более никому. Правоприменительной практики и официальных разъяснений нет ни у вас, ни у меня, потому что объект диспута весьма умозрительный. Хотя еще совсем недавно педалировалась тема о том, что кафешки и отели можно притягивать под лицензирование как организаторов пунктов коллективного доступа. Но роскомнадзор разумно представив объем КЛД ограничился указаниями.
devlind
26.04.2017 17:55+19Наплевать, серьёзно? Страховые компании что благотворительностью занимаюстся, или сами деньги печатают? В конечном итоге за всё платит юзер. Выростет стоимость страховки и всё. А она там и так не маленькая.
И если у вас сегодня страховка стоила 300$/мес, то завтра с такими расценками — 500$/мес.Blast
28.04.2017 01:23+1«Чем геймерские мыши отличаются от обычных» и ни слова про flawless сенсоры, при том что PMW3310 является таковым?
tnenergy
26.04.2017 17:55+2Оттуда, что на вводе 800 кВ. Если на первом моторе падает, скажем, 10 кВ, то на выходных клеммах его будет 790, а на корпусе тоже ~790 кВ. От которых надо изолировать все вокруг, в т.ч. ротор (или ротор должен быть непроводящим).
Кстати, ротор на 500 мегаватт на сотню двигателей скорее всего нереализуем из-за недостаточной жесткости материалов (будут крутильные и поперечные колебания, которые все разнесут)
И т.д. и т.п.
mphys
Достаточно примитивно для «технического» рассказа
RedVelvet
Были статьи про рендер кадра игр GTA V и DE:HR, по сравнению с ними данный топик выглядит очень уныло.