{"id":2335,"title":"Lineage II \u0441\u0442\u0430\u043b\u0430 \u043c\u0435\u043d\u0435\u0435 \u0445\u0430\u0440\u0434\u043a\u043e\u0440\u043d\u043e\u0439 \u2014 \u0447\u0442\u043e \u0432 \u043d\u0435\u0439 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c?","url":"\/redirect?component=advertising&id=2335&url=https:\/\/dtf.ru\/promo\/788837-avtoohota-komfortnaya-solo-igra-i-novyy-kontent-chem-lineage-2-essence-zainteresuet-veteranov-a-chem-novichkov&hash=4a14f156608748239cb5bb252e747dad2aa7047403eca51fa412385a34e5b278","isPaidAndBannersEnabled":false}
Денис Кузнецов

Оптимизация в UE4. Базовые советы

Всем привет и с новым годом! Наступили новогодние праздники, и у меня наконец-то появилась возможность выделить 2-3 дня на новую статью =)

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

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

Красивая картинка из нашей игры Cat Movies!

В этой статье мы поговорим о:

  • Frame per Second. Немного о самом понятном.
  • Объекты в игре, и как с ними стоит работать. Какие есть очевидные и не очевидные подводные камни.
  • Шейдеры, и какие есть нюансы с ними.
  • Draw Calls. Что это такое, зачем оно нужно и почему его нужно бояться (или не бояться).

Frame per Second.

Самое простое, с чего мы начнем в этой статье - это базовые понятия Frame Per Second и Milliseconds.

Понятно, что Frame Per Second (далее FPS) - это количество кадров в секунду. Так же понятно и то, что количество кадров определяется временем обработки этих кадров, которое измеряется в миллисекундах. Так, например, чтобы добиться 60 кадров в секунду, вам надо, чтобы ваша игра обрабатывалась в среднем за один проход в 16мс (в одной секунде 1000 мс).

Но почему именно 16мс? Почему не 30мс, например? К чему такое стремление? Ведь в фильмах 24-29 кадров используют, и все нормально, а в играх 30 кадров - это плохо.

Это такая очевидная вещь, что до одной из статей о геймдеве я никогда не задумывался, а почему все хотят 60 кадров в секунду, и почему 30 кадров в секунду в играх это не круто, а в кино - этого не замечаешь, и картинка плавная.

Все оказалось очень просто - в фильмах кадр создается с учетом движения. Камера фиксирует не только текущее положение объектов, но и их смещение за время захвата кадра. То есть, кадры на пленке никогда не бывают идеально чистыми - все объекты так или иначе смазаны в движении, и когда это проигрывается - создается ощущение плавности движения даже при 24 кадрах в секунду. А при новых технологиях и 120 кадрах в секунду (фильм "Gemini" был первым, кто показал в таком FPS ) и вовсе кажется, что смотришь какую-то реальную сцену в театре, а не в кино.

В играх же ситуация кардинально другая. Каждый кадр отрисовывается с нуля без учета движений. То есть, все объекты в этом кадре всегда статичны, и кадр не фиксирует движение объектов за отведенное ему время. В итоге 30 игровых кадров в секунду кажутся дерганными, недостаточными для глаза, чтобы довольствоваться картинкой, и отсюда стремления к 60 кадрам и выше.

Да, это очевидная штука, которой мне не хватало когда-то, чтобы прочувствовать саму суть проблемы. Оно было интуитивно понятно, но вот так вот четко я не встречал описания до определенной статьи. Может быть, у вас так же будет и с этой =)

В VR играх требуется 90 кадров в секунду. Тут требование закономерно. Если в 30 кадров в секунду вы играете и видите дергания, и это в целом нормально, то в VR вас не окружает квартира/офис или где вы играете. В VR вас окружает только игра и черный фон. Поэтому чувствительность к количеству кадров возрастает на порядок, а возможность вертеть головой в пространстве создает дополнительный дискомфорт при низком FPS. А некоторых и вовсе выворачивает.

Объекты в игре.

Что касается объектов в игре, то здесь очень много нюансов, но все они сводятся к 2 моментам:

  • Вертексы. Количество вертексов должно быть разумным. Помните, что создавая швы на развертке, окрашивая фейсы по Vertex Color'у, делая модель из жестких граней - вы пропорционально увеличиваете количество вертексов на ровном месте. Все должно быть в меру, взвешено и каждое решение должно быть обдумано.
  • Треугольники. Как мы все знаем - все полигоны (4хугольные n-угольные) все равно делятся видеокартой (а перед ней и самим движком) на треугольники. И если вы не порезали нужные вам полигоны заранее, за вас это сделает движок в рандомном направлении.

