Настало время оптимизации нашей иры-головоломки от первого лица. В посте будет много картинок, видео и НЕ БУДЕТ математики / программирования. Вся "математика" будет описана на пальцах, показаны только общие идеи. Итак, под оптимизацию попали лазерные лучи.
До этого в grayBox мы использовали юнитевский LineRendere.
Достоинства:
- берется из коробки
- гибкий, хорошо подходит для создания прототипа лазерного луча ну или чего вам заблагорассудится
Недостатки:
- работает неоптимально.
Неоптимальность незаметна на одном, 2-х и даже на десяти лучах. Но вот если у вас в игре лазер, который отражается от зеркал, делится на несколько лучей при прохождении через призму, имеет несколько цветов... то тут уже стоит прикинуть сколько +drawCalls накинут сверху только лучи. Ну, у нас могло набегать 4, 8 и даже 30 drawCalls.
Этот пост будет интересен, пожалуй, не программистам/разработчикам игр. Их этим не удивишь, а вот обычынм людям будет интересно посмотреть отдаленно на то, сколько усилий вбивается лишь для того, чтобы в игре были лазерные лучи.
До того как браться за луч, сделал наброски источника лазера и приемника. Свел их по дизайну между собой, ну и с другими элементами. Скетки источника лазера, надеюсь что-то будет понятно.
3/4, сбоку, вразрезе, чтобы моделлер лучше понял конструкцию:
Работает в целом так: луч попадает в приемник и применик передает сигнал о том, что в него попали. На основе этого разворачивается одна из игровых механик.
Больше скетчей, если интересно, выкладываю сюда:
https://twitter.com/CGAleksey
https://www.instagram.com/cgaleksey/
На https://vk.com/dronesgame пока ничего не выкладываю, но если добавятся больше 1.5 человека XD, то и туда начну скидывать.
По коду.
Почему не взял какой-то готовый плагин. Взял, результат не понравился. Пробовал Vectosity. Вот я, пишу несамый читабельный код. Но код мой и пишу так, что я могу его понять так как проект делаю для себя. А вот создатель Vectosity я бы сказал накрутил каких-то неральных сложностей. В его коде я так и не смог разобраться чтобы устранить баг, который мне мешал. Вот баг, тектсура как-то деформируется или хз что с ней:
Написал разработчику Vectosity чтобы узнать в чем проблема, когда исправит. С его слов это неисправимый косяк GPU. Короче нафиг-нафиг-нафиг... потом буду заикаться от него из-за того что будут вспоминать добрым словом.
Итак поехали. Что нужно:
- система рисования всех лучей на GPU за 1drawCall;
- возможность задавать разные текстуры и прочие параметры лучам.
Для решения этой задачи придется писать код не только на стороне процессора, но и на стороне GPU. Идея такая: рейкастим на пути луча все что видим, отражаем луч от предметов, которые отражаю. В итоге получаем набор координат. Этот набор координа - это начала и концы сегментов лазерного луча.
Этот набор параметров (array) мы подаем в шейдер при генерации меша на GPU.
Так же мы подаем на GPU набор параметров (тоже array): цвет, индек текстуры, толщина луча и всего чего вам еще вздумается.
И да, лучше чтобы эти параметры подавал какой-то менеджер. То есть в менеджер мы подаем какие лучи нарисовать, а он собирает данные и передает их на GPU.
Сам лазерный луч будем рисовать так:
1) на концах луч должен быть светлее так как это выглядит естественно. Используем grayscale-маск текстуру для этого;
2) переход от центра луча к перифирии (цвет и прозрачность) задаются grayscale-маской;
3) ну и добавим какой-нибудь шум, типа cloud и будем анимировать его, чтобы имитировать туман или движение воздуха.
И да, желательно склеить маски и все на свете в одну текстуру, не цари чтобы баиндами разбрасыватья.
Как по точкам на GPU нарисовать линию. Идея такая:
- в шейдере линия имеет вектор направления (начало-конец)
- есть доступ к позиции камеры.
Ну и все, делаем в шейдере сross(lineDir, camDir) и получаем направление вектора толщины линии. Передаем в шейдер параметром толщины и им можем регулировать толщину линии.
На словах все просто, на деле ушло много времени. Вот рисую линии на GPU по координатам:
Как видно, все ровненько и красиво и текстуру не колбасит как на предыдущем видео. И что там за проблемы с GPU у него были... Так можно отрисовать хз как много линий, зависит от того сколько вершин Unity разрешает использовать (если не ошибаюсь). Вроде 65к в последний раз было максималкой, а это куча линий, мне хватит :)
Версия сырого луча и приемника луча:
Это видео с грейбокс-тестов. Ну, бокс как видно, совсем не gray XD Еще здесь нет нормальной градиентной маски на луче. Луч сильно упрощен: (нет тумана, градиент подобран на скорую руку, концы луча не засветлены... короче, артист не занимался тогда еще этим)
Качество графики у нас не такое, на видосе часть прототипа с минимальной графикой и моделями, которые используются только в процессе разработки:
В итоге линия (лазерный луч) выглядит так. Здесь уже все:
- градиенты настроены
- туман пост-процессом наложен поверх полупрозрачного луча
- концы засветлены
- есть немного анимации тумана
- вся отрисовка на стороне GPU за 1 drawCall, красота :)
Надеюсь кому-то пост понравился и кто-то нашел что-то полезное для себя.
Еще раз ссылки на материалы по игре:
https://twitter.com/CGAleksey
https://www.instagram.com/cgaleksey/
На https://vk.com/dronesgame
Спасибо за внимание :)