Что такое render pipeline

Разработка мобильных игр на Unity. URP, 2D Animation и другие новомодные вещи на примере игры

Дисклеймер! Код в этой статье не проходил рефакторинг и носит лишь ознакомительный характер, чтобы поделиться идеями. И вообще, в целом, это smellscode.

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

Итак, запасаемся кофе, открываем Unity и погнали!

Базовая настройка проекта. URP и все-все-все.

Стоит указать, что ниже пойдет речь о 2D игре. Для 3D игр подходы будут несколько отличаться, как и настройки.

В нашем проекте стоят следующие настройки (для Quality уровней):

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

Настройки графики для пресета Low в Project Settings:

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

На что здесь следует обратить внимание:

Теперь перейдем к настройкам самих URP Asset. На что следует обратить внимание:

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

Adaptive Performance

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

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

Другие полезные настройки:

Отключите 3D освещение, лайтмапы, тени и все что с этим связано.

По-возможности подключите multithreaded rendering.

Игровой фреймворк

Едем дальше. URP и другие настройки проекта сделали. Теперь настало время поговорить о нашем ядре проекта. Что оно включает в себя?

Само ядро фреймворка включает в себя:

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

Базовые классы для интерфейсов (компоненты, базовые классы View).

Классы для работы с контентом, сетью, шифрованием и др.

Базовые классы для работы с логикой игры.

Базовые классы для персонажей и пр.

Утилитарные классы (Coroutine Provider, Unix Timestamp, Timed Event и пр.)

Зачем нужны менеджеры?

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

Хотя мы и используем внедрение зависимостей, менеджеры состояний реализованы в качестве синглтонов (атата по рукам, но нам норм) и могут быть (и по их назначению должны быть) инициализированы единожды. А дальше мы просто можем использовать их:

А уже сам менеджер распределяет, в какие системы аналитики, как и зачем мы отправляем эвент.

Базовые классы.

Здесь все просто. Они включают в себя базовую логику для наследования. К примеру, класс BaseView и его интерфейс:

А дальше мы можем использовать его, к примеру таким образом:

Классы для работы с контентом, сетью, шифрованием

Ну здесь все просто и очевидно. Вообще, у нас реализовано несколько классов:

1) Классы шифрования (Base64, MD5, AES и пр.)

3) Network-классы, которые позволяют удобно работать с HTTP-запросами, работать с бандлами / адрессаблс и др.

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

Утилитарные классы

Здесь у нас хранятся полезные штуки, вроде Unix Time конвертера, а также костыли (вроде Coroutine Provider-а).

Unix Time Converter:

Костыль Coroutine-Provider:

Логика сцен

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

Зачем это сделано?

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

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

Работа с контентом

При работе с контентом, мы стараемся делать упор на оптимизацию. В игре содержится много UI, скелетные 2D анимации, липсинк и прочее. Вообще, контента достаточно много, не смотря на простоту игры.

Анимации в игре

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

Упаковка и сжатие

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

Локализация

Вся локализация базируется на JSON. Мы планируем отказаться от этого в ближайшее время, но пока что на время Soft-Launch этого хватает:

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

Работа с UI

При работе с UI мы разбиваем каждый View под отдельный Canvas. 99% всех анимаций работает на проверенном временем DOTween и отлично оптимизирован.

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

View инициализируются и обновляются по запросу через эвенты, которые внедряются в Level Installer, либо в отдельных блоках логики.

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

Что мы используем еще?

Итого

Работа с механиками получается достаточно гибкой за счет блоков логики. Мы изначально думали взять связку Zenject + UniRX, но решили отказаться от нагромождения большой системы. Да, мы сделали проще, но нам и не нужно всех возможностей этих огромных библиотек.

Источник

Масштабируемая и высокая производительность рендеринга

Universal Render Pipeline от Unity стал мощным решением, сочетающим красоту, скорость и производительность, а также поддержку всех целевых платформ Unity.

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

