ASCII-шейдер
Первоисточник я не нашёл, но, там откуда брал, упоминались имена Lex Fridman и Andy Sloane.
Пысы: документация: https://www.a1k0n.net/2011/07/20/donut-math.html , спасибо @leshq
Godot путь новичка. MainMenu ч4. Рефакторинг(Предпосылки)
Добрый вечер, мои дорогие подписчики. Это продолжение поста Godot Путь новичка. Main menu ч3. Рефакторинг(Теория)
Уже можно сказать по традиции, немного поплачусь и изложу свои соображения, по происходящему. Во-первых хочется выразить огромную благодарность тем кто читает, это действительно дает огромный заряд мотивации и не только. Мне уже доводилось слышать теорию, что если хочешь что-то изучить, попробуй начать делиться своим опытом. Это по аналогии с очень хорошим приемом, задавать вопрос уточке. Как только по хорошему сформулировал вопрос для уточки, уже можно бежать к техлиду и спрашивать, как сделать эту приблуду, а пока бежишь приходит осознание, что решение-то вот же оно, прямо в вопросе и было) И стоишь как дурак, перед техлидом, все осознавший, благодаришь за помощь, а он знай себе хитро улыбается, ни слова, не сказав. Он то понял, что ты понял и безмерно этому рад, поскольку теперь ты не мешаешь ему подбирать билд для вечернего рейда, на спину смертокрыла. Так и тут, в меру сил своих пытаясь что-то объяснить, на самом деле сам учусь очень радостными темпами, прям инсайт за инсайтом.
Во-вторых, появилась какая-то размеренность и я бы даже сказал неотвратимость, все делается медленно и неспеша, но регулярно. "Медленно как меласса течёт" - Джон Рут Вешатель. Проникся этим персонажем, да и фильм обожаю. Другой бы убил и дело с концом, но если за дело берется Джон Рут, будь уверенна тебя повесят. В этом на самом деле очень много смысла, медленно, неторопливо, очень аккуратно и вдумчиво, но он всегда доводит дело до конца и поэтому он лучший, это приносит ему плоды и оплата у него одна из самых высоких. С другой стороны шайка стартаперов, наплодивших кучу простых и топорных решений, все же выпилила его. Количество переходит в качество рано или поздно. Как итог оба подхода хороши, что выбрать каждый решает сам) Но если количества было бы не достаточно, для перехода в качество, то они могли выгореть раньше, чем достигли результата. По факту так и произошло, локальный успех был достигнут(победа в джеме, релиз с минимальными продажами), но конечная цель(хит хитовый) достигнута не была, все выгорели. В идеале желательно, чтобы был на проекте один медленный вдумчивый сеньор-архитектор и шайка спринтеров, пускай себе там скрамят, покер на карточках устраивают, прокрастинируют, любой каприз, лишь бы задачи удобоваримо закрывались.
К чему это написал, вот сейчас у меня прям уверенность, что сделаю проект. Очень простой и маленький, вот в прямом эфире так сказать. Ориентировочный срок - три года на разработку, ну по хорошему любую оценку программиста надо бы умножать на три. 9 лет меня тоже устраивает в принципе. Никакой гонки, никакого выгорания, а это важная часть. Важнейшая, никакие технические навыки и красота картинки не спасут проект, если его главный креакл выгорит и забьет. Потому и спешить не стану, в целом изначально и говорил на какую аудиторию буду ориентироваться, 15 и 40 лет, одним ещё некуда спешить, а вторым уже некуда.
Work-life balance соблюден и можно по настоящему расслабиться и не просто делать игрульку, а получать настоящее удовольствие от самого процесса, наплевав на мнение хейтеров и достигаторов. Когда нет цели, а есть только путь, то двигаться можно расслабленно и с удовольствием, любуясь окружающими пейзажами и своим гавнокодом. Ведь даже если он плохой, то все равно такой миленький и родненький, когда работает конечно)) Не выгорайте, если сложное что-то делаете и не получается, просто забейте, подумайте можно ли это не делать, а если нужно, то как можно упростить. Или просто забейте, купите скайрим ещё разок или ведьмака 3го, а потом внезапно посреди подземелья, только квен наложил и бац, вот же оно решение, в три часа ночи бежишь к компу, что-то пробуешь)) Красота)
Ну ладно, теперь освежим воспоминания, чего там сын наделал, напомню
https://cs14.pikabu.ru/post_img/2021/08/03/8/162799217811092...
вот такая получилась структура по итогу этого поста Gogot путь новичка. Main menu ч2. События кнопки
В целом то даже кажется компактненько, незатейливо и работает же, с ошибкой правда, но работает. Ошибка была оставлена специально и не замечена сыном, ошибку уберу уже в конце поста, это ошибка не в коде, а в самой игровой логике, хотя казалось бы тут логики то +=1.
Но насчет компактности, не поленился с утра и переписал всё тут имеющееся без использования визуальных компонентов, вот все тоже самое, но только кодом. Сцены такие же оставил, Main и раз уж были им добавлены отдельные MainMenu и Game, то их тоже добавил отдельными сценами, наглядно даже получилось. Но экземпляры не перетаскиваются на дерево сцены, а то же кодом создаются инстансы и добавляются на сцену. Вот что получилось.
Сюда поместил копипасту https://docs.google.com/document/d/1-KLvTbmHMokCL0vKoLF4-osd...
вдруг у кого будет желание попробовать, то можно скопировать оттуда в скрипт Main.
А если лень, то вот картиночки.
Легко заметить, что функциональность идентична, структура проекта один в один, специально запилил все в один класс, для наглядности, потому что именно так и было сделано все на визуальных компонентах у сына. Так что когда вы создаете новую кнопочку например, через "добавить дочернюю ноду", вы используете запись вида
myButton = Button.new()
add_child(MyButton)
перетаскиваете её по экрану, это равносильно методу myButton.set_position(Vector2(150,200))
И так далее, так что казалось бы кнопочки таскаете, а на самом деле очень даже программируете, просто на ещё более высоком уровне) На самом деле более высоком уровне, потому что визуальное программирование ещё дальше от машинного кода, чем высокоуровневые языки. Сразу чувствуется высокий уровень, у самого даже плечи расправились и самомнение подросло)
А чем ниже уровень, тем больше кода, сами можете заметить, что было 15 строчек кода, а стало пиздец. И это ни какой-то абстрактный код для примера, это вполне мог бы быть реально существующий код и он даже мог бы попасть в продакшен, комментарии который я оставил, это мне по ходу написания вспомнились, были у нас такие в репозитории. Разве что кода было бы ещё больше, просто для двух кнопочек. Надо было бы вставить ключи локализации для текстов, добавить тултипы, ну логика была бы чуть посложнее чем +=1. Под условное окошечко используемое в разовом сезонном ивенте с двумя кнопочками, для покупки какой-нибудь недостающей фигулинки и под которое именно сейчас волей геймдизайнеров, ни одно из уже существующих и стандартных не подходит, вполне мог бы быть написан подобный кусок.
Самое интересное, что подобный кусок понятное дело бы не пропустили, сказали бы что у нас вообще-то MVC, ну ка давай переделывай. И вот у нас уже начинает это дело все превращаться не в один класс, а примерно в 10-12. Появляется MainMenuView, MainMenuModel, MainMenuController, аналогично для следующего экрана, для кнопочки прибавляющей 1, как это ни странно, но тоже начинает появляться три класса и ещё появятся вспомогательные классы, которые будут выравнивать интерфейс, в зависимости от ориентации устройства и разрешения экрана. Увлекательных можно вещей наделать конечно. В целом можно даже это переписать для наглядности, ещё бонусом навесить какой-нибудь дурной фреймворк типа PureMVC, кодовая база вырастет ещё втрое как минимум, для поддержания той же функциональности. Самое удивительное, что именно на таких примитивных примерах, всем сразу надо городить очень сложные архитектуры, в этом есть смысл и в этом есть большой смысл именно для программистов.
Сидит фрустрирующий формошлеп на однотипных задачах, надоело ему уже все до чертиков, сложностей не хочется. Подобные задачи и решения вызывают когнитивную легкость с одной стороны для разработчика, навострившегося на них. С другой имеется внешняя запутанность, в них очень непросто разобраться пришедшему со стороны, на создание этого уходит много времени, а это же ещё и как минимум 500 рублей в час. Смысл делать задачу за 15минут, да ещё так, чтобы это было понятно 15-летнему, тогда даже гуманитарии увидят что это же просто, за что тут столько платить тогда? И нет смысла делать колхоз-стайл за два часа, так джуны и студентики увидят, что они делают тоже самое, а получают втрое меньше. А тут и коммит выглядеть будет огого, столь любимую ими активность по гиту можно будет смотреть, эка столько классов, изящная архитектура, низкая связность и тд. Попутно можно на хабре написать статью из серии "Добавить две кнопки - почему так дорого".
Блин бомбануло меня что-то в процессе написания, пост из радостного, стремительно превращается в разжигающий. Я не об этом собирался вроде написать, а получилось как-то так, с другой стороны если в процессе пришел именно к этому, значит это на текущий момент сильнее всего меня тревожит, избыточная сложность. Да пожалуй не стоит это удалять и переписывать заново, оставлю это как напоминание самому себе, что я на верном пути. У меня сами мысли о MVC вызывают жгучую ненависть ко всему живому, сразу никаких нет сожалений о том что ушел с галер. С другой стороны визуальное программирование по сути и реализует цели MVC, в очень даже полной мере, отделить логику от визуального представления. Представление так оно в явном виде и есть представление, самой графикой все и сделано, сигналами связано с логикой, а логика написана с использованием кода. Кажется все вполне себе гармонично, четенько, опять прихожу к тому что Flash это была великолепная технология, просто опередившая время. А Godot в полной мере наследник этой философии, только ещё прекраснее и лишенный многих проблем. Жизнь чудесна и прекрасна, вспомните сколько прекрасных миниигрушек подарил нам флеш. Kingdom rush, для меня лично, так вообще эталонный тавердефенс, на всех платформах покупал. Люди творили и не заморачивались архитектурой, кодом и прочим, просто творили.
Пардон, что получился такой пост, обещаю исправлюсь в скором времени, не только нытьем единым. Вот для затравочки весьма занимательный эффект молнии в увеличенном размере, сделана шейдером. Доберусь уже когда-нибудь до боевки, будем такой молнией хреначить вражин и модифицируем её до цепной молнии.
Water Simulation
Появилась возможность рассказать о том как мы создавали жидкость для TReload. Нам всего лишь нужно было залить уровни кислотой. Кислоты должно быть много, площади затопления огромные :) Один из финальных результатов:
Визуально кислота должна была представлять из себя грязную воду с желтым оттенком. Вот референсы:
Кислота должна поддерживать физическое взаимодействие с объектами, которые в нее брошены: рисовать волны, пену. Так же должна быть возможность видеть сквозь грязь. Возможно в уровнях будет небольшой ветер, но это неточно.
Разработка кислоты проводилась в несколько этапов:
- разрабатывались инструменты для работы с кислотой (в основном это инструмент рисования текстурных масок пены)
- разрабатывалась кислота (работали над шейдерами, материалами, логикой взаимодействия, звуковыми эффектами)
Инструмент рисования текстурных масок пены
Механизм рисования достаточно прост.
Условно есть 2 текстуры:
- текстура маски пены (далее маска)
- текстура кисти (далее кисть)
Задача состоит в том чтобы правильно произвести операцию Blit кисти с маской (использовать для кисти соответствующие “scale” и “offset”, чтобы корректно ее спроецировать в нужную область маски).
Чтобы можно было водить кистью по модели и рисовать, нужно чтобы координаты точки пересечения модели и кисти переводились в пространство UV.
Здесь есть 2 решения по части перевода координат:
- использовать “MeshColluder” и из него получать “texcoord.xy” области пересечения луча “Raycast”. В этом случае координаты будут уже приведены к “UV” виду, нам только останется проецировать текстуру кисти в нужную область маски.
- использовать “BoxCollider” и самостоятельно переводить “worldSpace” координаты кисти к UV координатам маски.
Мы использовали второй вариант:
- к модели кислоты добавляется “BoxColider”
- делается RayCast
- worldSpace точка пересечения луча кисти и кислоты переводится в “acidLocalSpace”
- далее эта точка переводится в “UV-space”. Для этого мы делим координаты точки пересечения на размеры кислоты:
Доработчки: механизм отмены (ctrl+z)
Для ввода механизма отмены пришлось изменить подход: была создана ортографическая камера, которая рендерит только слой кистей. Размеры камеры соответствовали размерам кислоты. В области пересечения кисти и маски создавался меш кисти, который рендерился камерой, а далее делался "Blit" с маской. Таким образом появилась возможность отменять действия.
Небольшая демонстрация работы системы рисования масок:
Волны
Нами предпринимались разные попытки создания волн:
- рисования волн на тектуре кислоты
- волны созданные геометрическим шейдером поверх кислоты
- тесселяция + GPU Instancing и волны
Справа на рисунке представлена волна, которая создана геометрическим шейдером. Волна слева - плоская.
Кстати, про кубики я писал здесь: Немного о дематериализации в нашей игре
В конечном счете мы отказались от таких волн и решили сделать реалистичные волны, которые учитывают интерференцию и генерируют пену в области распространения волны.
Как работает генерация волн
Опишу это простыми словами: есть 2-х мерное уравнение “колебаний”, которое нужно решать каждый кадр. Это уравнение позволяет генерировать распространение волн. С материалом по теме вы можете ознакомиться здесь: ссылка 1
А здесь еще один отличный материал: ссылка 2
Здесь крутой пример исходного кода для Unity: ссылка 3
Мой результат генерации волны (используется стандартная тесселяция от Unity и стандартный шейдер):
Но генерация волн это еще не все. Если у вас маленький бассейн, то примера с Github должно хватить. А если нужно рендерить море или океан, то возникает масса проблем оптимизации:
- оказывается Unity не поддерживает “Tessellation + GPU Instancing of Standard shaders”
- ближние участки кислоты должны быть высокополигональными (для этого нужно использовать систему “LOD”)
- дальние волны, пену можно не рендерить
- артефакты распространения волн
Самое важное я узнал в самом конце. Unity, почему “Tessellation + GPU Instancing” не не работают со стандартными шейдерами? Для решения этой проблемы пришлось посмотреть сгенерированный код Standard-шейдера, вытащить из него то что вам нужно и вставить это в “Fragment shader”.
Структура водной поверхности, распространение воды на соседние сегменты
Водная поверхность представляет из себя NxN объектов с “LOD”. По мере удаления, объекты с LOD подменяют друг друга так, что на расстоянии X вместо 4-х различных объектов с LOD, рисуется один:
То есть водная поверхность - это “умная” сетка из разных участков воды. Допустим, вода имеет размеры 8х8 и пусть источник волн возник в ячейке [2,4]:
Тогда мы резервируем соседние N ячеек (в моем примере по одной с каждой стороны) и проецируем текстуру распространения волн на этот участок общей водной поверхности. Проекция текстуры распространения волн показана красным. То есть мы растягиваем и смещаем текстуру для каждого участка воды. Поиск зоны отрисовки волн в Unity выглядит следующим образом:
Кстати, если источник волн на к краю воды, то мы располагаем текстуру с волнами так, чтобы она не уходила за границы воды (на видео этого нет).
А здесь мы спроецировали текстуру на которой должны рисоваться волны (настроили “tilling & offset”):
Таким образом распространение волны происходит на прилегающие соседние объекты, то есть за пределы одного участка воды.
Вот результаты работы симуляции воды и тесселяции:
Генерация волн от объектов сложной формы
До этого момента я упрощенно рассказал и сослался на литературу, описывающую то как генерировать волны в виде кругов. А что если в воду упадет параллелепипед, капсула или еще какой-то объект (в том числе и невыпуклый). В этом случае форма волн должна быть соответствующей.
Чтобы добиться “реалистичной” формы волн, мы поступили следующим образом:
- падающие в воду объекты рендерятся в текстуру _FallTex. (Ортографическая камера рендерит значения глубины упавших объектов умноженные на скорость падения обьекта)
- далее текстура _FallTex размывается и результат размытия передается в текстуру волн
То есть мы вмешиваемся в процесс симуляции воды, добавляем в симуляцию новые значения (новые источники волн).
Здесь показан результат симуляции волн от объектов сложной формы:
Распространение волн на дальние сегменты
Это одна из проблемных задач. Распространение волн осуществляется за счет использования дополнительных текстур. Игрок не способен летать над водной гладью со скоростью пули и присутствовать то в одном месте, то в другом. Поэтому есть возможность переставать генерировать те волны, которые “далеко”. А распространение тех волн, которые близко, нужно плавно переносить из одного водного сегмента на другой. Здесь видно как ведет себя вода при переходе между разными участками симуляции жидкости:
Чтобы передергов с водой не было, нужно иметь 2 текстуры симулирующие жидкость. Одна из них должна симулировать жидкость, а другая должна перекопировать на себя участок с волнами при перемещении игрока (то есть при смене центрального участка воды). Если этого не делать, то возможны такие баги:
Допустим упавший в воду объект поплыл из [2,4] в [3,4] :
Тогда, чтобы сымитировать цельность водной поверхности, мы копируем часть текстуры текущей симуляции жидкости и вставляем ее во вторую текстуру. Теперь делаем вторую текстуру основной и продолжаем распространение волн на этой текстуре.
Для чего мы проводим операцию копирования? Дело в том, что нам нужно начать новую симуляцию и отобразить в ней результат старой симуляции (на рисунке выше этот результат находится в желтой зоне).
Артефакты
Если объект - источник волн расположен на границе разных водных сегментов, то при копировании текстуры распространения волны могут возникнуть артефакты:
Эти артефакты связаны с тем, что текстура волн является “Clamp”. Для устранения данных артефактов, необходимо учитывать расположение объектов (проверять расположение относительно стыков) и, в случае необходимости, исключать часть объектов из процесса симуляции волн.
Возможны ситуации, когда возникают щели. Они решаются в тесселляцинном шейдере путем уменьшения высоты отклонения волн. То есть высота волн меняется в зависимости от расстояния до камеры игрока.
Вот мои тесты тесселяции и попытки объединения Tesselation + GPU Instancing в Standard shader:
Волны от объектов разной формы:
Простенький, но миленький эффект
Две недели не мог собраться и доделать маленькую писечку, но @LuarZero, своей серией постов вдохновил продолжать и не забрасывать начинание. К слову кто с нуля хочет начать, хоть как-то, путь в геймдев, то загляните к нему. Сам в 2008 году на геймакере пытался склепать клон дисциплов, боевку сделал, а дальше дело не пошло)) Наивная юность, но вызывает ностальгию.
@LeeRoyHero, собственно просил, выкладываю. Простенький эффект "разлагающегося" спрайта.
В моем видении так будет выглядеть смерть от колдунского луча дезинтеграции.
В основе шум Перлина https://godotshaders.com/snippet/2d-noise/
Да и вообще по механике практически тот же эффект телепортации из прошлого поста
https://godotshaders.com/shader/teleport-effect/
Ну и видяшечка с моего тестового стенда так сказать, чтобы посмотреть в динамике. Вообще конечно шейдеры удобно тем, что легко можно к примеру менять цвет эффекта в зависимости от цвета луча который убивает вражину. Ну и что ещё более удобно это не надо заморачиваться и искать аниматора. Да и вообще геймдев всегда идет от ограничений, не можешь сделать сам или нет бюджета на специалиста, ищи обходной путь)
что-то видяшечка пережалась сильно после заливки, но суть думаю понятна. Дискотека века тут не только потому что я апологет армянского барокко, это для наглядности в разных вариантах, при перезапуске вообще рандомный цвет выбирается)
Вообще конечно это планируется ZP-RPG в фентези сеттинге, про бухающих магов в перерыве убивающих зомби и собирающих репу с огорода, чтобы было на что бухать. Но как всегда, за что не берусь получаются какие-то звездные войны) Так что это не танки убивающие повстанцев, это маги убивающие зомбов, рисовать вообще не умею, так влепил что было из ассетов)
Это Godot и тут свой шейдерный язык, но по сути это тот же самый GLSL с немного отличающимся синтаксисом.
Следующий пост будет или про магический/энергетический щит на основе эффекта Френеля если за выходной успею или лазер заебатовский с работы принесу, а то запилил, а флешку не взял с собой. Сейчас танчики стреляют просто палкой с Glow, а там прям как меч джедайский все светится и шевелится))
Немного о дематериализации в нашей игре
А мы все еще работаем в гараже над графической частью игры! Работа над скетчами вроде завершена! Набралась целая стопка изображений и чертежей игровых обьектов, идей по тому как это все подключается и взаимодействует, теперь осталось главное - собрать :)
Перед вами результат работы моего geometry - шейдера :
Шейдер позволяет использовать в игре эффкт дематериализации: рассыпание обьекта на кубики / исчезновения кубиками.
Как это работает в общем виде: в шейдер подается текстура - маска в которой содержатся пиксели - смещения.
Параметр _Burn смещает вершинки в зависимости от значений в текстуре и все это выглядит так, будто кубики двигаются.
А вот и обобщенное видео процесса:
В нашем мире всё (на самом деле почти всё) состоит из виртуальных кубиков, которые дематериализуются. Процесс дематериализации (наиболее приближенный к нашей игре) показан здесь:
Знаю, часто хочется получить готовый шейдер и использовать его в своем проекте.
Одна из версий шейдера (если кому пригодится) предсавлена здесь.
<Шейдер не должен работать на OGL и Metal>
Обновления по проекту обычно выкладываем здесь:
https://vk.com/treload
https://twitter.com/CGAleksey
https://www.instagram.com/cgaleksey/
На этом все, всем хорошего вечера :)
Control в Майнкрафт
Всем добра! Нечего было делать, да решил написать пост о том, чем я занимаюсь уже около года.
Работаю я над одним проектом - воссоздание игры Control в Майнкрафте без модов, с одним оптифайном, шейдером и ресурспаком (для тех кто скажет, что оптифайн - мод, он может ставиться как отдельная версия для майна). Сразу говорю - эта безумная идея не моя, я лишь делаю модели для проекта, а всем делом же руководит один немец, который судя по всему, очень большой фанат обеих игр)
Для наглядности, пару скриншотов из игры:
Да, все скриншоты реальны, пруфы того что это сделано в Майнкрафте есть у самого автора.
К чему я клоню - я очень хочу чтобы этот проект вышел за рамки узкого круга людей и обрёл популярность и среди игроков в России. Если хотите поддержать автора - дискорд сервер:
https://discord.gg/7s7tb2zx8J
Там же есть и патреон)
Всем спасибо за внимание!
Эффект силового поля (шейдер)
Небольшая практика в эффектиках и шейдерах под Unity. Хотелось мне сделать эффект силового поля, в которое попадают что-либо и провоцирует световое пятно, растекающееся по поверхности. Сказано - сделано :)

