Как сделать темноту в юнити

#7 Shadows

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Содержание

1 Вступление
2 Краткая теория
2.1 ShadowMapping
2.2 Рендер Depth Texture
2.3 Рендер Shadow Map
2.4 Сборка Теней
2.5 Настройка Теней в unity
2.6 Алгоритм фильтрации Percentage Closer Filtering
3 Практика. Пишем шейдер
3.1 Отбрасывание теней
3.2 Получение теней

Вступление

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

Краткая теория

Начнем мы, как всегда, с теории. Для начала разберемся, что такое тени и какими они бывают в юнити.
Когда на пути светового луча попадается какой-либо объект, то на поверхности, находящийся за ним, образуется темный силуэт этого объекта. В таких случаях мы говорим, что объект отбрасывает тень. В реальном мире переход из темной области в освещенную происходит плавно, образуя полутень (с англ. penumbra ).

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Shadow Mapping

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

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Это запрос отрисовки, направленный графическому API. Чем запросов больше, тем выше нагрузка.
Для оптимизации draw call’ы однотипных объектов объединяют (батчат / batching)

Рендер depth texture

Рендер shadow map

Далее по списку идет рендер карты теней, которая используется для shadow mapping’а.

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Т.к. directional light распространяется равномерно и независимо от расстояния, то для него будет использоваться ортогональная камера. Плюс ко всем, directinal light олицетворяет собой солнце, а значит, по идее, расстояние от него до каждой точки должно быть одинаковым, а shadow map очень большой. На самом деле это не так. Расстояние для рендера Shadow map выбирается в зависимости от положения камеры.

Вот так примерно выглядит shadow map для нашей сцены.

Сборка теней

В итоге мы имеем такой алгоритм shadow mapping’а в юнити:

Настройка теней

Открываем Edit->Project Settings->Quality

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

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

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

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

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

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Это были общие настройки для теней. Также в unity можно настроить отображение теней для каждого отдельного источника света.

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Алгоритм фильтрации Percentage Closer Filtering (PCF)

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Пишем шейдер

!Переходим! к практике!

Создадим новый материал, для него создадим новый Unlit шейдер и применим его к сфере.
Как видно на изображении ниже, сфера перестала отбрасывать тень, т.к. в unlit шейдере нет её реализации.

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Отбрасывание теней (Casting Shadow)

Добавим дополнительный проход в наш шейдер:

В этом проходе нам важна только позиция, поэтому просто переведем положение объекта из object space в clip space.
Ну, собственно, вот и все. Теперь наш шейдер пишет глубину, а это значит, что объект отбрасывает тень.
Но тут возникает небольшая проблема: у нас не производится смещения в зависимости от bias’а и normal bias’а.

Источник

Как включить тёмную тему редактора Unity (обновлено)

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

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

Подготовка

Предполагается, что Unity уже установлен. Иначе зачем любопытный %UserName% читает эту статью? Обычно исполняемый файл редактора 64-битной версии Unity располагается по пути:

Будем использовать этот путь для примера, поэтому скорректируйте под свою ситуацию в случае отличия. Настоятельно рекомендую сделать резервную копию! Чтобы потом не терять время на переустановку или поиск оригинального файла если пойдёт что-то не так, как задумано:

Затем скачиваем и распаковываем себе в любое удобное место x64dbg из раздела Releases. На момент написания статьи, самой новой была версия «snapshot_2017-12-26_13-39.zip». Запускаем x64dbg с помощью лаунчера x96dbg или же напрямую из его подкаталога:

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Меняем одно условие

Итак, пришла пора действовать. Открываем меню File => Open (также горячая клавиша F3 или первая пиктограмка на панели инструментов) и шагаем в каталог установленного Unity, выбираем всё тот же исполняемый файл редактора «Unity.exe» о котором упоминалось выше, получим примерно следующее:

Переходим на вкладку Symbols и слева в списке модулей выбираем unity.exe:

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Интерфейс может чуток притормаживать при обработке больших списков, поэтому терпеливо пишем в поле Search строку «GetSkinIdx»:

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Двойным кликом по строке результата поиска переходим по его адресу на вкладку CPU:

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Здесь нас интересует инструкция jne. Опять таки двойным кликом по ней открываем диалоговое окно, в котором меняем эту инструкцию на je, остальное не трогаем:

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

