Skip to main content

matrix/
matrix.rs

1use cuneus::prelude::ComputeShader;
2use cuneus::{
3    Core, ExportManager, RenderKit, ShaderApp, ShaderControls, ShaderManager,
4};
5use cuneus::WindowEvent;
6
7cuneus::uniform_params! {
8    struct ShaderParams {
9        red_power: f32,
10        green_power: f32,
11        blue_power: f32,
12        green_boost: f32,
13        contrast: f32,
14        gamma: f32,
15        glow: f32,
16        _pad_m: f32,
17    }
18}
19
20fn main() -> Result<(), Box<dyn std::error::Error>> {
21    cuneus::gst::init()?;
22    env_logger::init();
23    let (app, event_loop) = ShaderApp::new("matrix", 800, 600);
24    app.run(event_loop, MatrixShader::init)
25}
26
27struct MatrixShader {
28    base: RenderKit,
29    compute_shader: ComputeShader,
30    current_params: ShaderParams,
31}
32impl ShaderManager for MatrixShader {
33    fn init(core: &Core) -> Self {
34        let base = RenderKit::new(core);
35
36        let initial_params = ShaderParams {
37            red_power: 0.98,
38            green_power: 0.85,
39            blue_power: 0.90,
40            green_boost: 1.62,
41            contrast: 1.0,
42            gamma: 1.0,
43            glow: 0.05,
44            _pad_m: 0.0,
45        };
46
47        let config = ComputeShader::builder()
48            .with_entry_point("main")
49            .with_input_texture()
50            .with_custom_uniforms::<ShaderParams>()
51            .build();
52
53        let compute_shader = cuneus::compute_shader!(core, "shaders/matrix.wgsl", config);
54
55        compute_shader.set_custom_params(initial_params, &core.queue);
56
57        Self {
58            base,
59            compute_shader,
60            current_params: initial_params,
61        }
62    }
63
64    fn update(&mut self, core: &Core) {
65        // Update time
66        let current_time = self.base.controls.get_time(&self.base.start_time);
67        let delta = 1.0 / 60.0;
68        self.compute_shader
69            .set_time(current_time, delta, &core.queue);
70
71        // Update input textures for media processing
72        self.base.update_current_texture(core, &core.queue);
73        if let Some(texture_manager) = self.base.get_current_texture_manager() {
74            self.compute_shader.update_input_texture(
75                &texture_manager.view,
76                &texture_manager.sampler,
77                &core.device,
78            );
79        }
80    }
81
82    fn render(&mut self, core: &Core) -> Result<(), cuneus::SurfaceError> {
83        let mut frame = self.base.begin_frame(core)?;
84
85        let mut params = self.current_params;
86        let mut changed = false;
87        let mut should_start_export = false;
88        let mut export_request = self.base.export_manager.get_ui_request();
89        let mut controls_request = self
90            .base
91            .controls
92            .get_ui_request(&self.base.start_time, &core.size, self.base.fps_tracker.fps());
93
94        let using_video_texture = self.base.using_video_texture;
95        let using_hdri_texture = self.base.using_hdri_texture;
96        let using_webcam_texture = self.base.using_webcam_texture;
97        let video_info = self.base.get_video_info();
98        let hdri_info = self.base.get_hdri_info();
99        let webcam_info = self.base.get_webcam_info();
100
101        let full_output = if self.base.key_handler.show_ui {
102            self.base.render_ui(core, |ctx| {
103                RenderKit::apply_default_style(ctx);
104
105                egui::Window::new("Matrix Effect")
106                    .collapsible(true)
107                    .resizable(true)
108                    .default_size([300.0, 100.0])
109                    .show(ctx, |ui| {
110                        ui.collapsing("Media", |ui: &mut egui::Ui| {
111                            ShaderControls::render_media_panel(
112                                ui,
113                                &mut controls_request,
114                                using_video_texture,
115                                video_info,
116                                using_hdri_texture,
117                                hdri_info,
118                                using_webcam_texture,
119                                webcam_info,
120                            );
121                        });
122
123                        ui.separator();
124
125                        ui.collapsing("Matrix Color Settings", |ui| {
126                            changed |= ui
127                                .add(
128                                    egui::Slider::new(&mut params.red_power, 0.5..=3.0)
129                                        .text("Red Power"),
130                                )
131                                .changed();
132
133                            changed |= ui
134                                .add(
135                                    egui::Slider::new(&mut params.green_power, 0.5..=3.0)
136                                        .text("Green Power"),
137                                )
138                                .changed();
139
140                            changed |= ui
141                                .add(
142                                    egui::Slider::new(&mut params.blue_power, 0.5..=3.0)
143                                        .text("Blue Power"),
144                                )
145                                .changed();
146
147                            changed |= ui
148                                .add(
149                                    egui::Slider::new(&mut params.green_boost, 0.5..=2.0)
150                                        .text("Green Boost"),
151                                )
152                                .changed();
153
154                            changed |= ui
155                                .add(
156                                    egui::Slider::new(&mut params.contrast, 0.5..=2.0)
157                                        .text("Contrast"),
158                                )
159                                .changed();
160
161                            changed |= ui
162                                .add(egui::Slider::new(&mut params.gamma, 0.2..=2.0).text("Gamma"))
163                                .changed();
164
165                            changed |= ui
166                                .add(egui::Slider::new(&mut params.glow, -1.0..=1.0).text("Glow"))
167                                .changed();
168                        });
169
170                        ui.separator();
171                        ShaderControls::render_controls_widget(ui, &mut controls_request);
172                        ui.separator();
173                        should_start_export =
174                            ExportManager::render_export_ui_widget(ui, &mut export_request);
175                    });
176            })
177        } else {
178            self.base.render_ui(core, |_ctx| {})
179        };
180
181        self.base.export_manager.apply_ui_request(export_request);
182        self.base.apply_media_requests(core, &controls_request);
183
184        if changed {
185            self.current_params = params;
186            self.compute_shader.set_custom_params(params, &core.queue);
187        }
188
189        if should_start_export {
190            self.base.export_manager.start_export();
191        }
192
193
194        // Run compute shader
195        self.compute_shader.dispatch(&mut frame.encoder, core);
196
197        self.base.renderer.render_to_view(&mut frame.encoder, &frame.view, &self.compute_shader.get_output_texture().bind_group);
198
199        self.base.end_frame(core, frame, full_output);
200        Ok(())
201    }
202
203    fn resize(&mut self, core: &Core) {
204        self.base.default_resize(core, &mut self.compute_shader);
205    }
206
207    fn handle_input(&mut self, core: &Core, event: &WindowEvent) -> bool {
208        self.base.default_handle_input(core, event)
209    }
210}