Вертексы.

Количество вертексов обременяет ваше железо дополнительной обработкой. Тут все просто - чем больше вертексов, тем больше нужно учесть информации для корректного отображения модели.

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

И мы все прекрасно понимаем, что на швах развертки точки так же расщепляются.

Расщепление вертексов - очень интересная тема и при не умелом обращении может создать вам дополнительно х2 точек. Важно помнить всего 1 правило, которое позволит вам чуть лучше управлять моделью:

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

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

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

Так же помните, что второй и последующие слои развертки так же увеличивают количество вертексов.

Треугольники.

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

Я недавно столкнулся с тем, что не все понимают, почему программы и видеокарты обрезают любые n-гоны до треугольников. Поэтому немного помяукаю здесь об этом:
Дело в том, что полигон (поверхность с 4 точками) и n-гон можно погнуть. Вы можете взять любую одну точку и изменить ее положение, и вот полигон уже согнулся пополам, и не понятно, где проходит сгиб, а где должна быть цельная поверхность.
Треугольник вы не сможете погнуть. Как бы не старались - все три точки всегда будут в одной плоскости - в плоскости треугольника. Поэтому треугольник - это самая примитивная форма, с которой начинается приключение в сложный трехмерный мир.
Мяу-Мяу.

Котик
Треуголовед

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

Казалось бы, круто, понятно, нужно делать меньшее количество треугольников (полигонов и т.д.) и рендер будет быстрее обрабатывать модель. Но это не совсем так. Размер и положение треугольника так же важно, как и то, как собрана модель.

Дело в том, что GPU (графический процессор видеокарты) обрабатывает пиксели вашего монитора группами - по 4 пикселя. То есть, нельзя обработать 1 пиксель для отображения треугольника, если он очень узкий или заканчивается на каком-то пикселе - всегда будет запущен цикл обработки на 4 пикселя.

А теперь представьте, что у вас модель имеет следующую сетку:

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

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

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

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

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

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

Но здесь стоит учитывать 2 момента:

  1. Фаски не сводят к одной точке 100 треугольников, а только 2. А значит, это небольшая нагрузка на рендер.
  2. Если количество объектов с фасками в камере очень большое, то на помощь приходят LOD'ы.

LOD'ы моделей.

LOD расшифровывается, как Levels Of Detail - уровни детализации. Это копии ваших моделей, но с уменьшенным количеством вертексов, которые заменяют вашу модель на отдаленном расстоянии.

В UE4 они генерируются автоматически. Ну, то есть, нужно нажать на кнопки, но руками модель править не нужно, и это ускоряет процесс до пары кликов.

Например, у нас есть вот такая красивая лампа, которая сжирает 58 594 вертекса.

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

Мы создали LOD'ы для этой лампы, которые автоматически подменяют модели на менее детализированные. А вы и не заметите, что у лампы на заднем фоне всего 5648 вертексов.

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

Без LOD'ов в этом кинотеатре около 7 миллионов вертексов:

С LOD'ами на таком расстоянии кинотеатр имеет около 800 тысяч вертексов. При отдалении камеры их будет в разы меньше вплоть до 300 тысяч.

В UE4 можно так же посмотреть какие уровни LOD'ов сейчас активны и где вообще они используются:

Что касается проверки на загруженность треугольниками рендера - это можно в 2 клика проверить в UE4, включив отображение оптимизации сетки:

Culling. Обрезание отрисовки.

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

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

Поэтому большие объемные объекты должны быть собраны таким образом, чтобы их можно было нарезать на куски.

Например, так наш кинотеатр отображается на камеру:

А вот так сработал Culling объектов, и кинотеатр был обрезан для рендера:

Чтобы заморозить рендер и посмотреть, как происходит обрезка в вашей игре и вообще как зафиксировался кадр с LOD'ами и прочим - введите в консоль команду "FreezeRendering". Повторное использование команды приведет к возврату в нормальное состояние.
Мяу-Мяу.

Котик
Командовед

Итого.

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

LOD'ы позволяют нам снижать нагрузку на рендер.

Оптимизация Вертексов позволяет нам снижать расчеты для отображения модели.

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

