XNA Draw или пишем систему частиц. Часть II: шейдеры
Привет всем разработчикам игр и просто людям, которые интересуются геймдевом.
Пришло время рассказать вам о пиксельных шейдерах и о том, как сделать post-proccesing. Это вторая часть статьи о графических методах в XNA, в прошлой статье — мы рассматривали методы Draw и Begin у spriteBatch. Для примера: улучшим нашу систему частиц добавлением пиксельного шейдера, который будет искажать пространство.
Шейдер
Немного поговорим о шейдарах. Существуют два типа шейдера (в Shader Model 2.0, её то мы и используем): вертексный и пиксельный.
Вершинный шейдер оперирует данными, сопоставленными с вершинами многогранников. К таким данным, в частности, относятся координаты вершины в пространстве, текстурные координаты, тангенс-вектор, вектор бинормали, вектор нормали. Вершинный шейдер может быть использован для видового и перспективного преобразования вершин, генерации текстурных координат, расcчета освещения и т. д.
Пиксельные шейдеры выполняются для каждого фрагмента в фазе растеризации треугольников. Фрагмент (или пиксель) — точка, с оконными координатами, полученная растеризатором после выполнения над ней ряда операций. Проще говоря, результирующая точка буфере кадра, совокупность этих точек потом формирует изображение. Пиксельные шейдеры оперируют над фрагментами до заключительных стадий, т.е. до тестов глубины, альфы и stencil. Пиксельный шейдер получает интерполированные данные (цвет, текстурные координаты) из вершинного шейдера.
Если сказать очень коротко про пиксельный шейдер, то это обработчик готового изображения.
В случае с Displacement-шейдером — вершинные шейдеры не нужны, рассмотрим пиксельные.
Post-processing
Если быть ленивым лаконичным человеком, то Post-processing шейдеры выполняются тогда, когда вся картинка игры уже отрисована: шейдер накладывается сразу на всю картинку, никак не на отдельные спрайты.
-Ведь у spriteBatch.Begin есть параметр, effect, не проще применять шейдер сразу, как мы его рисуем?
Отвечаю: вот именно, что такой шейдер применяется к единичным спрайтам, как итог, Displacement-шейдер будет функционировать криво.
Для создания Post-process обработки, нужно сначала рисовать то, что должно быть нарисовано на экране — на отдельную текстуру, а потом рисовать эту самую текстуру с использованием Post-process шейдера. Таким образом, шейдер воздействует не на единичные спрайты, а на картинку в целом.
-Стоп, а как рисовать на отдельную текстуру?
Отвечаю: знакомьтесь — RenderTarget2D
RenderTarget2D
И опять, привет мой друг — лаконичность. RenderTarget2D — по сути является текстурой, на которую можно рисовать.
Идем туда, где обычно мы рисуем сцену, перед отчищением вставляем:
Теперь все будет рисоваться не на экран, а на RenderTarget2D.
Чтобы переключиться опять на экран, используем конструкцию:
Не забудьте очистить RenderTarget, перед прорисовкой.
Искажающий шейдер с Displacemenet-map
Идея такого пиксельного шейдера очень проста: на вход поступает текстура, которую нужно «погнуть», на второй вход — карта, о том, как гнуть.
Карту мы будем генерировать, о том как — в практике.
Кстати, о карте. Карта представляет собой такое же по размерам изображение, как и текстура сцены, за исключением того, пожалуй, что нарисованного изображения — не увидим.
Более подобно о карте и о том, как действует шейдер:
В процессе обработки изображения — получаем текущую позицию пикселя, получаем цвет. Тоже самое делаем и для карты. Т.е. в конечном итоге, у нас будет доступно для модификации: цвет пикселя, позиция пикселя, цвет пикселя на карте соответствующего позиции пикселя на изображении.
Будем использовать цвета карты, чтобы передать информацию шейдеру, как погнуть пиксель.
К примеру, R-канал (красный) получает значения от 0f до 1f. Если мы видим на карте искажения R=0.5f, то просто сдвигаем позицию пикселя изображения на 10f * 0.5f пикселя. 10f — это сила, с которой мы сдвигаем.
Соответственно, R-канал будет соответствовать X координате, а G-канал — Y.
Если вам нужны картинки, получите их:
Исходная картинка:
Карта:
Итоговая картинка:
Так, с теорией вроде разборались, сейчас попробуем это все реализовать кодом.
Практика: дорабатываем систему частиц
Дорабатываем исходный код из прошлой статьи.
Сразу добавим какую-нибудь картинку, чтобы искажения были заметны, например эту:
Копируем ParticleController и называем его ShaderController, в нем нам нужно изменить только сам процесс создания частицы, а конкретно:
Реализуем post-processing, создаем новые переменные:
Идем к методу Draw главного класса и пишем:
Post-processing готов, теперь создадим шейдер.
Создаем новый Effect (fx) файл (это файл шейдера, написанного на HLSL), вписываем туда, что-то вроде:
Шейдер создан, загрузить его можно так же, как и обычную текстуру, за исключением того, что тип не Texture2D, а Effect.
Теперь обновим наш Draw:
Запускаем, любуемся красивыми, реалистичными животными искажениями (лучше посмотреть демо):
На самом деле, эта реализация системы частиц (не шейдеры, а то что было в первом уроке) в целом — не совсем хороша для производительности. Сущесвтуют другие методы, более сложные в понимании, о них я расскажу как-нибудь потом.
Прикладываю исходники и демо (на этот раз, запустится на любом компьютере с XNA 4.0 и аппаратной подержкой DirectX9, inc sh 2.0)
Может быть на этой неделе, может быть неизвестно когда — расскажу о методе Update и как реализовать физику, используя Box2D.
Удачи вам и еще раз с праздником программиста 0xFF+1 днем! 😉
Imageshaderprocessor Virus ⛏️ (Coin Miner Trojan) Removal
Imageshaderprocessor is a Trojan Coin Miner that uses the infected computer’s resources to mine digital currency without your authorization. It can be Monero, Bitcoin, DarkCoin or Ethereum.
About “Imageshaderprocessor”
Imageshaderprocessor process will certainly use greater than 70% of your CPU’s power and graphics cards resources
It is better to prevent, than repair and repent!
Subscribe to our Telegram channel to be the first to know about news and our exclusive materials on information security.
What this suggests, is that when the miners are running you will locate that your computer is running slower and video games are stammering or freezing because the Imageshaderprocessor is utilizing your computer’s resources to create earnings on their own. This will certainly create your CPU to perform at really hot temperatures for extensive time periods, which might reduce the life of the CPU.
Imageshaderprocessor Technical Summary.
Besides reducing your computer, going for peek degree for very long times may trigger damage to your maker and also elevate electrical energy bills.
Central processing unit is not the single computer element that is exploited by Imageshaderprocessor miner. Graphic processing unit is also under attack, and while little and covered CPU cooling system is quite hard to crack, graphic cards have large and easy-to-access rotors, which can be easily cracked if affected while working, for example, by the user much earlier before the coin-miner injection. Malfunctioning cooling system, together with the really high load caused by Imageshaderprocessor malware can easily lead to graphic processing unit failure.
When a computer is contaminated with Imageshaderprocessor trojan, typical signs include:
Imageshaderprocessor – Really high CPU and also graphics cards use
How to detect Imageshaderprocessor Coin Miner Trojan?
Unlike ransomware, cryptocurrencies mining dangers are not obtrusive and are more likely to stay undetected by the target.
Discovering Imageshaderprocessor danger is reasonably easy. If the sufferer is using a GridinSoft Anti-Malware it is practically certain to discover any mining malware. Even without a safety solution, the victim is most likely to believe there is something wrong due to the fact that mining bitcoin or various other cryptocurrencies is a very source intensive procedure. One of the most typical symptom is a recognizable and typically consistent decrease in performance.
This sign alone doesn’t inform the victim what the precise trouble is. The customer can experience similar concerns for a range of reasons. Still, Imageshaderprocessor malware can be very disruptive because it will hog all available computer power as well as the sudden change in the method the contaminated device executes is likely to make the sufferer try to find remedies. If the equipment of the affected device is powerful sufficient, as well as the victim does not find as well as get rid of the danger rapidly, the power intake and subsequently the electrical power expense will certainly rise significantly too.
Attentive users can also observe strange alterations in the Task Manager, or any other utility for checking the currently active tasks. System processes, that are usually launched in the thread of Windows programs, are displayed as the task launched by the user. Along with this difference, you can also see that the picture of the notebook, that is common for system apps running in the background, is changed by another picture. And sometimes, Imageshaderprocessor malware does not even try to mimic the system processes, and can be spectated with his original name.
How to Remove Imageshaderprocessor Malware?
As soon as the sufferer has actually ascertained that their trouble is Imageshaderprocessor risk, for the common user there are a couple of services.
Most importantly usage GridinSoft Anti-Malware would be the most effective solution. There is no scarcity of readily available cybersecurity software that will certainly identify and get rid of mining malware.
Conversely, if the infected device doesn’t contain important information or the target has supported such information in a tidy location, and also the user has some experience, formatting the hard disk drives can work. This option may not suffice if the infection has penetrated one or more networks the maker is a part of.
Шейдеры. Что и как
Расскажу, как в общем случае они работают, что умеют и как их используют
Сразу оговорюсь, что материал рассчитан на тех, кто никогда не работал с шейдерами или вообще не знаком с разработкой игр, то есть это в некотором смысле научпоп.
Слово «шейдер» в контексте разработки игр очень популярно, слышать его могли и те, кто игры не делает. Само слово изначально появилось от англ. shading (затенение) — первые шейдеры использовались, чтобы передавать глубину с помощью работы со светом, блеском, тенями и прочим. Со временем шейдеры стали использоваться для совершенно разного вида постобработки и вообще отрисовки примерно всего.
Говоря общими словами, шейдер — это просто программа для графической карты. То есть то, что пишется школьниками на паскале (хипстерами на пайтоне) — это программы для вашего центрального процессора (CPU), а шейдеры — для графического (GPU). Особенность же этих программ выходит из особенностей GPU — они работают параллельно на сотнях маленьких ядех вместо нескольких больших, преимущественно осуществляя математические операции.
Теперь разберемся, как это все работает.
В общем случае цель шейдера — отрисовать некоторый объект. Поэтому возьмем куб, распишем процесс его отрисовки и посмотрим, где используются шейдеры и зачем. Сначала опишем сам куб. Для графической карты это 8 точек, между некоторыми из которых есть плоскость. Каждая из точек описывается тремя числами (правильно сказать, что это вершины). Помимо этого у кубика есть цвет и положение внутри мира.
Процесс отрисовки, если его достаточно упростить (что я и сделаю в рамках этой статьи), можно поделить на несколько шагов:
1. Получение входных данных из памяти.
2. Выполнение шейдера вершин.
3. Растеризация.
4. Выполнение шейдера пикселей (фрагментов).
5. Проведение тестов «глубины».
6. Отрисовка на текстуру для экрана.
В первом шаге видеокарта каким-то образом получает данные (вершины, плоскости, текстуры) в свою видеопамять, для нас это сейчас не так важно. Далее происходит конвертация координат относительно объекта в координаты на экране относительно камеры. После происходит растеризация — высчитывается, в каких пикселях уже на экране находится объект. Такие пиксели называют фрагментами. Отличие от пикселей заключается в том, что фрагмент помимо информации о пикселе, содержит еще и некоторую побочную информацию, полученную после растеризации. Для упрощения будем считать, что это все просто пиксели на экране. Далее для каждого пикселя выполняется шейдер фрагмента. А затем проверяется, что расстояние от камеры до фрагмента соответствует высчитанному заранее в нужном направлении в буфере глубины. Проще говоря, проверяется, нет ли перед объектом чего-либо еще, и нужно ли его отрисовывать на итоговое изображение.
Как видите, в процессе отрисовки можно заметить два вида шейдера. На самом деле, сейчас есть чуть больше видов, но они не столь важны для разбора, так как имеют более специфичный характер использования, а мы рассказываем на пальцах. Но вот те два, что нас волнуют:
1. Шейдер вершин.
2. Шейдер фрагментов.
Как сказано было ранее, этот шейдер (или группа шейдеров по очереди) занимается переводом координат относительно объекта, в координаты на текстуре.
На картинке начало координат немного не соответствует реальным, что все так же не влияет на понимание процесса 🙂
Пройдемся по состояниям. В первом у нас, очевидно, входные координаты без излишков. На втором они были перенесены в координаты относительно начала «мира». Потом они переносятся в координаты относительно точки смотрящего (видно на второй картинке), но заметно, что картинка плоская. Их проекция происходит далее и мы получаем наши итоговые координаты. Все эти операции производятся шейдером. Помимо прочего, он позволяет не только отобразить реальные координаты, но и модифицировать их так, чтобы исказить объект для эффекта. Например, я недавно писал шейдер, который переворачивал спрайт, чтобы отрисовать его тень:
После преобразований вершин и растеризации нужно высчитать цвет каждого фрагмента (помним, что для упрощения это пиксели). Для примера возьмём наш куб: мы помним, что он залит одним цветом. Просто сделаем так, чтобы цвет каждого фрагмента стал цвета куба и все:
Выглядит немного странно, да? Проблема в том, что мы не видим ни теней, ни текстур. Будь на кубе какая-либо текстура, мы бы заметили переход между гранями. Вот возьмем текстуру:
Теперь достаточно в каждом пикселе просто брать цвет из текстуры. Но чтобы это сделать, нужно добавить для каждой точки куба еще информацию: UV канал. Это координат вида (u, v). Отсюда и название, так как x и y были заняты. Она присваивается вершине объекта и обозначает точку на текстуре, которая ей соответствует. Чтобы было понятнее, если мы хотим на каждую грань куба нарисовать знакомое нам лицо, то UV координаты для каждой грани будут выглядеть предельно просто:
Модифицировать их никак не надо. Более того, считать координаты для каждой точки — тоже не нужно. Этим занимается GPU, самостоятельно интерполируя точки из вершин. Тогда достаточно просто сказать для каждой точки что-то вроде
Это очень условный пример, но примерно так в простейшем случае оно и работает:
Помимо натягивания текстур в пиксельном шейдере можно, например, получить информацию об освещенности и добавить к цвету черного пропорционально затемнению в этой точке, тогда объект будет менее плоским. Это, конечно, если просчет освещенности где-то написан, потому что сама по себе видеокарта о понятиях освещения и теней мало чего знает. Вообще делать с цветами можно что угодно, например подменять их в зависимости от игрока:
Помимо UV канала в шейдер в зависимости от его вида приходят разные данные. Они зависят от игрового движка и графической библиотеки. Обычно туда входят данные о местоположении точки, нормаль (вектор исходящий от поверхности в точке), UV канал. Но также шейдер вершин может передавать данные в пиксельный шейдер. Если это координата, то она будет проинтеполирована на основе положения фрагмента относительно вершин, между которыми он находится, как, например, UV данные.
Оптимизация рендера под Mobile. Часть 3. Шейдеры
Привет Хабр! В предыдущих частях цикла (раз, два) мы рассматривали тайловую архитектуру мобильных GPU, а также классифицировали различные семейства GPU, представленные у пользователей. В этой части мы рассмотрим приемы, которые помогут писать быстрые шейдеры для мобильных GPU.
Шейдерные инструкции
Чтобы лучше ориентироваться в оптимизационных подходах, применяемых при написании шейдеров, полезно знать об основных категориях шейдерных инструкций. Первая категория — арифметические инструкции. Эти инструкции, как правило, работают с регистрами GPU и выполняются предсказуемое количество тактов. Обычно это 1 такт. При этом в современных конвейерах может выполняться по нескольку арифметических инструкций за 1 такт. Вторая категория — текстурные инструкции. Они осуществляют текстурную выборку — чтение из текстур с применением определенной фильтрации. Время выполнения этих инструкций значительно дольше, чем арифметических, и зависит от наличия запрашиваемых текселей в текстурном кэше. Современные архитектуры маскируют длительное время выполнения текстурных инструкций за счет переключения на другие шейдерные потоки и выполнения их арифметических инструкций. Рекомендуемое соотношение количества арифметических инструкций к текстурным может быть 10 к 1 и выше.
Кроме арифметических и текстурных инструкций, ещё выделяют Load & Store инструкции. К ним относятся, например, инструкции записи вершинных атрибутов в вершинном шейдере и чтение интерполированных значений этих атрибутов во фрагментном шейдере. Время выполнения таких инструкций больше, чем у арифметических, но меньше, чем у текстурных.
Учет количества используемых регистров
В типичной архитектуре GPU имеется общий банк регистров (register file), используемый множеством параллельно выполняемых потоков. Каждый такой поток рассчитывает один вертекс или пиксель. От количества регистров, требуемых для выполнения шейдера, зависит максимальное количество одновременно выполняемых потоков. Например, на архитектуре Mali Midgard имеется следующая зависимость:
| 0-4 регистра | максимальное количество потоков |
| 5-8 регистров | половина максимального количества |
| 8-16 регистров | четверть максимального количества |
Дальнейшее увеличение потребности в регистрах приводит к записи промежуточных значений во временную память (так называемый register spilling). Иногда компилятор Mali может предпочесть небольшой register spilling переходу в менее выгодную конфигурацию потоков.
Оценить количество используемых регистров для Mali можно при помощи Mali Offline Compiler:
Точность mediump
В OpenGL ES предусмотрена возможность задавать точность величин при помощи квалификаторов lowp, mediump и highp.
| uniform lowp sampler2D u_texture0; varying mediump v_pos; … mediump float temporary; |
Применение пониженной точности позволяет задействовать меньше регистров и добиться повышенной плотности вычислений за такт. Как было рассказано в предыдущей статье, применение точности lowp сегодня нежелательно. Она поддерживается на аппаратном уровне только в устаревающих моделях PowerVR, а на всех современных GPU вместо lowp фактически используется mediump. Такая ситуация напрасно усложняет процессы QA, если в шейдерах используется lowp.
Есть ли смысл применять mediump? В самых актуальных рекомендациях по оптимизации для мобильных GPU по-прежнему предлагается по возможности использовать mediump во фрагментных шейдерах. Остановимся подробнее на этой точности.
Как мы показали выше, в GLSL ES точность можно задать отдельным переменным — uniform-ам и varying-ам. Кроме того, есть возможность задать точность по умолчанию для всех величин определенного типа. Например, используя такую строчку в начале шейдера, можно задать точность mediump для всех float:
С такой строкой в начале фрагментного шейдера можно получить заметное ускорение на большинстве мобильных видеокарт (при условии, что в шейдере большое количество арифметических инструкций).
Приведем пример выдачи утилиты Mali Offline Compiler для архитектуры Midgard для шейдера, содержащего большое количество арифметики.
С точностью highp (precision highp float;) получаем 32 такта на выполнение шейдера:
С точностью mediump — 21 такт:
Отметим, что, несмотря на немного возросшее количество инструкций, оценочное время выполнения шейдера сократилось. Это связано с тем, что за 1 такт с точностью mediump выполняется больше инструкций, чем с highp.
Похожую картину можно наблюдать в PVRShaderEditor от Imagination для видеокарт PowerVR Rogue.
С «precision highp float;» получаем:
Используются умножения с полной точностью, выполняемые по 2 за такт.
Если же начинать такой же шейдер строчкой «precision mediump float», можно увидеть, что операции были упакованы в 16-битные суммы произведений (SOP). Это операции вида a * b + c * d. Архитектура PowerVR Rogue позволяет выполнять 2 такие операции за такт, что дает большую плотность операций по сравнению с точностью highp:
Вместо 20 тактов с точностью highp, с mediump получили 15 тактов.
Смешанное использование точностей
Установка точности highp по умолчанию и выборочное понижение точности до mediump работает плохо. Лучший результат дает установка mediump по умолчанию и выборочное повышение точности там, где это необходимо. Приведем некоторые часто встречающиеся случаи, где требуется высокая точность:
Перемещение вычислений выше по pipeline
Типичный сценарий рендера на GPU предполагает следующее соотношение сущностей:
Туториал по Unreal Engine: Cel Shading
Благодаря физически точному рендерингу в Unreal Engine 4 удобно разрабатывать реалистичные игры. Модель рендеринга имитирует взаимодействие света с материалами, что приводит к созданию реалистичной картинки. Однако если вы хотите разработать игру со стилизованным внешним видом, то вам придётся исследовать другие техники.
Один из способов создания стилизации — использование cel shading (также известного как toon-шейдинг). Эта техника подражает затенению, обычно используемому в мультфильмах и аниме. Примеры её использования можно увидеть в таких играх, как Jet Set Radio, The Legend of Zelda: The Wind Waker и Gravity Rush.
В этом туториале вы научитесь следующему:
Примечание: В этом туториале подразумевается, что вы уже знакомы с основами Unreal Engine. Если вы новичок в Unreal Engine, то вам стоит изучить сначала туториал из десяти частей Unreal Engine для начинающих.
Приступаем к работе
Скачайте заготовку проекта и распакуйте её. Перейдите в папку проекта и откройте CelShader.uproject. Вы увидите следующую сцену:
Это персонаж, к которому мы будем применять cel shading. Прежде чем приступать, нужно разобраться, что же такое cel shading.
Что такое Cel Shading?
Cel shading — это процесс рендеринга с помощью нескольких полос цвета, а не непрерывного градиента.
Ниже представлен пример использования cel shading в The Legend of Zelda: Breath of the Wild. Заметьте, что cel shading реализован только для персонажа, а фон остался обычным.
На этом изображении есть три полосы: одна для теней, одна для средних тонов, и ещё одна для светлых участков.
Часто ошибочно считается, что cel shading применён, если у объектов есть контуры. Примером этого может служить Borderlands. Хотя эта игра имеет стилизованный внешний вид, на самом деле в ней нет cel shading. Это можно увидеть на изображении ниже. Заметьте, что в расцветке персонажа не используются полосы цвета.
Контуры не являются cel shading, но их часто используют совместно. Благодаря этому картинка становится похожей на нарисованную краской или тушью. Такой приём часто используется в играх с аниме-стилистикой, таких как Guilty Gear Xrd и Dragon Ball FighterZ.
В следующем разделе мы узнаем, как реализовать cel shading.
Способ реализации Cel Shading
Ограничение этого способа заключается в том, что на объекты с cel-шейдингом не могут влиять другие источники освещения. Кроме того, объекты не могут отбрасывать тени на объекты с cel-шейдингом.
Чтобы решить эту проблему, мы должны использовать другой способ. Вместо вычисления скалярного произведения мы будем вычислять освещённость поверхности. Затем при задании пороговых значений можно будет использовать это значение вместо скалярного произведения.
Теперь, когда вы знаете, что такое cel-шейдер и как он работает, настало время его создать.
Создание Cel-шейдера
В этом туториале cel shading будет эффектом постобработки. Постобработка (Post processing) позволяет изменять изображение после того, как движок закончит его рендеринг. Постобработку обычно используют для таких эффектов, как глубина резкости, motion blur и bloom.
Для создания собственного эффекта постобработки нам нужно воспользоваться материалом постобработки (post process material). Перейдите в папку Materials и создайте новый Material. Переименуйте его в PP_CelShader и откройте.
Чтобы преобразовать материал в материал постобработки, нужно изменить его домен (domain). Перейдите в панель Details и измените Material Domain на Post Process.
Первый шаг в создании cel-шейдера — вычисление освещённости каждого пикселя. Мы назовём это буфером освещения (lighting buffer).
Вычисление буфера освещения
Когда Unreal рендерит изображение на экран, то сохраняет проходы в буферы. Для вычисления буфера освещения нам нужно будет получить доступ к двум таким буферам:
Для доступа к этим буферам нужно использовать нод SceneTexture. Создайте его, выберите и перейдите в панель Details. Чтобы получить доступ к буферу Post Process Input, измените Scene Texture Id на PostProcessInput0.
Для доступа к Diffuse Color создайте ещё один нод SceneTexture. Измените его Scene Texture Id на DiffuseColor.
Буфер освещения должен содержать только значения в градациях серого (описывающие степени освещённости пикселей). Это значит, что нам не потребуется информация о цвете из обоих буферов. Чтобы отбросить цвета, соедините выход Color обоих нодов SceneTexture с Desaturation. Это полностью обесцветит оба буфера.
Для вычисления буфера освещения просто разделим SceneTexture:PostProcessInput0 на SceneTexture:DiffuseColor. Порядок здесь важен!
Затем используем Clamp, чтобы значения оставались в интервале от 0 до 1. Это упростит создание порогов, потому что мы будем знать возможные значения.
Вот визуализация буфера освещения:
Как вы видите, освещённые области ближе к белому, а неосвещённые — к чёрному.
Затем мы используем буфер освещения для создания порога.
Создание порога
В нашем cel-шейдере любой пиксель со значением больше 0,5 будет использовать обычный диффузный цвет. Пиксели со значениями меньше 0,5 будут использовать диффузный цвет половинной яркости.
Для начала создадим нод If. Он позволит нам сравнивать два значения. В зависимости от результатов сравнения мы сможем указывать разные выходы.
Далее соединим Clamp со входом A. Затем создадим Constant со значением 0.5 и соединим её со входом B.
Примечание: для изменения порога можно менять значение входа B.
Чтобы получить цвета, создадим SceneTexture и зададим для его Scene Texture Id значение Diffuse Color. Затем умножим Color на 0.5, чтобы получить диффузный цвет половинной яркости.
И наконец, соединим всё следующим образом:
Использование материалов постобработки
Чтобы использовать материалы постобработки, нам нужно создать Post Process Volume. Он обычно используется для управления такими эффектами постобработки, как баланс белого, насыщенность и контрастность.
Нажмите на Apply и вернитесь в основной редактор. Для создания Post Process Volume перейдите в панель Modes и выберите категорию Volumes. Затем перетащите Post Process Volume во Viewport, чтобы создать его.
Теперь нам нужно сказать Post Process Volume, чтобы он использовал cel-шейдер. Выбрав Post Process Volume, перейдите в панель Details. Затем найдите Rendering Features\Post Process Materials и нажмите на значок +. Так вы добавите в массив новый элемент.
Затем нажмите на раскрывающийся список Choose и выберите Asset Reference.
Это позволит выбрать материал. Нажмите на раскрывающийся список None и выберите PP_CelShader.
По умолчанию Post Process Volume воздействует только тогда, когда мы находимся внутри. Однако в нашем случае нужно, чтобы он влиял на весь мир. Для этого прокрутите до Post Process Volume Settings и включите Infinite Extent (Unbound).
Теперь, когда cel-шейдер применён ко всей игре, мы увидим следующее:
«Постойте-ка, это не похоже на тот cel-шейдер, который вы показывали раньше!»
Главная причина такого отличия в том, что движок применяет cel-шейдер после тональной компрессии. Чтобы исправить это, нам нужно попросить движок использовать cel-шейдер перед тональной компрессией.
Применение Cel Shading перед тональной компрессией
Прежде чем показать изображение игроку, Unreal выполняет процесс, известный как «тональная компрессия» (tonemapping). Одна из целей тональной компрессии — сделать изображение более естественным. Она берёт входной цвет, а затем использует кривую, чтобы сдвинуть его в новое значение.
Вот два изображения, до и после тональной компрессии:
Как вы видите, светлые участки до тональной компрессии слишком яркие. Однако после тональной компрессии яркие области становятся более мягкими.
Хотя тональная компрессия полезна для изображений, которые нужно отображать, мы не должны выполнять тональную компрессию для изображений, которые хотим использовать в вычислениях. Из-за смещения значений мы будем использовать не те значения, которые ожидаем.
Откройте PP_CelShader и убедитесь, что ничего не выбрано. Затем зайдите в панель и найдите раздел Post Process Material. Задайте Blendable Location значение Before Tonemapping.
Нажмите на Apply, а затем вернитесь в основной редактор. Цвета теперь выглядят гораздо лучше!
В следующем разделе мы научимся тому, как применять cel shading только к отдельным объектам.
Изолирование Cel Shader
Чтобы изолировать эффекты постобработки, нам нужно использовать функцию под названием Custom Depth. Как и Post Process Input с Diffuse Color, это тоже буфер, который можно использовать в материалах постобработки.
Прежде чем понять, что такое Custom Depth, вам нужно разобраться с буфером Scene Depth. Scene Depth хранит отдалённость каждого пикселя от камеры. Вот как выглядит визуализация Scene Depth:
Custom Depth хранит ту же информацию, но только для выбранных вами мешей. Вот его визуализация с викингом, отрендеренным в Custom Depth:
Сравнивая Scene Depth с Custom Depth, мы можем изолировать объекты. Если Scene Depth меньше, чем Custom Depth, то мы используем обычное изображение. Если Scene Depth больше Custom Depth, то используется изображение с cel-шейдингом.
Первый шаг — рендеринг викинга в Custom Depth.
Использование Custom Depth
Перейдите в World Outliner и выберите SK_Viking. Затем перейдите в панель Details и найдите раздел Rendering. Затем включите Render CustomDepth Pass.
Далее нам нужно выполнить сравнение глубин. Откройте PP_CelShader и создайте следующую схему:
Примечание: ноды Mask ( R ) являются масками компонентов (Component Mask). Они позволяют преобразовывать многоканальные данные в скалярные значения. Наложить маску на Scene Depth и Custom Depth нам нужно потому, что нод If для входов A и B принимает только скалярные значения.
Затем соедините выход сети cel shading с A > B. Наконец, соедините выход только что созданного If с Emissive Color.
Теперь cel shading будет применяться только к мешам, отрендеренным в Custom Depth.
Нажмите на Apply, а затем вернитесь в основной редактор. Вы увидите, что cel shading теперь выполняется только для викинга.
Сel-шейдер работает замечательно, но он довольно прост. Что, если нам понадобится большее количество полос? Что, если мы захотим создать более плавные переходы между полосами? Всё это можно реализовать с помощью таблиц поиска (lookup tables) (LUT).
Что такое «таблица поиска»?
В детстве мы учились тому, что такое умножение. Однако юный мозг не всегда мог выполнять такие вычисления. Поэтому вместо расчётов вы могли пользоваться таблицей умножения для «поиска» ответов.
По сути, это и есть LUT. Это массив значений (обычно предварительно вычисленных), к которым можно получить доступ с помощью входных данных. В случае таблицы умножения входными данными являлись множитель и множимое.
В контексте нашего cel-шейдера LUT — это текстура с неким градиентом. Вот четыре примера того, как может выглядеть LUT:
Пока мы вычисляем цвет тени, умножая диффузный цвет на 0,5. Вместо умножения на постоянную 0,5 мы будем использовать значение из LUT. Благодаря этому мы можем управлять количеством полос и их переходами. Понять, как будет выглядеть затенение, можно по внешнему виду LUT.
Прежде чем использовать LUT, нужно изменить некоторые из её параметров текстуры.
Изменение параметров LUT
Перейдите в папку Textures и откройте T_Lut_01. Вот, как выглядит LUT:
Первый параметр, который нам нужно изменить — это sRGB. При рендеринге Unreal преобразует все текстуры со включенным sRGB в линейный цвет. Это упрощает движку выполнение вычислений рендеринга.
Параметр sRGB полезен для текстур, описывающих внешний вид. Однако текстуры наподобие карт нормалей и LUT содержат данные, предназначенные для математических вычислений. Поэтому Unreal должен считать их значения уже верными. Если отключить sRGB, то Unreal не будет выполнять преобразование в линейный цвет.
Для этого снимите флажок sRGB. Этот параметр находится в разделе Texture.
Следующий параметр, который нам нужно изменить — способ тайлинга текстуры. Поскольку мы не будем отображать эту текстуру, то тайлинг ей не нужен. Более того, если оставить тайлинг включённым, то это добавит проблем при сэмплировании на краях текстуры. Например, если мы будем сэмплировать пиксель с левого края, то из-за тайлинга он попытается смешаться с правым краем.
Для отключения тайлинга измените значение X-axis Tiling Method на Clamp. Тоже самое сделайте для Y-axis Tiling Method.
И на этом мы закончили с параметрами. Теперь нам нужно использовать LUT в материале постобработки.
Использование LUT
Закройте T_Lut_01 и откройте PP_CelShader. Сначала удалите выделенные ноды:
Затем создайте Texture Sample и измените его Texture на T_Lut_01. Эта таблица LUT будет создавать три полосы с плавным переходом.
Как мы помним, для определения значений, которые нужно выводить, LUT используют входные данные. В нашем случае в качестве входных данных будет использоваться буфер освещения.
Чтобы реализовать это, соедините Clamp с UVs в Texture Sample.
Это работает, потому что значения буфера освещения и координаты текстуры находятся в интервале от 0 и 1. Например, если пиксель из буфера освещения равен 0,5, то LUT подаст на выход значение пикселя из середины текстуры.
Далее нам нужно умножить диффузный цвет на LUT. Для этого воссоздайте следующую схему:
Мы используем Append для преобразования вывода Texture Sample’s в четырёхканальный вектор. Это нужно нам, потому что мы не можем умножать трёхканальный вектор на четырёхканальный (SceneTexture).
Наконец, соединим всё следующим образом:
Теперь вместо умножения диффузного цвета на константу мы умножаем его на значение из LUT. Так мы контролируем количество полос цвета и их переходы (зависящие от LUT). Выводимое значение LUT определяется буфером освещения.
Нажмите на Apply, а затем закройте PP_CelShader. Теперь затенение будет иметь три полосы с более плавными переходами между полосами.
Ниже представлено сравнение того, как могут выглядеть различные LUT. Эти LUT тоже добавлены в проект.
Куда двигаться дальше?
Готовый проект можно скачать отсюда.
Как вы видите, материалы постобработки — очень мощный инструмент. Они позволяют создавать множество реалистичных и стилизованных эффектов. Если вы хотите больше узнать о постобработке, то изучите документацию по постобработке UE.





Subscribe to our Telegram channel to be the first to know about news and our exclusive materials on information security.
















































