Skip to main content

wisp_storybook/stories/
s_drop_shadow.rs

1//! Story: `DropShadowFilter` — rounded rect with a soft shadow (M0.17).
2
3use glam::Vec2;
4use wisp::application::Application;
5use wisp::math::Rect;
6use wisp::render::Renderer;
7use wisp::{
8    Color, DropShadowFilter, Fill, Graphics, RenderTexture, Sprite, Stage, Stroke, Texture,
9};
10
11use crate::story::Story;
12
13pub fn story() -> Story {
14    Story {
15        id: "filter-drop-shadow",
16        category: "Filters",
17        title: "Drop shadow",
18        milestone: "M0.17",
19        writeup: include_str!("writeups/drop_shadow.md"),
20        build,
21        tick: None,
22    }
23}
24
25fn build(app: &Application, stage: &mut Stage) {
26    let format = wgpu::TextureFormat::Rgba8Unorm;
27    let renderer = Renderer::new(app, format).expect("renderer");
28
29    // Light backdrop sprite — drop shadows are invisible against the
30    // black clear color, so we render a paper-white RT and attach it
31    // as a sprite (Graphics paints AFTER Sprites in render_stage, so
32    // a Graphics backdrop would paint over the shadow). The shadowed
33    // foreground sprite sits on top.
34    let backdrop_rt = RenderTexture::with_format(app, 256, 256, format);
35    let _ = renderer.render_stage(
36        app,
37        backdrop_rt.view(),
38        Color::rgba(0.92, 0.93, 0.95, 1.0),
39        &Stage::new(),
40    );
41    let backdrop_bytes = backdrop_rt.read_pixels(app);
42    let backdrop_tex = Texture::from_rgba(app, 256, 256, &backdrop_bytes);
43    let mut backdrop = Sprite::from_texture(backdrop_tex).with_anchor(Vec2::splat(0.5));
44    backdrop.container.transform.scale = Vec2::splat(2.0); // fills canvas
45    let _ = stage.add_child(stage.root(), backdrop);
46
47    // Build the source scene: a rounded rect on a transparent canvas.
48    let mut source_stage = Stage::new();
49    let mut g = Graphics::new();
50    g.fill(Fill::Solid(Color::rgba_u8(120, 200, 255, 255)));
51    g.stroke(Some(Stroke::new(0.04, Color::rgba_u8(20, 50, 90, 255))));
52    g.draw_rounded_rect(Rect::new(-0.5, -0.4, 1.0, 0.8), 0.18);
53    let _ = source_stage.add_child(source_stage.root(), g);
54
55    let input_rt = RenderTexture::with_format(app, 384, 384, format);
56    let _ = renderer.render_stage(app, input_rt.view(), Color::TRANSPARENT, &source_stage);
57
58    let output_rt = RenderTexture::with_format(app, 384, 384, format);
59    let filter = DropShadowFilter {
60        offset: Vec2::new(8.0, 8.0),
61        blur: 6.0,
62        color: Color::rgba(0.0, 0.0, 0.0, 0.55),
63    };
64    renderer.apply_filter(app, &filter, &input_rt, &output_rt);
65
66    let shadowed_bytes = output_rt.read_pixels(app);
67    let shadowed_texture = Texture::from_rgba(app, 384, 384, &shadowed_bytes);
68
69    let mut sprite = Sprite::from_texture(shadowed_texture).with_anchor(Vec2::splat(0.5));
70    sprite.container.transform.scale = Vec2::splat(0.7);
71    let _ = stage.add_child(stage.root(), sprite);
72}