Шейдеры.

Шейдеры в UE4 - в первую очередь это материалы. То, в чем вы создаете расчеты текстур и накладываете на объекты, чтобы они смотрелись красиво, стилизовано, реалистично или как-то еще.

В UE4 есть множество способов оптимизировать шейдеры. Но я рассмотрю только основные моменты.

Количество занимаемых пикселей на экране.

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

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

При этом, если мы увеличим объект до 100% площади экрана - мы получим самый долгий рендер кадра.

Иначе говоря, чем больше площадь объекта, тем меньше нагруженным должен быть шейдер.

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

И нет, это не значит, что теперь на карандашик, который занимает 20 пикселей, вы можете наложить суперсложный материал. Однажды в какой-нибудь кат-сцене он может быть ключевым объектом в кадре, и ваш GPU вас поблагодарит.

Сложность шейдера.

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

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

Нужно понимать, что если вы делаете материал с разделением на 2 и более объектов по UV координатам (так называемые атласовые шейдеры с атласовыми текстурами), а в кадр попал только 1 объект, то все равно ВЕСЬ код будет просчитан для отображения пикселя и загружены будут все текстуры, даже которые не участвуют в текущей работе шейдера.

В связи с этим нужно несколько раз подумать, а стоит ли запихивать несколько объектов в один шейдер? Может быть, проще будет, если на каждый объект будет по собственному шейдеру?

Инстансы и параметры.

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

Как это работает?

Для этого в шейдерах существует тип данных называемый "Параметром". Этот тип данных позволяет динамически менять что-то в шейдерах, например, цвет поверхности, при этом не просчитывая с нуля весь шейдер.

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

Например, вы создали шейдер, который базово можно наложить на любой объект. Вы добавили в него параметр текстуры (можно заменять текстуру) и параметр цвета, который будет изменять цвет текстуры.

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

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

В шейдерах можно использовать константные значения (те, которые нельзя изменять в реальном времени) и параметры (динамические значения, которые можно изменять в реальном времени).

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

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

Поэтому с момента, как в коде появляется параметр, шейдер начинает делиться на 2 куска кода - тот, который он может просчитать и зафиксировать железно и тот, который он не может предугадать.

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

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

Что касается инстансов, то у них есть другая проблема - это параметры текстур. Когда вы создаете параметр текстуры в шейдере, то там обязательно требуется указать какую-нибудь текстуру. А когда вы заменяете текстуру в инстансе на другую, то первая никуда не пропадает.

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

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

Помните, что максимальное число текстур равно 16 штукам. И это вместе с текстурами из базового шейдера и используемого инстанса.

Для всех устройств, что работают на DirectX11 и выше, вы можете указать шейдерах в настройках ноды текстуры Sampler Source на Shared: Wrap, и вам будет доступно 128 текстур с такой настройкой для работы в шейдере. Но это только в случае с работой над проектами, которые работают в DX11 и выше. Вулканы, OpenGL'ы работают в пределе 16 текстур. Поэтому будьте аккуратны в выборе.


Мяу-Мяу.

Котик
числотекстуровед

Итого.

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

Главное, помните. Шейдеры - это та штука, к которой нужно подходить максимально осознанно и обдуманно. Иногда стоит посмотреть на текстуры и сделать заготовки чего-то, чем писать динамический код, который будет делать всё то же самое, но в 5 раз дороже. А иногда нужно поискать решение, которое позволит вам быть гибче и легче.

Draw Calls.

Draw Calls - это набор команд по отрисовке объектов. Каждый объект требует 2 действий для отрисовки:

  1. Обработка объекта для рендера.
  2. Просчет текстуры/шейдера для отображения объекта.

Переключение между этими действиями требует времени и сил CPU и GPU одновременно. И чем больше у нас объектов, чем больше у нас отдельных текстур, тем больше у нас Draw Calls. А чем больше DC, тем меньше FPS.

В первую очередь DC нагружает CPU. Условно говоря, процессор обрабатывает заявки на рендер, составляет список объектов, которые должны отрисоваться за один прогон и отправляет список видеокарте, которая быстро рисует меши. Потом процессор определяет шейдер, который должен наложиться на эти объекты и снова отправляет информацию в GPU. А потом заново с последующими объектами.

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

