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 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 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 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}