Немношко матана для любителей: сфера - это обычный меш, эффектом френеля симулируем "энергетическое" поле, с прозрачностью в зависимости от взаимного направления нормали вертекса и направления зрения камеры.
отдаем всё это добро в пиксельный шейдер, где считаем возвращаемый цвет пикселя (на всякий случай добавил ещё и текстуру для поля, какие-нить флуктуации там добавить о.О
тут немношко вкусовщины... Смешение слоёв аддитивом, поэтому перемножение и цвета и альфы влияет на финальное изображение, а именно на переход от прозрачки к непрозрачки. Тут на вкус и цвет, как говорится, особенно поэкономить на этой функции не удастся, так как в любом случае придётся что-то на что-то перемножать, но под себя "поколдовать" можно попробовать.
Ну и сам эффект растекающейся энергетической вспышки
_InpactPoint - это вектор направления попадания (для отрисовки пятна в нужной стороне, передавать его придётся с помощью скрипта(конечное, минус начальное, ну и привести к единичке). Подсказочка:
Нормаль берём из вертекса.
_HitOverlayArea - регулирует величину пятна (плавность пятна зависит от полигонажа сферы, потому что работаем с нормалями вертексов. Тут немношко экономии - нормали берём в вертексном шейдере. Но если экономить не надо - можно нормаль посчитать и во фрагменте, тогда никаких проблем с размерами пятна не будет).
_HitOverlayValue - сила пятна.
_HitColor - собственно цвет для пятна.
Цвет самого поля задается через _TintColor и сохраняется в переменную col (код был выше).
Собственно сам инспектор всего этого добра, для тех, кто не хочет математики, а подёргать ползунки:
Надеюсь, что подписульки понятны лучше, чем названия переменных :)