Когда-то там в далекие года средним значением DC было около 1к запросов. Это считалось нормой, при которой центральный и графический процессоры могли комфортно обрабатывать модели и отображать их, чтобы не проседал FPS.

В современной игре это значение поднимается далеко за 3к и может превышать 5к (GTA V), или же вовсе быть за пределами 40к (Watch Dogs 2). Например, среднее значение в Ведьмаке 3 - это 3.5к, иногда выше, иногда ниже.

И, наверное, вы хотите спросить - "и что, они используют всего от 3.5 до 5 тысяч объектов?". Нет, это далеко не так.

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

Можно было создавать текстурные атласы (о них я рассказывал в 4 части цикла "Текстурирование", которые можно найти у меня в профиле на DTF), в которые засовывались 10-ки текстур для различных объектов. И тогда эти 10 объектов можно было засунуть в один DC, а потом сверху натянуть 1 большую атлас-текстуру.

Проблема этой статьи в том, что она устарела технически лет на 10. Сейчас используется в основном PBR (рендер, основанный на физике), и на объекты накладывают шейдеры, в которых мы можем использовать до 16 текстур одновременно для создания красивой поверхности объекта. Таким образом, атласы текстур перестали иметь свою ценность, так как теперь используются комплексные куски кода с ссылками на текстуры, а не текстуры напрямую.

Однако, это не значит, что мы не можем срезать углы.

Instance Meshes.

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

Но стоит отметить, что это работает только с одним объектом. Вы не можете загнать в один шейдер текстуры для двух разных объектов (например, куб и конус) и рассчитывать, что у вас будет всего 1 DC. Их будет минимум 3:

  • Отрисовка первого объекта.
  • Отрисовка второго объекта.
  • Наложение шейдера на первый и второй объект

Но. Если у вы используете динамичный шейдер (инстансы или шейдер с параметрами), то тогда у вас будет 4 DC, так как добавится наложение шейдера на второй объект отдельно.

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

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

Для начала определим, что 1 DC может быть для 1000 объектов, если это копия одного и того же объекта. Теперь, зная это мы можем расставить на сцене 1000 одинаковых объектов, и они все будут отрисованы под одним DC с некоторыми оговорками:

  • Объекты должны быть указаны, как статичные (Static). Если какой-то из объектов динамический, то он будет учитываться отдельно. Dynamic и Stationary объекты не группируются для DC, и каждый отрисовывается отдельно.
  • Один шейдер для всех объектов. Если там есть параметры, то они не должны отличаться и/или редактироваться в реальном времени.
  • Можно использовать компонент Instance Static Mesh для явного указания, какие объекты должны быть отрисованы в один DC.

Если использование компонента Instance Static Mesh насильно запихивает все копии объекта в один DC, то использование расставленных вручную статичных объектов и одного шейдера не гарантирует вам одного DC. И это связано с оптимизацией работы рендера и обработки объектов.

Движок UE4 - умная штука. Он определяет оптимальное количество статичных одинаковых объектов, которое можно записать в один DC и отправляет их через CPU в GPU. Это необходимо, чтобы CPU не перегрузился большим списком объектов, которые ему нужно обработать прежде, чем отправить GPU.

В связи с этим, небольшое количество одинаковых объектов (в пределах 10-20 штук) имеет смысл не засовывать в Instance Static Mesh (далее ISM), чтобы у движка был самостоятельный выбор - как корректно это обработать.

Instance Static Mesh Component.

Просто расстановка 100-1000 одинаковых статичных объектов без использования ISM приводит к другим проблемам - скорей всего все объекты будут отрисованы под 2-5 DC (и это хорошо), но они будут занимать память каждый отдельно (а это не хорошо).

Иначе говоря. Если мы возьмем, к примеру, кубики земли из нашей игры (на которых стоят наши отделы) и просто раскопируем обычными статическими объектами по игре, то DC мы сохраним в пределах 1-2 значений, а вот поле из 300х300 кубиков сожрет 7 гигабайт оперативной памяти. Без шуток =)

ISM же позволяет нам не только экономить насильно в DC, но и экономить память, так как 1 компонент ISM может хранить себе только 1 объект и занимает память соответственно 1-му объекту. Даже если мы создадим поле 300х300 кубиков, в оперативной памяти это будет все еще равно 50 кбайтам.