Universal Render Pipeline — это:

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

В Universal Render Pipeline постобработка встроена непосредственно в процесс рендеринга, обеспечивая высокую производительность. Разработчикам доступны такие эффекты, как сглаживание, глубина резкости, размытие в движении, проекция Панини, блеск, искажение линзы, хроматические аберрации, цвето- и тонокоррекция, виньетка, зернистость и 8-битный дизеринг.

Universal Render Pipeline
Упор на производительность
Однопроходный упреждающий рендеринг
Поддержка Shader Graph

Встроенный процесс рендеринга
Универсальность
Поддерживает как упреждающий, так и отложенный рендеринг

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

От 2D и 3D до AR/VR-проектов: Universal Render Pipeline позволяет не тратить время на доработку проекта для выпуска на новом устройстве.

Возможность конфигурации рендеринга в Unity с помощью скриптов на C# позволяет вам:

Universal Render Pipeline проще, чем встроенный процесс рендеринга, но улучшает качество графики. Перевод проекта со встроенного процесса рендеринга на Universal Render Pipeline должен обеспечивать аналогичную или улучшенную производительность. Прочтите статью в нашем блоге, чтобы узнать о том, как Universal Render Pipeline повышает частоту кадров без снижения качества графики.

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

Источник

3D рендеринг: как работает GPU

Всем привет. Меня зовут Глеб Булгаков, я — программист. Вместе с тех. артистом Романом Лещенко мы работаем в компании Fractured Byte и хотим поделиться нашими знаниями и опытом в деле оптимизации реалтаймового контент пайплайна.

Работая вместе в компании BWF, мы успели приложить руку ко множеству разных по жанру, целевой платформе и сложности проектов. Среди них было и портирование всемирно известных проектов на мобильные платформы (Life Is Strange, Brothers: A Tale of Two Sons), разработка собственных проектов на разные платформы (In Fear I Trust, Renoir), и даже прототипирование и R’n’D для VR игр. У нас 9 лет опыта работы с движком Unreal Engine, начинали с UE3. Мы успели поработать с такими компаниями как Square Enix, Disney, 505 Games, Framestore, Chillingo и т.д. Таким образом, мы почти никогда не сталкивались с одними и теми же задачами и нам всегда приходилось изыскивать возможности отрисовать много контента на экране за минимальное количество миллисекунд. Так что, можно сказать, что мы съели на этом пару собак 🙂

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

Обзор

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

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

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

Также перед тем, как начать, стоит сказать, что эта статья не описывает какой-то конкретный конвейер рендера в конкретном движке (хотя мы и будем приводить много примеров из UE4), а старается обобщить наши знания таким образом, чтобы вы могли понять откуда “растут ноги” у современных контент-пайплайнов и графических фич, а также поняли, почему количество полигонов в модели уже давно не является адекватным мерилом производительности.

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

Какие ресурсы можно создать на GPU?

Текстуры

Самый известный и самый объемный ресурс — это, наверное, текстура. Чисто для примера мы будем использовать простую текстуру 8х8 пикселей с призраком из игры Pac-man:

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

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

Двумерные (в терминологии DirectX 11 — Texture2D).
Тут и рассказывать особо нечего — это самый распространенный тип текстур, который с большой вероятностью будет делать

90% картинки в вашем проекте. Текстуры объектов, интерфейса, служебные LUT, карты высот — как правило, это все Texture2D.

Трехмерные (в терминологии DirectX 11 — Texture3D).
Такие текстуры можно представить как массив или слои двумерных текстур. В редакторе создавать их как ассеты можно, но для этого их нужно включить ( r.AllowVolumeTextureAssetCreation 1 ). В основном, 3D текстуры используются самим движком для работы с Vector Fields, Distance Fields, Volumetric Lightmaps и т.д. То есть там, где необходимо пространственное представление данных.

