wisp_storybook/stories/
s_motion_blur.rs1use glam::Vec2;
4use wisp::application::Application;
5use wisp::render::Renderer;
6use wisp::{Color, MotionBlurFilter, RenderTexture, Sprite, Stage, Texture};
7
8use crate::story::Story;
9
10pub fn story() -> Story {
11 Story {
12 id: "filter-motion-blur",
13 category: "Filters",
14 title: "Motion blur",
15 milestone: "M0.18",
16 writeup: include_str!("writeups/motion_blur.md"),
17 build,
18 tick: None,
19 }
20}
21
22fn build(app: &Application, stage: &mut Stage) {
23 let format = wgpu::TextureFormat::Rgba8Unorm;
24 let renderer = Renderer::new(app, format).expect("renderer");
25 let source = dot_texture(app);
26
27 let mut sharp = Sprite::from_texture(source.clone()).with_anchor(Vec2::splat(0.5));
29 sharp.container.transform.position = Vec2::new(-0.5, 0.0);
30 sharp.container.transform.scale = Vec2::splat(0.35);
31 let _ = stage.add_child(stage.root(), sharp);
32
33 let input = RenderTexture::with_format(app, 256, 256, format);
35 let output = RenderTexture::with_format(app, 256, 256, format);
36 let mut sprite_stage = Stage::new();
37 let mut sprite = Sprite::from_texture(source).with_anchor(Vec2::splat(0.5));
38 sprite.container.transform.scale = Vec2::splat(0.95);
39 let _ = sprite_stage.add_child(sprite_stage.root(), sprite);
40 let _ = renderer.render_stage(app, input.view(), Color::BLACK, &sprite_stage);
41
42 let filter = MotionBlurFilter {
46 velocity: Vec2::new(900.0, 600.0),
47 peak_velocity_pps: 1100.0,
48 max_kernel_px: 60.0,
49 };
50 renderer.apply_filter(app, &filter, &input, &output);
51
52 let bytes = output.read_pixels(app);
53 let blurred_texture = Texture::from_rgba(app, 256, 256, &bytes);
54 let mut blurred = Sprite::from_texture(blurred_texture).with_anchor(Vec2::splat(0.5));
55 blurred.container.transform.position = Vec2::new(0.5, 0.0);
56 blurred.container.transform.scale = Vec2::splat(0.35);
57 let _ = stage.add_child(stage.root(), blurred);
58}
59
60fn dot_texture(app: &Application) -> Texture {
61 let size = 96u32;
62 let mut bytes = Vec::with_capacity((size * size * 4) as usize);
63 let center = (
64 i32::try_from(size / 2).unwrap_or(48),
65 i32::try_from(size / 2).unwrap_or(48),
66 );
67 let radius_sq = 28 * 28;
68 for y in 0..size {
69 for x in 0..size {
70 let dx = i32::try_from(x).unwrap_or(0) - center.0;
71 let dy = i32::try_from(y).unwrap_or(0) - center.1;
72 if dx * dx + dy * dy < radius_sq {
73 bytes.extend_from_slice(&[255, 200, 80, 255]);
74 } else {
75 bytes.extend_from_slice(&[20, 20, 30, 255]);
76 }
77 }
78 }
79 Texture::from_rgba(app, size, size, &bytes)
80}