Но ISM имеет свой недостаток. Он жестко ограничивает DC. То есть, нельзя будет поделить группу объектов на 2 или 3 DC - все будет подгружено через один DC, а значит, вам нужно убедиться, чтобы процессор не захлебнулся в данных, которые ему нужно подготовить для отправки GPU. Возможно, имеет смысл разделить на несколько компонентов, если количество вертексов у копируемого объекта очень большое.

Так же ISM не позволяет объектам переключать свои LOD'ы на расстоянии. Поэтому для массового использования объектов существует второй компонент Hierarchical Instance Static Mesh Component (далее HISM). Он подгружает не только основной объект, но и LOD'ы этого объекта и корректно переключает их.

Hierarchical Instance Static Mesh имеет свои сложности и не всегда корректно работает. Так, например, мы пока не решили проблему с ошибкой, когда генерируется наше игровое поле из кубиков на карте. При игре в редакторе все хорошо работает, но если собрать готовую игру, то в ней выпадает ошибка при генерации кубиков, и игра просто не работает.Поэтому временно нам приходится использовать ISM.Как только найдем ошибку, то обязательно расскажем вам, если это, конечно, будет интересно =)
Мяу-Мяу.

Котик
Ошибковед

Ну и на примере поля 300 на 300 кубиков (90000 объектов) стоит помнить, что количество вертексов играет особенную роль. Когда на кубике было 350 вертексов (казалось бы), а шейдер травы был напичкан ненужным и не используемым кодом, такое поле в 90.000 кубиков роняло FPS до 20 кадров в секунду. Обрезав количество вертексов до 120 и поправив немного шейдер, мы сейчас имеем стабильные 90-120 кадров в секунду при разрешении 1920х1080. Осталось дооптимизировать шейдер, и наши кубики практически не будут отнимать время на рендер.

Итого.

Draw Call'ы - это важная часть оптимизации, которую нельзя игнорировать. Посмотреть количество DC в UE4 можно, введя в консоль команду 'stat rhi", где предпоследней строчкой будет число DC.

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

Большое итого.

На самом деле практически все приведенные мною советы есть в туторах и курсах от Epic Games на их обучающей площадке (ссылка тут). Я очень рекомендую ознакомиться с курсами и пройти большинство из них. Но, к сожалению для многих, там туторы на английском языке. Учите английский =)

Особенно интересен курс "Unreal Engine Kickstart for Developers", который позволит очень быстро ознакомиться с некоторыми особенностями движка.

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

Что касается поиска проблемных мест в кадре Unreal Engine 4 - это крутой движок, в котором достаточно большое количество инструментов для профилирования вашего кадра. И мы обязательно рассмотрим инструменты профилирования в какой-нибудь другой раз и в другой статье.

Вот вам на последок еще один скриншот кинотеатра из нашей игры =)

Не забывайте подписываться, я обязательно буду выкладывать новые статьи и больше рассказывать о нашем опыте и делиться знаниями =)

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

Всех с новым годом!

{ "author_name": "Денис Кузнецов", "author_type": "self", "tags": ["unrealengine"], "comments": 30, "likes": 107, "favorites": 218, "is_advertisement": false, "subsite_label": "unknown", "id": 225988, "is_wide": true, "is_ugc": true, "date": "Sun, 03 Jan 2021 08:57:12 +0300", "is_special": false }
0
30 комментариев
Популярные
По порядку
Написать комментарий...

Дурной жар

2

А потом они с несколькими тысячами draw callls приходят на свич (где и 500 много) и получается Ark Survival :)

Ответить
0

Где-то читал, что важно не только само количество вызовов. Как было написано в статье, в играх от Юбисофт количество вызовов всегда на уровне десятков тысяч, Но это из-за особенностей их рендера. Грубо говоря, их 40к вызовов примерно эквивалентны 5к у ГТА 5, поэтому просто поставить голые цифры и тыкать пальцем некорректно

Ответить
0

Не совсем понял комментария. Я ничего плохого не написал о собаках =) Просто указал, что у них DC могут быть выше 40к. Нигде не сказано, что это плохо или хорошо =)

Ответить
1

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

Ответить
0

Хах =) Забавно, ога =)

Ответить
2

Спасибо за материал, подобного на ДТФ не хватает 

Ответить
2

Про порядки малость загнул. Разница в районе 10-15%. Ну или мб прост современные видюхи на уровне архитектуры лучше обсчитывают пересечение большого количества эджей. У тебя ссылка на статью 9-го года.

Ответить
0

