Хитрости игровой разработки #4

В этой части речь пойдет о спрайтах, частицах и одном очень интересном эффекте из World of Warcraft.

Заметки пишутся от лица Simon Trümpler.

Другие части:

Первая часть

Вторая часть

Третья часть

Пятая часть

Приятного чтения!

▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬

Company of Heroes – Огнемет

Недавно мне на глаза попался вот такой эффект для огнемета из Company of Heroes. Выглядит довольно интересно, так что давайте попробуем детально разобраться как он реализован:

Если вы уже в курсе как устроены системы частиц, то продолжайте чтение с 1-го (чуть ниже) пункта.

▬▬▬

Частица это, чаще всего, полигон, который постоянно повернут к камере:


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

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

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

▬▬▬

– 1 – Для создания такого эффекта нужно что-то большее чем простая система частиц:

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


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


Это спрайт. Сперва я подумал, что это текстура движется вдоль геометрии, но при ближайшем рассмотрении становится ясно, что движется сама геометрия(особенно это видно на верхнем конце этого плейна). Я не уверен на 100%, но мне кажется, что они покрасили его с помощью vertex color так, что верхняя часть становится оранжевой и желтеет ближе к основанию.  

Но как они избежали ситуации, в которой наблюдатель может увидеть этот эффект сбоку? Вот и ответ:

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

Также советую заглянуть в тред на тему спрайтов в играх.

Пример похожего эффекта из UE

World of Warcraft – Шар

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

Сперва я подумал, что разработчики просто создали шейдер с красным эффектом френеля. Это помогло бы окружить шар красной виньеткой, а после просто обрезать её по маске и оставить нужную информацию ближе к центру объекта. Конечно же я ошибался. Сперва, с помощью DirectX Ripper и WoW Modelviewer я нашел некоторую интересную информацию в файлах игры. Вот, к примеру, текстуры этого шара:


На первых двух картинках всё ожидаемо, но вон тот градиент снизу вызывает интерес. Это ведь градиент, который используется для создания описанного мною эффекта! Но как? Я решил проверить модель шара в model viewer.

Плохая новость: эффект тут не видно.

Плохая новость #2: Я понадеялся, что большой полигон, сетку которого видно в вьювере, поможет получить ответ на мой вопрос, но он не помог…

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

Но теперь к хорошим новостям! Благодаря Neox мы узнаем ответ буквально через несколько секунд. Давайте я объясню: техника, которую они(Blizzard) использовали, называется Lit Spheres Shading. Суть её состоит в динамическом переназначении цвета(наподобие Gradient Map в Photoshop). Но, в отличии от Gradient map в Photoshop, Lit Spheres Shading использует не 1D градиент, а 2D текстуру. Если вы не понимаете о чем я говорю, советую почитать тред от Peppi. Он написал похожий шейдер, который вы можете скачать и потестить самостоятельно.  


Насколько я понимаю, они используют нормали (screen space) объекта, чтобы отрисовывать этот эффект в нужных местах. Чем больше нормаль “смотрит” от камеры (на краях сферы), тем сильнее угасает градиент. Таким образом, шар всегда подкрашен в центре ярким желтым цветом, что и создает этот замечательный эффект.

Вот ещё один хороший пример. Плейн вращается, так что нормали постоянно меняют свое направление, что приводит к динамическому перекрашиванию vertex color’а.

На данный момент остался лишь один вопрос: Как они создали маску для обрезания нарисованного паттерна? Мое предположение: они использовали маску, записанную в alpha канал карты diffuse. Большая часть красной ткани шара в альфа канале выкрашена в черный, но ближе к центру видно белый паттерн. Возможно, он находится там именно для этого. Звучит вполне логично как по мне, но кто знает как оно есть на самом деле.. А что вы думаете на этот счет?

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


half fresnel = pow(saturate(dot(normal, viewDir)),sizeoftheglow);half mask = tex2D(mymask,uv).r; // mask stored in a the R channel
half3 color = half3(mycolor); // 0.4,.02,0.01 for example

half3 selfillumfresnel = fresnel* color+(bias* color)*mask;

Вот пример реализации из UE4:

UE4

P.S

А также примечательно, что в half-life 2 episode 1 использовали точно такой же эффект для создания вот этого инопланетного монстра с картинки ниже:

half-life 2 episode 1

Спасибо за внимание!

▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬

P.S

Если хотите видеть переводы раньше всех - подписывайтесь на телеграм канал - https://t.me/CGTranslate

Все советы, предложения и критику пишите в мою личку телеграма @DenisNik или на почту treedestudio@gmail.com

Перевод был подготовлен в рамках проекта CgTranslate.