Каждая текстура может обладать уменьшенными копиями самой себя — это так называемые мип-мапы (mip maps). Дело в том, что когда мы рисуем объект, он может находится как близко, так и далеко от игрока и соответственно иметь разный размер на экране. В результате, если объект имеет текстуру большего разрешения, чем его фактический размер на экране в этот момент — невозможно стабильно получить один и тот же цвет пикселя на экране с течением времени. В результате возникает эффект, известный под названием Муар (от фр. Moire).

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

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

8 Мб

Константные буферы

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

Рассмотрим простейшую модель — куб. Как вам, возможно, известно — треугольник является наименьшим элементом, из которого состоят все 3D-модели. И чтобы описать модель способом, который будет понятен GPU, нам нужно 2 буфера — вершинный и индексный. Вершинный описывает, как ни странно, вершины модели: их позицию, нормали, текстурные координаты и т. д. Индексный буфер содержит индексы вершин, порядок соединения этих вершин, необходимый для построения на экране отдельного треугольника.

Шейдера и стейты

Вместе с ресурсами на GPU можно создавать дополнительные объекты: стейты и шейдера. Первые из них являются объектами, которые описывают правила взаимодействия с конкретным ресурсом, вторые же —отдельные программы для GPU.

SamplerState — стейт, который определяет, как GPU делает выборку с текстуры: фильтрацию, адресацию. Это, наверно, единственный стейт в UE4, на который мы явно можем влиять, когда устанавливаем текстуру в материале или в настройках самой текстуры. При описании текстур я избегал слова “сэмплить” (sample), под сэмплингом подразумевается вычисление цвета, именно вычисление, а не простая загрузка из памяти. Каждый раз, когда мы сэмплим из текстуры, GPU делает много вычислений, чтобы определить, с какой мипмапы брать цвет, возможен даже блендинг между мипмапами; для определенных типов фильтрации, таких как Anisotropic, в учет берется и положение треугольника в пространстве. Сэмплинг — достаточно сложная операция, и чем меньше текстур используется при рендеринге объекта, тем лучше.

А что будет, если мы попытаемся взять пиксель за пределами текстуры (напомним, что текстура находится в пределах 0. 1)? Для этого и была придумана адресация.

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

Шейдер — это программа, которая выполняется на GPU. Есть разные виды шейдеров, но мы остановимся только на двух основных: Vertex Shader (вершинный) и Pixel Shader (пиксельный). Основной задачей вершинного шейдера является трансформация объекта в пространстве (перемещение, поворот и масштабирование) и проецирование на экран. После того, как GPU спроецировала примитив на экран, вступает в работу пиксельный шейдер, который и вычисляет цвет пикселя.

В UE4 шейдеры создаются путём визуального программирования в Material Editor. Каждый материал имеет в себе предопределенные входы (инпуты) (Base Color, Metallic, Roughness, Emissive и т.д.), и мы, создавая ноды и соединяя их в определенной последовательности, генерируем код шейдера. Этот код является только частью шейдера и определяет основные параметры материала. В свою очередь, материал генерирует не один шейдер, а целое множество шейдеров для разных проходов материала.

Операции на GPU

На GPU есть особая текстура — Swapchain, которая представляет собой область экрана, где рисуется наша сцена. Swapchain состоит из нескольких буферов: front buffer — содержит то, что сейчас мы видим на экране, и back buffer — текстура, в которую мы рисуем следующий кадр. Когда наша сцена отрисована и находится в back buffer, мы вызываем команду Present — она меняет front buffer и back buffer местами. И отрисовка следующего кадра начинается снова.

Спасибо за внимание, надеемся, вы почерпнули новых знаний и лучше поняли возможности GPU и для чего они используются. Во второй части статьи мы рассмотрим на практическом примере процесс отрисовки сцены. До встречи на UE4 Daily!

Источник

Rendering Pipeline Overview

The Rendering Pipeline is the sequence of steps that OpenGL takes when rendering objects. This overview will provide a high-level description of the steps in the pipeline.

Contents

Pipeline

Что такое render pipeline. Смотреть фото Что такое render pipeline. Смотреть картинку Что такое render pipeline. Картинка про Что такое render pipeline. Фото Что такое render pipeline

The OpenGL rendering pipeline is initiated when you perform a rendering operation. Rendering operations require the presence of a properly-defined vertex array object and a linked Program Object or Program Pipeline Object which provides the shaders for the programmable pipeline stages.

Once initiated, the pipeline operates in the following order:

Vertex Specification

The process of vertex specification is where the application sets up an ordered list of vertices to send to the pipeline. These vertices define the boundaries of a primitive.

Primitives are basic drawing shapes, like triangles, lines, and points. Exactly how the list of vertices is interpreted as primitives is handled via a later stage.

This part of the pipeline deals with a number of objects like Vertex Array Objects and Vertex Buffer Objects. Vertex Array Objects define what data each vertex has, while Vertex Buffer Objects store the actual vertex data itself.

A vertex’s data is a series of attributes. Each attribute is a small set of data that the next stage will do computations on. While a set of attributes do specify a vertex, there is nothing that says that part of a vertex’s attribute set needs to be a position or normal. Attribute data is entirely arbitrary; the only meaning assigned to any of it happens in the vertex processing stage.

Vertex Rendering

Once the vertex data is properly specified, it is then rendered as a Primitive via a drawing command.

Vertex Processing

Vertices fetched due to the prior vertex rendering stage begin their processing here. The vertex processing stages are almost all programmable operations. This allows user code to customize the way vertices are processed. Each stage represents a different kind of shader operation.

Many of these stages are optional.

Vertex shader

Vertex shaders perform basic processing of each individual vertex. Vertex shaders receive the attribute inputs from the vertex rendering and converts each incoming vertex into a single outgoing vertex based on an arbitrary, user-defined program.

Vertex shaders can have user-defined outputs, but there is also a special output that represents the final position of the vertex. If there are no subsequent vertex processing stages, vertex shaders are expected to fill in this position with the clip-space position of the vertex, for rendering purposes.

One limitation on vertex processing is that each input vertex must map to a specific output vertex. And because vertex shader invocations cannot share state between them, the input attributes to output vertex data mapping is 1:1. That is, if you feed the exact same attributes to the same vertex shader in the same primitive, you will get the same output vertex data. This gives implementations the right to optimize vertex processing; if they can detect that they’re about to process a previously processed vertex, they can use the previously processed data stored in a post-transform cache. Thus they do not have to run the vertex processing on that data again.

Vertex shaders are not optional.

Tessellation

Tessellation

Core in version4.6
Core since version4.0
Core ARB extensionARB_tessellation_shader

Primitives can be tessellated using two shader stages and a fixed-function tessellator between them. The Tessellation Control Shader (TCS) stage comes first, and it determines the amount of tessellation to apply to a primitive, as well as ensuring connectivity between adjacent tessellated primitives. The Tessellation Evaluation Shader (TES) stage comes last, and it applies the interpolation or other operations used to compute user-defined data values for primitives generated by the fixed-function tessellation process.

Tessellation as a process is optional. Tessellation is considered active if a TES is active. The TCS is optional, but a TCS can only be used alongside a TES.

Geometry Shader

Geometry shaders are user-defined programs that process each incoming primitive, returning zero or more output primitives.

The input primitives for geometry shaders are the output primitives from a subset of the Primitive Assembly process. So if you send a triangle strip as a single primitive, what the geometry shader will see is a series of triangles.

However, there are a number of input primitive types that are defined specifically for geometry shaders. These adjacency primitives give GS’s a larger view of the primitives; they provide access to vertices of primitives adjacent to the current one.