10-15% это 10-15 кадров. В любом случае лучше модели делать лучше и не забивать на них)

Ответить
0

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

Ответить
0

Это лишь пример. Речь идёт о модели в целом. У меня когда-то прям был застой в голове, что нужно минимизировать точки и все сводить к одной. Такой подход усугубляет рендер.
Пример был приведен для максимально наглядного визуала проблемы. А не рекомендация только круглые штуки улучшать.
В данном случае родине вертекс не играет никакой роли, а вот отрисовка треугольников - тот вопрос, который важен и затрагивается в этой теме.
Возможно позже дополню статью другими примерами того, как не стоит делать

Ответить
0

Очень интересно, спасибо!
Однако "на порядок" = в 10 раз, в некоторых местах это критично. 60 и 90 fps это не разница "на порядок"

Ответить
0

Тут скорее устойчивое выражение, чем численно точное =)

Ответить
1

Division 2 - игра, которая меня действительно впечатляет по сей день кол-вом объектов в кадре и крутым освещением (полностью динамическое, кстати). И при этом она выдает 60 FPS на RX 570. Я не знаю какую там используют магию, может меня кто-то просветит.
Это так, небольшой офтоп)

Ответить
0

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

Ответить
1

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

Ответить
1

Хорошая статья, спасибо)

Ответить
1

А материал крутой, реально узнал кое-что новое для себя. Подписался

Ответить
1

Спасибо за статью)

Ответить
1

"отрисовывется объект",потом на него  "накладывается шейдер". Отрисовывется это и значит что выполнился шейдер. Шейдер это программа для gpu , а не "эффект" в понимании дизайнера

Ответить
0

Спасибо за статью). Жаль, что я (как новичок в UE4, и движках в принципе) дальше шейдеров уже мало что понял xd.

Ответить
1

Не страшно =) Можно, например, прочитать мои статьи про текстурирование с самого начала. В целом, там достаточно объемный материал, который затрагивает как модели, так и текстурирование в целом.

Потом можно пройти туторы, ссылку на которые я дал здесь в конце статьи. И если все еще останутся вопросы - можно написать мне в личку - я с удовольствием поделюсь всем тем, что знаю, а что не знаю - подскажу куда копать =)

Ответить
0

А что насчёт декалей? Они жрут много ресурсов или нет?
Может вместо карт нормалей везде накидать декалей в редакторе карт. (На статичные элементы вроде стен, пола, колонн, труб и т.п. вещей)
Про Doom Eternal была статья, что там просто тысячи декалей в каждом кадре используется, а сами модели предметов окружения примитивные по геометрии и материалам.
Как с этим обстоит дело на UE ? Если напихать 100500 декалей, будет ли выше fps, по сравнению со сложными материалами?

Ответить
1

Мысли в слух. Возможно, я не прав.
На сколько понимаю декали, то в большей мере зависит всё же от того в каком пассе идёт просчитывание материала:
Если blend mode translucent, то будут в base pass
Masked blend mode частично просчитывается в pre-pass начиная с 4.23
Opaque - в pre-pass
Ну а сама декаль выглядит так словно проецируется планарно в +X absolute world position, что вряд ли является нагрузкой по сравнению с трёхпланарной проекцией. Но относительно текстуры в uv возможно потребит больше.

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

Ответить

Комментарий удален

0

Пример с треугольниками сомнительный, были к центр сведённые стали тонкие по краям шило на мыло поменяли. Можно и лучше сделать путь даже добавит пяток вершин.

Ответить
0

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

Ответить
0

Суть в том, что у тебя сведены крошечные треугольники по бокам, но теперь их там 2-3 штуки, а не все 20. И на 2-3 штуки в разных местах, в разных пиксельных блоках вычисление идет более быстро и параллельно. А когда у тебя все сведено в одну точку, то у тебя 1 блок из 4-х пикселей прокручивается минимум 20 раз.
То есть, в новой сетке нагрузка распараллеливается, а в старой - все падает на одну группу.

Ответить
0

я не спорю что стало лучше но что мешало сделать ещё лучше?

Ответить
0

Хм. Может и можно сделать лучше =) Но в целом, надо уметь остановиться в таких делах.
Предлагай свои варианты - я добавлю в статью =)

Ответить
0

ISM получается батчит меши?

Ответить
0

Вот не могу сказать с уверенностью. Но вроде как да.

Ответить
Читать все 30 комментариев
null