Для чего нужна данная статья? :
Создать мультиплатформенное графическое приложение с:
- 3D-сценой на движке Bevy, Fyrox или Godot Rust.
- Физикой с Rapier и Legion ECS.
- Низкоуровневым рендерингом через wgpu, Vulkano, OpenGL.
- GUI-интерфейсом (egui, iced, gtk-rs, slint).
- Веб-версией (WebGPU, WebGL, Canvas API).
- Обработкой изображений (image, resvg).
- Вычислениями на GPU (CUDA, OpenCL, wgpu-compute).
Найти альтернативы для работы с графикой:
✅ Игровые движки (Bevy, Fyrox, Godot Rust)
✅ Низкоуровневые графические API (wgpu, Vulkano, OpenGL)
✅ 2D/3D-визуализация (wgpu, raqote, Skia, Cairo)
✅ GUI (egui, iced, slint, gtk-rs)
✅ Веб-графика (WebGPU, WebGL, Canvas API)
✅ Физика и игровые ECS (Rapier, Legion)
✅ Обработка изображений (image, resvg)
✅ GPGPU-вычисления (CUDA, OpenCL, wgpu-compute)
Зачем Вам это уметь? :
1. 2D-графика
Библиотеки для рендеринга 2D-графики:
- wgpu – безопасная и кроссплатформенная графическая абстракция на основе Vulkan, Metal, DX12 и OpenGL.
- vulkano – безопасная обертка над Vulkan.
- glow – безопасная обертка над OpenGL.
- minifb – минималистичная библиотека для создания окон и рендеринга пикселей.
- pixels – простой буфер пикселей поверх wgpu.
- softbuffer – библиотека для прямого управления буфером кадра без GPU.
Библиотеки для 2D-отрисовки и векторной графики:
- image – работа с растровыми изображениями.
- resvg – отрисовка SVG.
- raqote – рендеринг векторной графики в стиле Cairo.
- skia-safe – биндинги для Google Skia.
- cairo-rs – биндинги для Cairo.
2. 3D-графика
Графические движки:
- Bevy – современный ECS-движок с поддержкой wgpu.
- Fyrox – удобный 3D-движок с GUI-редактором.
- Godot Rust – Rust-биндинги для Godot.
- Macroquad – удобный движок без зависимостей.
- Tetra – 2D-игровой движок с OpenGL.
Прямой доступ к графическим API:
- wgpu – поддерживает Vulkan, DX12, Metal.
- vulkano – безопасная работа с Vulkan.
- ash – низкоуровневые биндинги к Vulkan.
- glium – удобный слой над OpenGL.
- glutin – создание окон с OpenGL.
3. GUI (Графический интерфейс пользователя)
Фреймворки на Rust:
- egui – быстрый и удобный GUI на wgpu.
- druid – декларативный GUI с поддержкой piet.
- iced – вдохновлен Flutter.
- slint – декларативный GUI в стиле Qt/QML.
- orbtk – для Redox OS и не только.
- fltk-rs – биндинги для FLTK.
Биндинги для классических GUI-фреймворков:
- gtk-rs – GTK для Rust.
- qt-rs – биндинги к Qt/QML.
- native-windows-gui – Windows GUI.
4. Графика для научных расчетов и визуализации данных
5. Web-графика (WebAssembly + Rust)
- WebGPU (wgpu-rs) – WebGPU в браузере.
- Canvas API – рендеринг в <canvas> через wasm-bindgen.
- WebGL (gl-rs) – биндинги к WebGL.
- Three-d – 3D-рендеринг на WebAssembly.
6. GameDev и физика
- Rapier – физический движок 2D/3D.
- NPhysics – физический движок.
- Legion – ECS-фреймворк для игр.
- hecs – еще один ECS-фреймворк.
7. Инструменты для работы с шейдерами
- naga – транслятор шейдеров.
- spirv-cross – работа с SPIR-V.
- rust-gpu – написание шейдеров на Rust.
8. Графика с использованием GPU (GPGPU)
- cuda-rs – работа с CUDA.
- wgpu-compute – вычисления на GPU через WebGPU.
- ocl – OpenCL в Rust.
- rust-gpu – Rust для GPU.
Давайте создадим простой пример, который рисует прямоугольник и круг на экране.
Перед тем как начать, убедитесь, что у вас установлен Rust и Cargo. Также вам нужно добавить Speedy2D в зависимости вашего проекта, отредактировав файл Cargo.toml:
[dependencies]
speedy2d = "1.0"
После настройки зависимостей, вы можете создать файл main.rs с следующим содержанием:
use speedy2d::color::Color;
use speedy2d::window::{WindowHandler, WindowHelper, WindowStartupInfo};
use speedy2d::{Graphics2D, Window};
struct MyWindowHandler;
impl WindowHandler for MyWindowHandler {
fn on_draw(&mut self, helper: &mut WindowHelper, graphics: &mut Graphics2D) {
// Очищаем экран, используя черный цвет
graphics.clear_screen(Color::from_rgb(0.0, 0.0, 0.0));
// Рисуем прямоугольник красного цвета
graphics.draw_rectangle(
(100.0, 100.0), // верхний левый угол
(200.0, 200.0), // нижний правый угол
Color::from_rgb(1.0, 0.0, 0.0), // цвет
);
// Рисуем круг зеленого цвета
graphics.draw_circle(
(300.0, 300.0), // центр
50.0, // радиус
Color::from_rgb(0.0, 1.0, 0.0), // цвет
);
// Запланируйте следующую перерисовку
helper.request_redraw();
}
}
fn main() {
let window = Window::new_centered("Speedy2D: Simple 2D Graphics", (800, 600)).unwrap();
window.run_loop(MyWindowHandler);
}
Этот пример создает окно с размером 800x600 пикселей и рисует в нем красный прямоугольник и зеленый круг. Метод on_draw вызывается каждый раз, когда требуется перерисовка окна, и здесь мы реализуем нашу логику отрисовки.
Для запуска этого примера, сохраните файл и выполните команду cargo run в терминале из корневой директории вашего проекта. Cargo скомпилирует и запустит ваше приложение, отобразив окно с прямоугольником и кругом.
Давайте адаптируем предоставленный вами код для создания простого графика в Speedy2D. Мы сделаем линейный график, который может быть использован, например, для визуализации температуры за неделю. Для этого мы добавим в код рисование линий, соединяющих точки на графике, где каждая точка представляет значение температуры в определенный день недели.
Мы модифицируем метод on_draw для рисования линейного графика:
use speedy2d::color::Color;
use speedy2d::shape::Rectangle;
use speedy2d::window::{WindowHandler, WindowHelper, WindowStartupInfo};
use speedy2d::{Graphics2D, Window};
struct MyWindowHandler {
// Данные для графика
data_points: Vec<(f32, f32)>, // Вектор с координатами точек (X, Y)
}
impl WindowHandler for MyWindowHandler {
fn on_draw(&mut self, helper: &mut WindowHelper, graphics: &mut Graphics2D) {
// Очищаем экран, используя черный цвет
graphics.clear_screen(Color::from_rgb(0.0, 0.0, 0.0));
// Рисуем оси графика
let axis_color = Color::from_rgb(1.0, 1.0, 1.0); // Белый цвет для осей
// Ось X
graphics.draw_line((50.0, 550.0), (750.0, 550.0), 2.0, axis_color);
// Ось Y
graphics.draw_line((50.0, 50.0), (50.0, 550.0), 2.0, axis_color);
// Рисуем линейный график по данным
let data_color = Color::from_rgb(0.0, 1.0, 0.0); // Зеленый цвет для данных
for i in 0..self.data_points.len() - 1 {
graphics.draw_line(
self.data_points[i],
self.data_points[i + 1],
2.0,
data_color,
);
}
// Запланируйте следующую перерисовку
helper.request_redraw();
}
}
fn main() {
let window = Window::new_centered("Speedy2D: Simple Data Visualization", (800, 600)).unwrap();
// Создаем вектор с данными для графика (примерные координаты)
let data_points = vec![
(100.0, 500.0), // День 1
(200.0, 300.0), // День 2
(300.0, 450.0), // День 3
(400.0, 350.0), // День 4
(500.0, 400.0), // День 5
(600.0, 250.0), // День 6
(700.0, 300.0), // День 7
];
window.run_loop(MyWindowHandler { data_points });
}
В этом примере мы добавили вектор data_points в структуру MyWindowHandler, который содержит координаты точек на графике. В методе on_draw мы сначала рисуем оси графика, а затем соединяем точки линиями, чтобы получить линейный график.
Давайте создадим простое GUI-приложение с использованием Speedy2D, где пользователь может нажимать на кнопки, изменяя цвет фона окна. Это базовый пример, иллюстрирующий, как можно реализовать взаимодействие с пользователем и обновлять графический интерфейс в ответ на его действия.
use speedy2d::color::Color;
use speedy2d::window::{MouseButton, WindowHandler, WindowHelper, WindowStartupInfo};
use speedy2d::{Graphics2D, Window};
struct MyWindowHandler {
background_color: Color,
}
impl WindowHandler for MyWindowHandler {
fn on_draw(&mut self, helper: &mut WindowHelper, graphics: &mut Graphics2D) {
// Очищаем экран, используя текущий цвет фона
graphics.clear_screen(self.background_color);
// Рисуем кнопки
// Кнопка для смены фона на красный
graphics.draw_rectangle(
(50.0, 50.0),
(150.0, 100.0),
Color::from_rgb(1.0, 0.0, 0.0),
);
// Кнопка для смены фона на зеленый
graphics.draw_rectangle(
(200.0, 50.0),
(300.0, 100.0),
Color::from_rgb(0.0, 1.0, 0.0),
);
// Запланируйте следующую перерисовку
helper.request_redraw();
}
fn on_mouse_button_down(&mut self, helper: &mut WindowHelper, button: MouseButton, x: f32, y: f32) {
if button == MouseButton::Left {
// Проверяем, была ли нажата красная кнопка
if x >= 50.0 && x <= 150.0 && y >= 50.0 && y <= 100.0 {
self.background_color = Color::from_rgb(1.0, 0.0, 0.0);
}
// Проверяем, была ли нажата зеленая кнопка
if x >= 200.0 && x <= 300.0 && y >= 50.0 && y <= 100.0 {
self.background_color = Color::from_rgb(0.0, 1.0, 0.0);
}
}
}
}
fn main() {
let window = Window::new_centered("Speedy2D: Simple GUI Example", (800, 600)).unwrap();
window.run_loop(MyWindowHandler {
background_color: Color::from_rgb(0.0, 0.0, 0.0), // Начальный цвет фона
});
}
Создадим код проекта который объединяет:
- Bevy для сцены.
- Rapier для физики.
- wgpu для сложного рендеринга.
- egui для GUI.
- CUDA и OpenCL для вычислений.
- WebAssembly для WebGL-версии.
Сцена рендерит 3D-объекты, использует SVG-текстуры, поддерживает вычисления на GPU и имеет GUI-кнопку для их запуска.
Главный файл: main.rs
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
use slint::SharedString;
use wgpu::util::DeviceExt;
use resvg::{usvg, tiny_skia};
use std::fs;
fn main() {
App::new()
.insert_resource(WindowDescriptor {
title: "Rust Graphics Engine".to_string(),
width: 1280.,
height: 720.,
..Default::default()
})
.add_plugins(DefaultPlugins)
.add_plugin(RapierPhysicsPlugin::<NoUserData>::default())
.add_startup_system(setup)
.add_system(update)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(0.0, 5.0, 10.0)
.looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
});
let svg_data = fs::read("assets/image.svg").unwrap();
let tree = usvg::Tree::from_data(&svg_data, &usvg::Options::default()).unwrap();
let mut pixmap = tiny_skia::Pixmap::new(512, 512).unwrap();
resvg::render(&tree, tiny_skia::Transform::default(), &mut pixmap.as_mut());
println!("SVG rendered to pixmap!");
commands.spawn((
RigidBody::Dynamic,
Collider::cuboid(0.5, 0.5, 0.5),
));
}
fn update(time: Res<Time>, mut query: Query<&mut Transform>) {
for mut transform in query.iter_mut() {
transform.rotation *= Quat::from_rotation_y(1.5 * time.delta_seconds());
}
}
// --- wgpu-compute ---
fn run_wgpu_compute() {
let instance = wgpu::Instance::new(wgpu::Backends::all());
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: None,
force_fallback_adapter: false,
})).unwrap();
let (device, queue) = pollster::block_on(adapter.request_device(&Default::default(), None)).unwrap();
println!("wgpu-compute initialized!");
}
// --- GUI (slint) ---
slint::slint! {
export component MainApp inherits Window {
Text {
text: "Rust Graphics Engine";
horizontal-alignment: center;
}
Button {
text: "Run wgpu-compute";
clicked => { rust_run_wgpu_compute(); }
}
}
}
fn rust_run_wgpu_compute() {
run_wgpu_compute();
}
fn launch_gui() {
let ui = MainApp::new().unwrap();
ui.run().unwrap();
}
#[cfg(target_os = "windows")]
mod cuda {
extern crate cust;
use cust::prelude::*;
pub fn run_cuda() {
let ctx = cust::context::Context::create_and_push(
cust::context::ContextFlags::MAP_HOST, Device::get_device(0).unwrap()
).unwrap();
println!("CUDA context initialized: {:?}", ctx);
}
}