The output of a GS is zero or more simple primitives, much like the output of primitive assembly. The GS is able to remove primitives, or tessellate them by outputting many primitives for a single input. The GS can also tinker with the vertex values themselves, either doing some of the work for the vertex shader, or just to interpolate the values when tessellating them. Geometry shaders can even convert primitives to different types; input point primitives can become triangles, or lines can become points.

Geometry shaders are optional.

Vertex post-processing

After the shader-based vertex processing, vertices undergo a number of fixed-function processing steps.

Transform Feedback

The outputs of the geometry shader or primitive assembly are written to a series of buffer objects that have been setup for this purpose. This is called transform feedback mode; it allows the user to transform data via vertex and geometry shaders, then hold on to that data for use later.

The data output into the transform feedback buffer is the data from each primitive emitted by this step.

Primitive assembly

Primitive assembly is the process of collecting a run of vertex data output from the prior stages and composing it into a sequence of primitives. The type of primitive the user rendered with determines how this process works.

The output of this process is an ordered sequence of simple primitives (lines, points, or triangles). If the input is a triangle strip primitive containing 12 vertices, for example, the output of this process will be 10 triangles.

If tessellation or geometry shaders are active, then a limited form of primitive assembly is executed before these Vertex Processing stages. This is used to feed those particular shader stages with individual primitives, rather than a sequence of vertices.

The rendering pipeline can also be aborted at this stage. This allows the use of Transform Feedback operations, without having to actually render something.

Clipping

The primitives are then clipped. Clipping means that primitives that lie on the boundary between the inside of the viewing volume and the outside are split into several primitives, such that the entire primitive lies in the volume. Also, the last Vertex Processing shader stage can specify user-defined clipping operations, on a per-vertex basis.

The vertex positions are transformed from clip-space to window space via the Perspective Divide and the Viewport Transform.

Face culling

Triangle primitives can be culled (ie: discarded without rendering) based on the triangle’s facing in window space. This allows you to avoid rendering triangles facing away from the viewer. For closed surfaces, such triangles would naturally be covered up by triangles facing the user, so there is never any need to render them. Face culling is a way to avoid rendering such primitives.

Rasterization

Primitives that reach this stage are then rasterized in the order in which they were given. The result of rasterizing a primitive is a sequence of Fragments.

A fragment is a set of state that is used to compute the final data for a pixel (or sample if multisampling is enabled) in the output framebuffer. The state for a fragment includes its position in screen-space, the sample coverage if multisampling is enabled, and a list of arbitrary data that was output from the previous vertex or geometry shader.

This last set of data is computed by interpolating between the data values in the vertices for the fragment. The style of interpolation is defined by the shader that outputed those values.

Fragment Processing

The data for each fragment from the rasterization stage is processed by a fragment shader. The output from a fragment shader is a list of colors for each of the color buffers being written to, a depth value, and a stencil value. Fragment shaders are not able to set the stencil data for a fragment, but they do have control over the color and depth values.

Fragment shaders are optional. If you render without a fragment shader, the depth (and stencil) values of the fragment get their usual values. But the value of all of the colors that a fragment could have are undefined. Rendering without a fragment shader is useful when rendering only a primitive’s default depth information to the depth buffer, such as when performing Occlusion Query tests.

Per-Sample Operations

The fragment data output from the fragment processor is then passed through a sequence of steps.

The first step is a sequence of culling tests; if a test is active and the fragment fails the test, the underlying pixels/samples are not updated (usually). Many of these tests are only active if the user activates them. The tests are:

After this, color blending happens. For each fragment color value, there is a specific blending operation between it and the color already in the framebuffer at that location. Logical Operations may also take place in lieu of blending, which perform bitwise operations between the fragment colors and framebuffer colors.

Lastly, the fragment data is written to the framebuffer. Masking operations allow the user to prevent writes to certain values. Color, depth, and stencil writes can be masked on and off; individual color channels can be masked as well.

Источник

Добавить комментарий

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