После применения правки, открываем контекстное меню и выбираем Patches (Ctrl+P):

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

При помощи кнопки Patch File сохраняем новый Unity.exe временно куда-нибудь, потому что текущий файл занят отладчиком:

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Проверка результата

Поздравляю, всё выполнено успешно. Остаётся лишь закрыть отладчик и перезаписать оригинальный Unity.exe тем файлом, который сохранили при помощи Patch File. Убедиться в том, что не затронули случайно чего лишнего, можно сравнив по содержимому резервную копию с новым файлом, должен отличаться только один байт и больше ничего:

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Запускаем и наслаждаемся тёмной темой оформления:

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Вполне вероятно, что в Unity перепрячут тёмную тему и описанный тут метод перестанет работать. Но как известно, против лома нет приёма. Также может наоборот произойти чудо, наконец-то прислушаются к пользователям и таки сделают её бесплатной. Если устали ждать и хочется ещё больше кастомизации — рекомендую присмотреться к Zios Themes: описание на форуме Unity + исходники на GitHub.

Источник

Как я делал 2D тени в Unity

Что первое приходит в голову разработчику инди-игры, когда он сталкивается с необходимостью добавления фичи, представления о реализации которой толком не имеет? Разумеется, он идёт искать следы тех, кто уже прошёл этот путь и удосужился записать свой опыт. Так поступил и я некоторое время назад, приступая к созданию теней в своей игре. Найти нужную информацию — в виде статей, уроков и гайдов — не составило особого труда. Однако, к моему удивлению, я обнаружил, что ни одно описанное решение мне попросту не подходит. Поэтому, реализовав своё собственное, я решил поведать о нём миру.

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

Сам результат перед вами:

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

А подробности пути к его достижению ждут вас под катом.

Постановка задачи

Итак, на момент принятия решения о добавлении в игру теней, у меня имелось:

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

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

Реализация

Собственно, основная загвоздка оказалась в том, что Dwarfinator, грубо говоря — 2,5D игра. Подавляющее большинство объектов существует в двухмерном пространстве с осями X и Y, а ось Z используется крайне редко. Визуально же, и отчасти, геймплейно, ось Y используется для отображения как высоты, так и глубины, разделяясь таки образом на виртуальные оси Y и Z. Использовать в такой ситуации для создания теней стандартные средства Unity не представлялось возможным.

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

Выглядела такая синхронная анимация примерно так:

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

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

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

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

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

Ниже можно наблюдать пример вывода этой камеры:

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

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

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

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

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

Но тут меня поджидала новая неприятность. С тенями и десятками новых объектов максимальное количество одновременно находящихся на сцене GameObject, а вместе с ними и компонентов Animator и SpriteRenderer, возросло более чем в два раза. Когда я выпускал на локацию всю волну мобов, что составляло порядка 150 штук, Profiler укоризненно показывал мне примерно 40мс, уходивших только на рендеринг и анимацию, а частота кадров в целом колебалась в районе 10. Я отчаянно оптимизировал собственные скрипты, борясь за каждую миллисекунду, но этого было недостаточно.

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

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

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

Но кое-что мне не давало покоя. Объект-тень, являясь в реальной сцене потомком объекта-хозяина, технически входил в тот же самый SortingGroup, однако с динамическим батчингом объектов-теней проблем не возникало. Единственное отличие было в том, что объекты-хозяева отрисовывались основной камерой сразу на экран, а объекты-тени — сначала в RenderTexture.

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

Скриншот ниже демонстрирует пример отрисовки слоя сущностей.

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Итого в общем случае рендеринг некоторой сущности в координате Y выглядит так:

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

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

Нюансы

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

Следующий скриншот показывает пример вращения снарядов и их теней.

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

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

На гифке ниже изображён пример перемещения объекта по высоте.

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

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

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

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

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

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

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

Как сделать темноту в юнити. Смотреть фото Как сделать темноту в юнити. Смотреть картинку Как сделать темноту в юнити. Картинка про Как сделать темноту в юнити. Фото Как сделать темноту в юнити

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

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

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

Источник

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

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