Как сделать отбрасывание в unity

Обновлено: 03.07.2024

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 ).

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

Unity рендерит реалтайм тени с помощью самого распространенного на данный момент алгоритма - Shadow Mapping.

Shadow Mapping

Shadow Mapping - это алгоритм рендеринга динамических теней, где информация о тенях хранится в текстуре. Главным его достоинством является быстрая отрисовка теней геометрически-сложных объектов.

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

Теперь посмотрим, как происходит рендер одного кадра нашей сцены. В этом нам поможет инструмент для отладки frame debugger . Откываем Windows->FrameDebugger.
Нажимаем кнопку Enable.
Перед нами появилось окно с какими-то непонятным вещами внутри.

Разложим все по полочкам.
1 . Ползунок, показывающий номер шага. В моем случае номер шага равен 4, а всего 24 шага. Хотя правильно называть это не шагами, а draw call'ми.
2 . Окно вызовов отрисовки (draw call'ов). Здесь находятся сгруппированные draw call'ы.

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

3 . Окно информации о draw call'е, выбранном во втором окне. Здесь есть данные о шейдере, pass'е(проходе), ключевых словах, смешивании, текстурах, векторах, матрицах, текстурах и т.д.

Что же нам показывает frame debugger ?

Рендер depth texture

Прежде , чем рендерить геометрию, unity делает дополнительный проход, в котором создает depth texture(текстуру глубины). Это текстура, в которой каждый пиксель содержит информацию о "глубине" обектов на сцене.

  • Размер текстуры глубины совпадает с разрешением экрана
  • Значения пикселей в текстуре глубины находятся в диапазоне от 0 до 1 с нелинейным распространением. 0 - далеко от камеры, черный цвет. 1 - близко к камере, белый цвет.
  • Показывает только видимые объекты. Т.е. если один закрыт другим объектом, то информация о нем не будет занесена в текстуру глубины.
  • Рассчитывается с "точки зрения" камеры в clip space //(пространство камеры)

Рендер shadow map

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

Идея Shadow mapping очень проста - мы поместим камеру в положение источника света и отрендерим для нее depth map. Её мы будем использовать для проверки видимости объектов. По сути, shadow map будет показывать, какое расстояние прошел луч от источника света, прежде чем столкнулся с объектом. Если расстояние(в shadow_space) от источника света до точки меньше, чем расстояние, закодированное для этой-же точки в shadow map, то это значит, что эта точка затенена.

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

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

  • Размер текстуры задается в настройках теней
  • Значения пикселей в текстуре глубины находятся в диапазоне от 0 до 1 с нелинейным распространением. 0 - далеко от камеры, черный цвет. 1 - близко к камере, белый цвет.
  • Показывает только видимые объекты. Т.е. если один закрыт другим объектом, то информация о нем не будет занесена в текстуру глубины.
  • Рассчитывается с "точки зрения" источника света в clip space
  • Рендерится для каждого отдельного источника света

А почему их четыре ? Это все потому, что мы используем четыре теневых каскада в сцене. Подробнее о них мы поговорим чуть позже.

Сборка теней

Итак, что мы имеем ? У нас есть карта глубины с точки зрения камеры, у нас есть карта глубины с точки зрения источника света. Конечно, они сохранены в разных системах отсчета ( clip spaces), но мы знаем пространственные отношения и направления "взгляда" обоих точек зрения. А это в свою очередь означает, что мы можем сравнивать глубину depth map и shadow map. Другими словами, мы можем сравнивать два вектора(вектор, выходящий из источника света и вектор, выходящий из камеры). Если они, заканчиваются в одной точке и эта точка затенена, то мы сохраняем цвет тени в отдельную текстуру. Если же вектор света или вектор взгляда сталкиваются по пути с каким-либо объектом и не достиюте точки, то она не затенена или ее не видно, следовательно, нам не нужно сохранять значение тени в ней. В итоге получается текстура с тенями. На значения, сохраненные в ней, умножается цвета объетов во время рендера сцены.

clip space shadows final scene

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

  1. Создание карты глубины камеры (depth map)
  2. Создание карт глубины всех источников света (shadow map)
  3. Вычисление теней
  4. Сборка теней

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

Теперь , когда мы знаем, как происходит ренер теней, можно перейти к изучению их настроек.

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

В Quality Setting мы устанавливаем параметры для всех источников света. Тем не менее, некоторые из них можно будет переопределить уже на каждом конкретном источнике света в зависимости от его задачи.
1 . Shadows - этот параметр отвечает за качество теней.

  • Diable Shadows - тени не будут рендерится
  • Hard Shadows Only - будет происходить рендер только жестких(hard) теней
  • Hard and Soft Shadows - будет происходить рендер мягких(soft) и жестких(hard) теней в зависимости от настроек источника света

жесткие тени мягкие тени

2 . Shadow Resolution - настройка разрешения карты теней. Чем больше разрешение, тем качественнее на сцене будут тени

  • Low Resolution - размер карты теней с низким разрешением (1024x1024 )
  • Medium Resolution - размер карты теней со средний разрешением (2048x2048 )
  • High Resolution - размер карты теней с высоким разрешением (4096x4096)
  • Very Hight Resolution - размер карты теней с очень высоким разрешением (не особо понятно, в чем разница, размер карты теней 4096x4096)

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

Чем больше текстура, тем качественнее тени, но и ресурсов будет использоваться больше. Особенно это касается памяти. А зачем нам рендерить качественные тени для объектов, находящихся далеко от камеры ? Для них вполне бы подошли текстуры с маленьким разрешением. Эту проблему решают теневые каскады.

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

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

3 . Shadow Projection - параметр, на основе которого выбирается теневой каскад. По умолчанию используется Stable fit.

  • Close Fit - в зависимости от глубины камеры
  • Stable Fit - в зависимости от расстояния до камеры

Close fit cascade Stable fit cascade

4 . Shadow Distance - расстояние от камеры, после которого тени не будут рендерится. По умолчанию стоит 150. При установки значения shadow distance = 8, тень от дальней сферы уже не рендерилась. Так же, при маленьком shadow distance необходимость в теневых каскадах отпадает.
Чем меньше shadow distance, тем быстрее рендерится сцена. У меня количество draw call'ов уменьшилось с 24 до 16.

5 . Shadowmask Mode - в каком режиме будет работать shadowmask (текстура, в которой сохранены тени от статических объектов на другие статические объекты. Используется в mixed-lighting'е)

  • Shadowmask - статические объекты отбрасывают всегда запеченные тени
  • Distance Shadowmask - использование реал-тайм теней до shadow distance, а статических после нее

6 . Shadow Near Plane Offset - смещение ближнего плана(near plane) камеры, которая рендерит теневую карту. По умолчанию равно 3.

7 . Shadow Cascades - количество теневых каскадов.

  • No Cascades - без теневых каскадов, тени ограничиваются только shadow distance
  • Two Cascades - два теневых каскада
  • Four Cascades - четыре теневых каскада

8 . Cascade splits - Панель, где можно настроить расстояние, на котором активируется и выключается каждый из каскадов. Расстояние считается в процентах от shadow distance.

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

1 . Shadow Type - тип теней.

  • Hard Shadows - жесткие тени
  • Soft Shadows - мягкие тени

-
2 . Strength - коэффициент, на который умножаются тени при их сборке. 0 - нет теней, 1- черные тени.

3 . Resolution - разрешение карты теней для данного источника света.

  • Use Quality Settings - использует значение, которое мы выставили в Quality Settings
  • Low Resolution - размер карты теней для данного источника света 512x512
  • Medium Resolution - размер карты теней для данного источника света 1024x1024
  • High Resolution - размер карты теней для данного источника света 2048x2048
  • Very Hight Resolution - размер карты теней для данного источника света 4096x4096

4 . Bias - коэффициент, который добавляется к глубине. Он как-бы немного вдавливает тень. По умолчанию равен 0.05. Если выставить слишком большое значение, тень слишком далеко отодвинется от объекта и будет казаться, что она отдельно от него, если слишком маленькое, появится эффект shadow acne.

5 . Normal Bias - делает все то-же самое, что и bias, только выравнивание идет по нормалям объекта. Если установить слишком высокое значение, то тень станет слишком "вжатой в себя" или очень узкой.

6 . Near Plane - смещение ближнего плана камеры для данного источника света.

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

PCF - это алгоритм фильтрации, основанный на усреднении значений между соседними текселями тени в shadow map. В нем для каждого пикселя мы берем значения соседних пикселей в Shadow Map и усредняем их. Получается своеобразный переход из затененной в освещенную область. Узнать поподробнее о PCF, а также о других алгоритмах можно здесь

Пишем шейдер

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

  1. Предмет должен отбрасывать тени
  2. Шейдер должен принимать тени от directional light'а
  3. Тени должны быть мягкими

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

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

Итак , перейдем к реализации отбрасывания теней. Если мы сейчас откроем frame debugger, то увидим, что наша unlit сфера не рендерится ни в shadowmap, ни в depth map. Это происходит потому, что в шейдере unlit сферы нет прохода с тэгом "ShadowCaster", из которого движок берет информацию о глубине на основе положения объекта.

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

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

Чтобы правильно скорректировать положение сферы в зависимости от bias'а, мы применим функции, реализованные в " UnityCG.cginc ":

UnityClipSpaceShadowCasterPos - переводит точку из model space в clip space с поправкой на normal bias

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

Пару вводных фраз о самих эффектах, чтобы понимать что к чему куда и как. Мое личное мнение, что 70% отлично выглядящего эффекта составляет правильно сделанная, качественная текстура. Сюда же входят цвета, наилучшим образом передающие суть эффекта, ее размер, анимированная она или нет, с альфа каналом или без. Текстура всегда должна быть оптимальной. К примеру, нет смысла делать маленькую искру на листе 256х256. Тут достаточно 8х8 или 16х16. Оставшиеся 30% успеха создаются умело настроенным поведением частиц. Чтобы развить этот навык – смотрите больше референсов, подмечайте различные эффекты в жизни, в кино, в играх. Изучайте физику вещей. Очень хороши замедленные съемки всякого рода взрывов, выстрелов, горящего огня и т.д.
Начнем. Перед тем, как настраивать эффект необходимо определиться из каких частей он будет состоять. В нашем примере – это пламя, искры, дым и немного искажения в стиле горячего воздуха в пустыне . Всегда помните, что чем проще и оптимальнее эффект, тем больше их можно использовать на сцене и тем меньше все будет тормозить. Далее мы определяемся с общим видом эффекта – что это будет. Собираем референсы, изучаем, как ведет себя огонь в горящих бочках, что там горит – это дает понимание какой будет дым по цвету и по интенсивности. Бывает неплохо набросать эффект, несколько фаз его развития на бумаге, чтобы понимать что делать. Своеобразная раскадровка.
Запускаем Юнити. У меня версия 4.1. В какой-то из предыдущих версий была переделана система частиц, поэтому на очень старых версиях редактора урок может быть не актуален. Из меню выбираем GameObject>Create Other>Particle System. У нас появилась система частиц с летящими белыми пятнами. В правом нижнем углу вьюпорта должно появиться маленькое окошко, которое помогает запускать симуляцию эффектов, останавливать их, ставить на паузу, менять скорость проигрывания (ВНИМАНИЕ! Скорость самого эффекта изменяется не здесь).Это позволяет отловить различные артефакты и косяки при очень быстрых эффектах, например выстрел. Чтобы понимать размер эффекта относительно мира я рекомендую настраивать на уже готовых объектах. В нашем случае – это бочка. Ставим бочку в координаты 0,0,0, вводя их в свитке Inspector в окошки сабсвитка Transform. Если свитка нет, то в меню Window включите его, а так же свиток Hierarchy. Далее выбираем нашу Particle System и ставим ее так же в 0,0,0. И чуть поднимаем вверх, чтобы огонь горел не на самом дне, а начинался чуть выше. Систему частиц можно переименовать, кликнув по ней в свитке Hierarchy и нажав F2. Я назвал нашу систему частиц FireMetalBarrelы.

ОГОНЬ

рис. 002 Выбирать эмитеры(после того, как Вы их создали, конечно) можно в свитке Hierarchy.

Первое, что мы должны сделать, чтобы увидеть не дефолтные белые шарики, а нужную текстуру – это сделать материал и текстуру огня. Сразу оговорюсь, что делать текстуры то еще развлечение. Достаточно долгая и кропотливая работа. Но как я уже говорил – это стоит того в итоге. Я использую свои давние наработки, которые я делал для игры Deep Black. Для текстур огня и дыма я использовал FumeFX плагин для 3dsmax. Моделировал различные взрывы, интенсивность, направления и т.д. Потом рендерил в серии кадров. Так я получал различной интенсивности и размера огненные всполохи. С дымом чуть проще. Как? Расскажу позже.

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

Возвращаемся в наш редактор частиц, выбираем эмитер FireMetalBarrel и сразу же переходим в самый нижний сабсвиток Renderer. Ставим галочку (это значит, что он не будет игнорироваться и будет активен ) и разворачиваем его кликом по нему. Render Mode оставляем Billboard. Это значит, что при любом угле обзора текстуры частиц всегда будут направлены в камеру. Снимаем галочки Cast и Receive Shadows так как шейдер материалов, типа Particles не принимает тени и не отбрасывает их. Перетаскиваем наш заготовленный материал в строчку Material и видим что-то отдаленно похожее на огонь. Материал можно так же назначить выбрав его из всех доступных, нажав маленький кружок обведенный зеленым цветом.

Возвращаемся в начало нашего свитка FireMetalBarrel и начинаем настраивать один сабсвиток за другим. Сразу подскажу, что наведя мышку на любой параметр вы увидите всплывающую краткую подсказку о том, что он значит и как работает. Все настроенные параметры я приведу на скриншотах. У некоторых параметров есть опции – они обозначены маленькими черными треугольничками справа от параметра. Constant -постоянная величина, Curve – изменение параметра по кривой, например размер от малого к большому и опять к малому. Random between two constants – случайное значение в промежутке от одного до другого указанных значений. Random between two curves – то же, что и предыдущее, только крайними значениями выступают кривые.

Опишу действие параметров:

Duration – общая продолжительность эффекта. Как показала практика особо не влияет при зацикленных эффектах.

Looping – зацикленность

Prewarm – как бы прогрев эффекта, чтобы он начался уже с основной интенсивностью

Start Delay – задержка в старте проигрывания в секундах

Start Lifetime – Время жизни в секундах

Start Speed – скорость, с которой стартуют частицы

Start Rotation – стартовая повернутость частицы на определенный градус. В нашем случае от 0 до 360.

Start Color – стартовый цвет. Можно регулировать по альфе или по цвету.

Gravity Multiplier – множитель гравитации. 9,82 м/c– гравитация для планеты Земля.

Inherit Velocity – наследуемая скорость для следующих частиц.

Simulation Space – система координат. В нашем случае – world. При Local позволяет частицам перемещаться вместе с эмитером, как например, двигатель космолета. При world – частицы ведут себя, как факел, который несут, т.е. отстают от эмитера.

Play on Awake – частицы стартуют автоматически.

Max Particles – максимальное кол-во частиц одновременно проигрываемых из этого эмитера.

Emission

Rate – количество частиц генерируемое в секунду.

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

Shape

Shape – форма эмитера. Конус, сфера, полусфера, бокс, меш.

Angle – угол разброса. В нашем случае 15 градусов.

Radius – радиус эмитера.

Emit from – откуда генерируются частицы. Из одной точки, из всего тела эмитера и т.д.

Random Direction – Задает случайное направление движения частиц. Помогает создать некую мелкую хаотичность в нашем случае с генерацией языков пламени.

Velocity over Lifetime

Параметр позволяет рандомайзить скорость движения частиц в течение их жизни. В нашем случае система координат world и направление вверх, т.е. по Y. Случайная скорость от 0.5 до 1. Получается, что языки пламени движутся с разной скоростью.

Force over Lifetime

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

Color over lifetime

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

рис. 007 Тут можно регулировать цвета и альфаканал.

Rotation over Lifetime

Параметр позволяет придать некоторое вращение в течение жизни. В нашем случае от -20 до 30 градусов. Частицы вращаются в случайные разные стороны с медленной скоростью, что тоже придает реализма.
Остальные параметры нам оказались без надобности, но Вы можете поэкспериментировать и попытаться настроить еще лучше .

ДЫМ.

Если Вы все сделали правильно, то у Вас должно получиться довольно неплохое пламя. Но, сам эффект еще не готов и на половину и поэтому не стоит расстраиваться, если огонь далек от идеала. Сейчас я расскажу о том, как сделать дым. По сути с ним все просто – это тот же самый огонь лишь клонированный и немного перенастроенный

и с материалом (текстурой дыма). Копируем так – выбираем наш эмитер огня FireMetalBarrel, нажимаем Ctrl+D переименовываем в Smoke и перетаскиваем новый эмитер прямо на FireMetalBarrel, чтобы эмитер Smoke стал дочерним объектом и все они вместе образовывали иерархию. Все это делается в свитке Hierarchy. Подробнее хочу остановиться на том, как сделать текстуру дыма. Тут я не использовал никаких плагинов – только старый добрый Фотошоп и Гугл . Ищем картинку одинокого облака в Гугле. Я нашел вот такую

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

Кстати я рекомендую стараться делать все текстуры кратные степени двойки по сторонам, типа 128х128, 1024х512, 64х16 и т.д. Это сэкономит Вам уйму времени, если надумаете использовать их потом еще в других проектах.
Создаём новый материал Smoke и настраиваем его по образу и подобию материала огня с той разницей, что используем немного другой шейдер и другую текстуру.

Вводим параметры, как показано на рисунке 008.

ИСКРЫ.

Искры, по сути, тот же клонированный огонь с измененными параметрами и другим материалом. Назовем этот эмитер Spark. Настройки для него показаны на рис. 012.

Текстуру можете сделать в Фотошопе – это маленькая точка или точка с небольшим свечением вокруг. Размер текстуры, как я уже говорил в начале 16х16 или около того. Шейдер – Particles/Additive.

ГОРЯЧИЙ ВОЗДУХ.

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

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

Ну и финальный результат смотрите на видео:

Да, с появлением Сюрикена, работать с частицами в Юнити стало удобнее и приятнее) Хороший урок, многим будет полезен.

Шейдеры в Unity: введение в тему от Cyanilux

Привет! Это небольшое введение в тему шейдеров, что это такое и как они используются при рендере графики в Unity.

Что такое меш?

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

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

Даже такая, казалось бы, простая фигура, как куб, должна быть разбита на треугольники.

Пример кубического меша, состоящего из 12 треугольников. В Blender (ПО для 3D-моделирования) это 8 вершин, однако при импорте в Unity цифра может измениться в зависимости от того, являются ли данные для каждой вершины одинаковыми у общих вершин.

  • Положение в 3D пространстве (пространстве объектов, поэтому (0,0,0) стоит в начале меша).
  • UV, также известные как Текстурные Координаты, поскольку они чаще всего используются для наложения текстуры на модель. Обычно бывают координаты Vector2 (два значения с плавающей точкой, каждая ось помечена как xy или uv), но настоящие UV каналы могут быть Vector4 и содержать для передачи данных до 4 чисел с плавающей точкой. Смотрите Mesh.SetUVs.
  • Нормали (Направления, используемые для шейдинга. Некоторые программы моделирования (например, Blender) могут дополнительно использовать для определения порядка поворота вершин, который показывает в какую сторону смотрит грань. Во время рендеринга в шейдере можно отбросить переднюю или заднюю сторону грани).
  • Касательные (Направления перпендикулярные нормалям, которые огибают поверхность сетки вдоль горизонтальных координат текстуры (uv.x). Используются для построения Пространства Касательных, которое необходимо в таких разновидностях шейдинга как Normal/Bump Mapping)
  • Цвета вершин (Цвет, который задан каждой вершине).

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

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

Пример кубических мешей. Оба имеют по 12 треугольников, но у левого 24 вершины, а у правого 8.

Достаточно распространено, когда модель имеет равномерное затенение (flat shading), а не плавное (smooth shading) как на изображении выше. Равномерное затенение увеличит количество вершин, потому что нормали вершин у разных граней должны указывать в разных направлениях. (Этого может не случиться в самой программе моделирования, но произойдет при экспорте в Unity). Для плавного затенения используется среднее значение направлений, поэтому вершины можно использовать как общие, при условии, что остальные данные также совпадают!

Что такое шейдер?

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

В таких инструментах, как Shader Graph, все это обычно делается за нас, но важно понимать, что здесь происходит два отдельных этапа. В новых версиях есть Master Stack, чтобы четче разделить эти этапы. Он предоставляет нам порт Vertex Position, чтобы переопределить положение в пространстве объектов, прежде чем оно будет преобразовано в пространство отсечения, например, для Vertex Displacement. Мы также можем переопределить нормали и касательные, которые передаются на этап фрагментного шейдера, но Shader Graph обработает порты автоматически, если оставить поля пустыми.

Для каждого треугольника и вершины в этом треугольнике положения пространства отсечения, переданные из стадии вершин, используются для создания фрагментов — потенциальных пикселей на 2D-экране. Все данные для каждой вершины, переданные во фрагментный шейдер, также интерполируются по треугольнику. Вот почему у каждой вершины может быть указан один цвет, но треугольник получится градиентно окрашенным. (То же самое происходит с UV, что позволяет правильно наложить текстуру, а не просто брать цвет как один пиксель для каждой вершины)

Треугольник с интерполированной раскраской.

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

Код шейдера

Шейдеры в Unity написаны на HLSL (High Level Shading Language), хотя обычно вы увидите, что его называют CG. Особенно, если имеете дело со встроенным конвейером (Built-in Render Pipeline).

Вы всегда увидите этот шейдерный код между тегами CGPROGRAM / HLSLPROGRAM и ENDCG / ENDHLSL. (А еще можете увидеть CGINCLUDE / HLSLINCLUDE, который включает код в каждый проход шейдера).

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

Технически Shaderlab имеет несколько устаревших способов создания шейдеров с фиксированными функциями, что означает отсутствие в надобности CG/HLSL PROGRAM, но я бы не стал беспокоиться об их изучении, поскольку программирование шейдеров намного полезнее.

В качестве альтернативы существуют редакторы шейдеров на основе нодов, такие как Shader Graph (официальный, доступен для URP или HDRP), Amplify Shader Editor (работает во всех конвейерах, но не бесплатный) и Shader Forge (больше не поддерживается, работает только в старых версиях Unity).

Проходы шейдера

Обычно шейдеры включают основной этап, который либо не имеет тега LightMode, либо использует один из таких тегов как UniversalForward (URP), ForwardBase (Built-in Pipeline) или Forward (HDRP), если шейдер предназначен для использования в прямом, а не в отложенном рендеринге (я немного объясню это в следующем разделе).

Unity - Полное руководство для начинающих по разработке игр 🔥

🎮 Игры

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

Вступление

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

Почему Unity?

Если вы хотите делать игры

Когда дело доходит до разработки инди-игр, вариантов действительно очень мало. Если вы хотите создавать игры, есть три основных варианта: Unreal, Unity или GameMaker.

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

В Unity есть множество очень успешных игр, таких как Escape from Tarkov (FPS), Monument Valley (Puzzler) и This War of Mine (Стратегия / Выживание).

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


Если вы хотите прототипировать пользовательский опыт

Поскольку Unity — это всего лишь движок с кучей физики, анимации и 3D-рендеринга в реальном времени, это также отличное место для создания полноценных интерактивных прототипов для исследований UX.

Unity полностью поддерживает VR и AR и, следовательно, может стать отличным инструментом для изучения архитектуры, автоматизации и моделирования с помощью клиентов.

Окно редактора Unity

Окно редактора разделено на несколько разделов. Мы расскажем об этом очень кратко, так как будем постоянно к нему обращаться на протяжении всей статьи. Если вы уже знакомы с этим, пропустите мимо!

Просмотр сцены: позволяет размещать и перемещать игровые объекты в сцене.

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

Инспектор: предоставьте подробную информацию о выбранном GameObject в сцене.

Assets / Project: здесь хранятся все префабы, текстуры, модели, скрипты и т. Д.

Иерархия: позволяет вложение и структурирование игровых объектов внутри сцены.

Теперь мы готовы начать!

Объекты Unity Game

Что такое GameObjects

Если у вас есть опыт веб-дизайна, вы можете думать о GameObjects как о элементах

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

Буквально все, от эффектов частиц, камер, игроков, элементов пользовательского интерфейса… (список продолжается) — это GameObject.

Создание иерархии

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

Беспорядок и эффективность

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

Позиционирование

Unity Translation: вы создали группу дронов-помощников, которые летают вокруг игрока. На самом деле вы бы не стали писать код, чтобы они гонялись за игроком, поэтому вместо этого вы создаете их как дочерние элементы игрового объекта player.

Встроенные компоненты Unity

Компонентная модель актера

Unity работает на основе модели компонентов акторов, проще говоря, GameObjects — это актеры, а компоненты — ваши скрипты.

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

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

Ключевые встроенные компоненты

Думаю, пришло время привести несколько примеров встроенных компонентов, предоставляемых движком Unity Games.

  • MeshFilter: позволяет назначать материалы для 3D-сетки GameObject.
  • MeshRender: позволяет назначать материалы 3D-сетке.
  • [Коробка | Mesh] Collider: позволяет обнаруживать GameObject во время столкновений.
  • Rigidbody: позволяет реалистичному физическому моделированию воздействовать на GameObjects с 3D-сетками и запускать события обнаружения на коллайдерах боксов.
  • Свет: освещает части вашей сцены.
  • Камера: определяет область просмотра игрока, которая будет прикреплена к GameObject.
  • Различные компоненты холста пользовательского интерфейса для отображения графического интерфейса пользователя

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

Создание пользовательских компонентов

Структура моноповедения

Ключевые функции

Все компоненты наследуются от класса MonoBehaviour. Он включает в себя несколько стандартных методов, главное:

  • void Start (), который вызывается всякий раз, когда объект, содержащий скрипт, создается в сцене. Это полезно в любое время, когда мы хотим выполнить некоторый код инициализации, например. установить экипировку игрока после того, как он появится в матче.
  • void Update (), который вызывается каждый кадр. Это то место, где будет выполняться основная часть кода, включающего пользовательский ввод, обновляющего различные свойства, такие как движение игрока в сцене.

Переменные инспектора

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

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

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

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

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

Принятие пользовательского ввода

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

Управление игровыми объектами

Трансформации

Все GameObjects имеют свойство transform, которое позволяет выполнять различные полезные манипуляции с текущим игровым объектом.

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

В общем, рекомендуется использовать локальное [Положение, Вращение], а не глобальное положение / поворот объекта. Обычно это упрощает перемещение объектов разумным образом, поскольку ось локального пространства будет ориентирована и центрирована на родительском объекте, а не на мировом начале координат и направлениях x, y, z.

Преимущества локального пространства станут немного более очевидными с диаграммой!

Если вам нужно преобразовать между локальным и мировым пространством (что часто бывает), вы можете использовать следующее:

Создание новых игровых объектов

Поскольку GameObjects — это в основном все в вашей сцене, вы можете иметь возможность генерировать их на лету. Например, если у вашего игрока есть какая-то пусковая установка для снарядов, вы можете захотеть создавать снаряды на лету, у которых есть собственная инкапсулированная логика для полета, нанесения урона и т. Д.

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

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

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

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

Доступ к другим игровым объектам и компонентам

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

Доступ через переменную инспектора

Это самый простой способ. Просто создайте общедоступную переменную для GameObject, как мы продемонстрировали ранее с префабами, и вручную перетащите ее на компонент через инспектор. Затем перейдите к переменной, как указано выше.

Доступ через теги

Мы можем пометить GameObjects или prefabs через инспектор, а затем использовать функции поиска игровых объектов, чтобы найти ссылки на них.

Доступ через преобразование

Доступ через SendMessage

Raycasting

Есть два сценария, в которых это может пригодиться (вероятно, их гораздо больше):

Обнаружение столкновений

Ранее мы упоминали компоненты Collider и Rigidbody, которые можно добавить к объекту. Правило для столкновений состоит в том, что один объект в столкновении должен иметь твердое тело, а другой — коллайдер (или оба имеют оба компонента). Обратите внимание, что при использовании raycasting лучи будут взаимодействовать только с объектами, к которым прикреплены компоненты коллайдера.

После настройки в любом настраиваемом компоненте, прикрепленном к объекту, мы можем использовать методы OnCollisionEnter, OnCollisionStay и OnCollisionExit для реагирования на коллизии. Получив информацию о столкновении, мы можем получить ответственность за GameObject и использовать то, что мы узнали ранее, для взаимодействия с прикрепленными к нему компонентами.

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

Расширенные возможности

Мы не будем вдаваться в подробности сейчас, но, возможно, в следующей статье — просто чтобы вы знали, что они существуют.

Создание графического интерфейса

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

Расширение редактора Unity

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

Анимация

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

Материалы и PBR

Unity использует физический движок рендеринга, который обеспечивает освещение в реальном времени и реалистичные материалы. Реальность такова, что вам нужно либо сначала изучить 3D-моделирование, либо использовать модели, созданные и оптимизированные кем-то другим, прежде чем вы доберетесь до этого, чтобы создавать вещи, которые действительно хорошо выглядят.

Совет новичкам по Unity

Если вы планируете написать свою первую игру, не стоит недооценивать сложность и время, необходимое для написания даже самых тривиальных игр. Помните, что над большинством игр, которые выходят в Steam, команды работают над ними в течение многих лет!

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

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

Хорошие ресурсы для разработки игр в Unity

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

Читайте также: