shader/
shader.rs

1use sfml::{
2    cpp::FBox,
3    graphics::{
4        Color, Drawable, Font, IntRect, PrimitiveType, RenderStates, RenderTarget, RenderTexture,
5        RenderWindow, Shader, ShaderType, Sprite, Text, Texture, Transformable, Vertex,
6    },
7    system::{Clock, Vector2f},
8    window::{Event, Key, Style},
9    SfResult,
10};
11
12include!("../example_common.rs");
13
14trait Effect: Drawable {
15    fn update(&mut self, t: f32, x: f32, y: f32) -> SfResult<()>;
16    fn name(&self) -> &str;
17    fn as_drawable(&self) -> &dyn Drawable;
18}
19
20struct Pixelate<'t> {
21    sprite: Sprite<'t>,
22    shader: FBox<Shader<'static>>,
23}
24
25impl<'t> Pixelate<'t> {
26    fn new(texture: &'t Texture) -> SfResult<Self> {
27        let mut sprite = Sprite::new();
28        sprite.set_texture(texture, false);
29        Ok(Self {
30            sprite,
31            shader: Shader::from_file("pixelate.frag", ShaderType::Fragment)?,
32        })
33    }
34}
35
36impl Drawable for Pixelate<'_> {
37    fn draw<'a: 'shader, 'texture, 'shader, 'shader_texture>(
38        &'a self,
39        target: &mut dyn RenderTarget,
40        states: &RenderStates<'texture, 'shader, 'shader_texture>,
41    ) {
42        let mut states = *states;
43        states.shader = Some(&self.shader);
44        target.draw_with_renderstates(&self.sprite, &states);
45    }
46}
47
48impl Effect for Pixelate<'_> {
49    fn update(&mut self, _t: f32, x: f32, y: f32) -> SfResult<()> {
50        self.shader
51            .set_uniform_float("pixel_threshold", (x + y) / 30.0)
52    }
53    fn name(&self) -> &str {
54        "pixelate"
55    }
56    fn as_drawable(&self) -> &dyn Drawable {
57        self
58    }
59}
60
61struct WaveBlur<'fo> {
62    text: Text<'fo>,
63    shader: FBox<Shader<'static>>,
64}
65
66const WAVEBLUR_TEXT: &str = "\
67Praesent suscipit augue in velit pulvinar hendrerit varius purus aliquam.
68Mauris mi odio, bibendum quis fringilla a, laoreet vel orci. Proin vitae vulputate tortor.
69Praesent cursus ultrices justo, ut feugiat ante vehicula quis.
70Donec fringilla scelerisque mauris et viverra.
71Maecenas adipiscing ornare scelerisque. Nullam at libero elit.
72Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
73Nullam leo urna, tincidunt id semper eget, ultricies sed mi.
74Morbi mauris massa, commodo id dignissim vel, lobortis et elit.
75Fusce vel libero sed neque scelerisque venenatis.
76Integer mattis tincidunt quam vitae iaculis.
77Vivamus fringilla sem non velit venenatis fermentum.
78Vivamus varius tincidunt nisi id vehicula.
79Integer ullamcorper, enim vitae euismod rutrum, massa nisl semper ipsum,
80vestibulum sodales sem ante in massa.
81Vestibulum in augue non felis convallis viverra.
82Mauris ultricies dolor sed massa convallis sed aliquet augue fringilla.
83Duis erat eros, porta in accumsan in, blandit quis sem.
84In hac habitasse platea dictumst. Etiam fringilla est id odio dapibus sit amet semper dui laoreet.";
85
86impl<'fo> WaveBlur<'fo> {
87    fn new(font: &'fo Font) -> SfResult<Self> {
88        let mut text = Text::new(WAVEBLUR_TEXT, font, 22);
89        text.set_position((30., 20.));
90        Ok(Self {
91            text,
92            shader: Shader::from_file_vert_frag("wave.vert", "blur.frag")?,
93        })
94    }
95}
96
97impl Drawable for WaveBlur<'_> {
98    fn draw<'a: 'shader, 'texture, 'shader, 'shader_texture>(
99        &'a self,
100        target: &mut dyn RenderTarget,
101        states: &RenderStates<'texture, 'shader, 'shader_texture>,
102    ) {
103        let mut states = *states;
104        states.shader = Some(&self.shader);
105        target.draw_with_renderstates(&self.text, &states);
106    }
107}
108
109impl Effect for WaveBlur<'_> {
110    fn update(&mut self, t: f32, x: f32, y: f32) -> SfResult<()> {
111        self.shader.set_uniform_float("wave_phase", t)?;
112        self.shader
113            .set_uniform_vec2("wave_amplitude", Vector2f::new(x * 40., y * 40.))?;
114        self.shader
115            .set_uniform_float("blur_radius", (x + y) * 0.008)
116    }
117    fn name(&self) -> &str {
118        "wave + blur"
119    }
120    fn as_drawable(&self) -> &dyn Drawable {
121        self
122    }
123}
124
125struct StormBlink {
126    points: Vec<Vertex>,
127    shader: FBox<Shader<'static>>,
128}
129
130impl StormBlink {
131    fn new() -> SfResult<Self> {
132        use rand::{thread_rng, Rng};
133        let mut rng = thread_rng();
134
135        let mut points = Vec::new();
136        for _ in 0..40_000 {
137            let x = rng.gen_range(0.0..800.);
138            let y = rng.gen_range(0.0..600.);
139            let (red, green, blue) = (rng.r#gen(), rng.r#gen(), rng.r#gen());
140            points.push(Vertex::with_pos_color(
141                Vector2f::new(x, y),
142                Color::rgb(red, green, blue),
143            ));
144        }
145
146        let shader = Shader::from_file_vert_frag("storm.vert", "blink.frag")?;
147        Ok(Self { points, shader })
148    }
149}
150
151impl Drawable for StormBlink {
152    fn draw<'a: 'shader, 'texture, 'shader, 'shader_texture>(
153        &'a self,
154        target: &mut dyn RenderTarget,
155        states: &RenderStates<'texture, 'shader, 'shader_texture>,
156    ) {
157        let mut states = *states;
158        states.shader = Some(&self.shader);
159        target.draw_primitives(&self.points, PrimitiveType::POINTS, &states);
160    }
161}
162
163impl Effect for StormBlink {
164    fn update(&mut self, t: f32, x: f32, y: f32) -> SfResult<()> {
165        let radius = 200. + t.cos() * 150.;
166        self.shader
167            .set_uniform_vec2("storm_position", Vector2f::new(x * 800., y * 600.))?;
168        self.shader
169            .set_uniform_float("storm_inner_radius", radius / 3.)?;
170        self.shader
171            .set_uniform_float("storm_total_radius", radius)?;
172        self.shader
173            .set_uniform_float("blink_alpha", 0.5 + (t * 3.).cos() * 0.25)
174    }
175    fn name(&self) -> &str {
176        "storm + blink"
177    }
178    fn as_drawable(&self) -> &dyn Drawable {
179        self
180    }
181}
182
183struct Edge<'t> {
184    surface: FBox<RenderTexture>,
185    bg_sprite: Sprite<'t>,
186    entities: Vec<Sprite<'t>>,
187    shader: FBox<Shader<'static>>,
188}
189
190impl<'t> Edge<'t> {
191    fn new(bg_texture: &'t Texture, entity_texture: &'t Texture) -> SfResult<Self> {
192        let mut surface = RenderTexture::new(800, 600)?;
193        surface.set_smooth(true);
194        let mut bg_sprite = Sprite::with_texture(bg_texture);
195        bg_sprite.set_position((135., 100.));
196        let mut entities = Vec::new();
197
198        for i in 0..6 {
199            entities.push(Sprite::with_texture_and_rect(
200                entity_texture,
201                IntRect::new(96 * i, 0, 96, 96),
202            ));
203        }
204
205        let mut shader = Shader::from_file("edge.frag", ShaderType::Fragment)?;
206        shader.set_uniform_current_texture("texture")?;
207
208        Ok(Self {
209            surface,
210            bg_sprite,
211            entities,
212            shader,
213        })
214    }
215}
216
217impl Drawable for Edge<'_> {
218    fn draw<'a: 'shader, 'texture, 'shader, 'shader_texture>(
219        &'a self,
220        target: &mut dyn RenderTarget,
221        states: &RenderStates<'texture, 'shader, 'shader_texture>,
222    ) {
223        let mut states = *states;
224        states.shader = Some(&self.shader);
225        target.draw_with_renderstates(&Sprite::with_texture(self.surface.texture()), &states);
226    }
227}
228
229impl Effect for Edge<'_> {
230    fn update(&mut self, t: f32, x: f32, y: f32) -> SfResult<()> {
231        self.shader
232            .set_uniform_float("edge_threshold", 1. - (x + y) / 2.)?;
233        let entities_len = self.entities.len() as f32;
234
235        for (i, en) in self.entities.iter_mut().enumerate() {
236            let pos = (
237                (0.25 * (t * i as f32 + (entities_len - i as f32))).cos() * 300. + 350.,
238                (0.25 * (t * (entities_len - i as f32) + i as f32)).cos() * 200. + 250.,
239            );
240            en.set_position(pos);
241        }
242        self.surface.clear(Color::WHITE);
243        self.surface.draw(&self.bg_sprite);
244        for en in &self.entities {
245            self.surface.draw(en);
246        }
247        self.surface.display();
248        Ok(())
249    }
250    fn as_drawable(&self) -> &dyn Drawable {
251        self
252    }
253    fn name(&self) -> &str {
254        "edge post-effect"
255    }
256}
257
258fn main() -> SfResult<()> {
259    example_ensure_right_working_dir();
260
261    let mut window = RenderWindow::new(
262        (800, 600),
263        "SFML Shader",
264        Style::TITLEBAR | Style::CLOSE,
265        &Default::default(),
266    )?;
267    window.set_vertical_sync_enabled(true);
268    let font = Font::from_file("sansation.ttf")?;
269    let bg = Texture::from_file("background.jpg")?;
270    let mut bg_texture = Texture::from_file("sfml.png")?;
271    bg_texture.set_smooth(true);
272    let mut entity_texture = Texture::from_file("devices.png")?;
273    entity_texture.set_smooth(true);
274    let effects: [&mut dyn Effect; 4] = [
275        &mut Pixelate::new(&bg)?,
276        &mut WaveBlur::new(&font)?,
277        &mut StormBlink::new()?,
278        &mut Edge::new(&bg_texture, &entity_texture)?,
279    ];
280    let mut current = 0;
281    let text_bg_texture = Texture::from_file("text-background.png")?;
282    let mut text_bg = Sprite::with_texture(&text_bg_texture);
283    text_bg.set_position((0., 520.));
284    text_bg.set_color(Color::rgba(255, 255, 255, 200));
285    let msg = format!("Current effect: {}", effects[current].name());
286    let mut desc = Text::new(&msg, &font, 20);
287    desc.set_position((10., 530.));
288    desc.set_fill_color(Color::rgb(80, 80, 80));
289    let msg = "Press left and right arrows to change the current shader";
290    let mut instructions = Text::new(msg, &font, 20);
291    instructions.set_position((280., 555.));
292    instructions.set_fill_color(Color::rgb(80, 80, 80));
293    let clock = Clock::start()?;
294
295    while window.is_open() {
296        while let Some(event) = window.poll_event() {
297            use crate::Event::*;
298            match event {
299                Closed => window.close(),
300                KeyPressed { code, .. } => match code {
301                    Key::Escape => window.close(),
302                    Key::Left => {
303                        if current == 0 {
304                            current = effects.len() - 1;
305                        } else {
306                            current -= 1;
307                        }
308                        desc.set_string(&format!("Current effect: {}", effects[current].name()));
309                    }
310                    Key::Right => {
311                        if current == effects.len() - 1 {
312                            current = 0;
313                        } else {
314                            current += 1;
315                        }
316                        desc.set_string(&format!("Current effect: {}", effects[current].name()));
317                    }
318                    _ => {}
319                },
320                _ => {}
321            }
322        }
323
324        let x = window.mouse_position().x as f32 / window.size().x as f32;
325        let y = window.mouse_position().y as f32 / window.size().y as f32;
326
327        effects[current].update(clock.elapsed_time().as_seconds(), x, y)?;
328
329        window.clear(Color::rgb(255, 128, 0));
330        window.draw(effects[current].as_drawable());
331        window.draw(&text_bg);
332        window.draw(&instructions);
333        window.draw(&desc);
334        window.display();
335    }
336    Ok(())
337}