В предыдущем посте было про интерективные снежинки ❄️ на SwiftUI. А что на счет тех же снежинок но на более низком уровне? Да, делаем тоже самое но на языке Metal Shading Language (MSL).
Опустим настройку работы с Metal. Посмотреть как это сделать, можно в репозитории.
Для начала вспомним логику движения снежинок. Кстати, логику оставим внутри swift кода, а саму отрисовку вынесем на шейдер.
Каждая снежинка 1 раз в кадр изменяет свою позицию:
snowflake.position.y += snowflake.velocity.y
Снежинки появляются сверху и если попадают вних экрана, то мы их перемещаем вверх, что и позволяет сделать бесконечный цикл снега:
if snowflake.position.y > screenSize.y {
snowflake.position.y = 0
snowflare.position.x = Float.random(in: 0…screenSize.x)
}
Все так, логика максимально простая.
Шейдер не менее простой, но так как она написан на другом языке и имеет не привычный интерфейс от Metal, пришлось довольно долго экспериментировать и смотреть туториалы.
Разберем шейдеры по кусочкам.
Будем использовать два шейдера:
Вершинный шейдер - отвечает за позицию x.y нашей снежинки
Фрагментный шейдер - отвечает за цвет пикселей снежинки, мы вернем просто белый цвет
Нам нужно добавить всю информацию из swift кода в Metal код. Эта информация в структуре Snowflake.
struct Snowflake {
float2 position;
float size;
};
Дальше эту информацию нужно преобразовать в данные вершинного шейдера:
// Данные для позиции и размеры шейдера
struct VertexOut {
float4 position [[position]];
float pointSize [[point_size]];
};
// Шейдер вершин. Здесь происходит преобразование swift данных в данные шейдера
vertex VertexOut vertex_main(const device Snowflake *snowflakes [[buffer(0)]],
constant float2 &screenSize[[buffer(1)]],
uint vertexID [[vertex_id]]) {
// Здесь происходит преобразование координат из SwiftUI в координаты Metal
float2 position = convert_to_metal_coordinates(snowflakes[vertexID].position, screenSize);
VertexOut out;
out.position = float4(position, 0, 1);
out.pointSize = snowflakes[vertexID].size;
return out;
}
В конце остается фрагментый шейдер. В нем мы закругляем снежинку и возвращаем белый цвет:
fragment float4 fragment_main(VertexOut fragData [[stage_in]],
float2 pointCoord [[point_coord]]) {
// Закругляем снежинку
if (length(pointCoord - float2(0.5)) > 0.5) {
discard_fragment();
}
return float4(0, 0, 0, 0);
}
Готово, шейдер полностью написан, запускаем и видим пархающие снежинки ❄️❄️❄️
А бонусом, этот шейдер можно конвертировать под OpenGL, DirectX и в целом запускать почти везде. Далее попробую сделать такое же приложение под Windows.
☃️ Скачать можно через brew:
brew install --cask barredewe/cask/snowfallPS: Если интересен более подробный обзор, то дайте знать в комментариях
#blog