Базовые FX на GMS2. Сложные эффекты
В предыдущих постах был обзор системы частиц и частично фишки по сношению частиц с кодом. Но не частицами едиными, как говорится. В некоторых задачах частицы вообще не нужны или выполняют только вспомогательную роль, а используется программная генерация и шейдеры.
Погнали!
Молнии, ага. Их уже не сделать с помощью системы частиц. Это не массив объектов, это цельный, протяженный, хаотично-упорядоченный объект, который отрисовывается, зачастую сразу полностью (ну в реале типа так проиходит по ощущениям).
Я не претендую на труЪ истину, я просто опишу задачу и вариант решения, который пришел в голову мне.
Итак имеем:
-яркий протяженный объект, который меняет свою внутреннюю структуру
для того, чтобы отрисовать подобную дичь - можно воспользоваться массивом точек, создав примитив:
vertex_format_begin();
vertex_format_add_position();
vertex_format_add_colour();
vertex_format_add_textcoord();
v_format = vertex_format_end();
v_buff = vertex_create_buffer();
vertex_begin(v_buff, v_format);
вот так он выглядит в общем виде. В этот примитив вносятся данные по каждому вертексу, а пользователей сможет определить после каким макаром отрисовывать этот примитив (как квадрат или круг, только не предустановленный, а нагенеренный вами же).
функция отрисовки выглядит так:
vertex_submit(buff, pr_trianglelist, sprite_get_texture(sprite_index));
тут юзер сам решает что (buff), каким макаром (pr_trianglelist) и с использованием чего (prite_index) отрисовывать. Т.е. берётся массив точек из buff, и по типу построения pr_trianglelist(разрозненные треугольники) отрисовывается.
В нашей задаче молния непрерывна, поэтому функция отрисовки идет через pr_trianglestrip.
чтобы сформировать саму загогулину, приходится мотать вертекс буфер по циклу, в каждой итерации прибавляя к текущему положению с помощью lengthdir_x/lengthdir_y значения для точек следующего шага. Честно говоря, тут я жоско втупил и полез даже на форум спрашивать совета, однако первый же ответ спровоцировал меня перепроверить свои вычисления и я нашел ошибку со знаками. Зря людей тревожил.
Уже под конец, когда перешел к отладке вспомнил, что не худо бы у молнии изменять этот самый шаг приращения с каждым разом в меньшую сторону, пришлось вспоминать прогрессии... спасибо школе, что я знаю об их существовании.
в принципе, на этом хитрости заканчиваются(нет). И можно получить "молнию", распространяющуюся из точки в направлении на определённую дистанцию.
Но что делать с монохромным зигзагом? Тут как раз пригодятся шейдеры. Совсем простой frag-шейдер. Вертексный тут нам вообще не нужно трогать.
varying vec2 v_vTexcoord;
uniform float cur_alpha;
uniform float col_r;
uniform float col_g;
uniform float col_b;
void main()
{
vec4 col = vec4 (col_r,col_g,col_b,0.0);
col.r *= (1.0-(abs((v_vTexcoord.x-0.5)*2.0)));
col.g *= (1.0-(abs((v_vTexcoord.x-0.5)*2.0)));
col.b *= (1.0-(abs((v_vTexcoord.x-0.5)*2.0)));
gl_FragColor = col;
gl_FragColor.a = (1.0-sqrt(abs(v_vTexcoord.x-0.5)*2.0)) * cur_alpha;
}
Он просто принимает из объекта(функции, откуда укажит пользователь) значения для цветов по ргб каналам, значение для корректировки альфы и всё это обсчитывает, основываясь на значениях текстурных координат. Тут надо пояснить, что текстурные координаты берутся из точки и я при формировании сам устанавливаю их для левых 0, для правых 1 (ну или наоборот, кто их знает). А в шейдере получаю градиент от границы объекта к центру. Градиент для цвета линейный. Градиент для шейдера - через квадратный корень. Кому интересно разница вот в чем:
т.е. для корня в моём случае будет "острее" серединка, т.е. место где прозрачности не будет, потом резкий спад и плавное затухание. Этакая аура вокруг центральной части. Модификатор cur_alpha я тут использовал для реализации мерцания всей молнии... чтобы она просто не выглядела статичной, а "жила", как любят говорить всякие художники...
Шейдер вызывается из объекта. Вот функция отрисовки конечного результата:
shader_set(shader_lightning)
//color settings to shader
shader_col_r = shader_get_uniform(shader_lightning, "col_r");
shader_col_b = shader_get_uniform(shader_lightning, "col_b");
shader_col_g = shader_get_uniform(shader_lightning, "col_g");
shader_set_uniform_f(shader_col_r,0.6)
shader_set_uniform_f(shader_col_g,0.6)
shader_set_uniform_f(shader_col_b,2)
//alpha settings to shader
shader_alpha = shader_get_uniform(shader_lightning, "cur_alpha");
shader_set_uniform_f(shader_alpha,random_range(0.8,1.2))
for (var i=0;i < ds_list_size(vb);i++)
{
cvb = ds_list_find_value(vb,i)
//draw call
vertex_submit(cvb,pr_trianglestrip,sprite_get_texture(sprite1,0))
//draw call to moar HOT effect
gpu_set_blendmode(bm_add)
vertex_submit(cvb,pr_trianglestrip,sprite_get_texture(sprite1,0))
gpu_set_blendmode(bm_normal)
}
shader_reset()
Что здесь происходит:
цикл прокатывается по всему массиву (vb, в моём случае). И отрисовывает все вертекс буферы, которые в нём лежат (а в него приходится складывать по частям кажду "веточку", потому что если их сцепить в один массив - то между концами потянуться перемычки, которые нам не нужны). Шейдер устанавливается на нужный, в него впихиваются все переменные (только сперва их нужно вызвать), устанавливается по вкусу. Внимательный зритель уже заметил, что в col_b стоит 2, что как бы вызывает сомнения в адекватности автора... на первый взгляд. Но, на самом деле, нет. Этот параметр реально будет работать за пределами единицы, ввиду того, что у нас для расчёта цвета стоит формула с линейным затуханием. Дальше сама отрисовка и тут приходится костылить. Можно закостылить в шейдере, по идее, но мне лень, так как есть более простой способ. Устанавливаем gpu_set_blendmode(bm_add) (режим смешивания для видеокарты) и отрисовывает этот же примитив поверх предыдущего. Add - смешивает цвета вашего объекта с цветами фона под ним. Поэтому нельзя просто так взять и... нужна подложка, без неё всё будет не так красочно.
Ну и "на фоне" (рандомный картинка из интернетофф)
ну и куда же без оружия. LightningGun в студию:
Уж простите за непрезентабельную иконку "игрока" ) Думаю всем понятно, что она тут не главное. Молния выполнена по тому же принципу, с одной лишь разницей:
lightning_direction = (prev_lightning_dir+point_direction(start_pos_x,start_pos_y,end_pos_x,end_pos_y))/2+random_range((i-steps),(abs(i-steps)))
каждый проход молния всё сильнее стремиться попасть в точку назначения, где и заканчивается в итоге, иначе уходила бы она в сраные ебеня, само собой. Ну а "след" - просто несколько последовательно вызываемых партиклов.
З.ы. вопросы в комменты, ссылка на проект, для личного ковыряния: https://yadi.sk/d/HtxPPn8R3R29uU
Лига Разработчиков Видеоигр
7.3K постов22.4K подписчиков
Правила сообщества
ОБЩИЕ ПРАВИЛА:
- Уважайте чужой труд и используйте конструктивную критику
- Не занимайтесь саморекламой, пишите качественные и интересные посты
- Никакой политики
СТОИТ ПУБЛИКОВАТЬ:
- Посты о Вашей игре с историей её разработки и описанием полученного опыта
- Обучающие материалы, туториалы
- Интервью с опытными разработчиками
- Анонсы бесплатных мероприятий для разработчиков и истории их посещения;
- Ваши работы, если Вы художник/композитор и хотите поделиться ими на безвозмездной основе
НЕ СТОИТ ПУБЛИКОВАТЬ:
- Посты, содержащие только вопрос или просьбу помочь
- Посты, содержащие только идею игры
- Посты, единственная цель которых - набор команды для разработки игры
- Посты, не относящиеся к тематике сообщества
Подобные посты по решению администрации могут быть перемещены из сообщества в общую ленту.
ЗАПРЕЩЕНО:
- Публиковать бессодержательные посты с рекламой Вашего проекта (см. следующий пункт), а также все прочие посты, содержащие рекламу/рекламные интеграции
- Выдавать чужой труд за свой
Подобные посты будут перемещены из сообщества в общую ленту, а их авторы по решению администрации могут быть внесены в игнор-лист сообщества.
О РАЗМЕЩЕНИИ ССЫЛОК:
Ссылка на сторонний ресурс, связанный с игрой, допускается только при следующих условиях:
- Пост должен быть содержательным и интересным для пользователей, нести пользу для сообщества
- Ссылка должна размещаться непосредственно в начале или конце поста и только один раз
- Cсылка размещается в формате: "Страница игры в Steam: URL"