1use cuneus::compute::*;
2use cuneus::prelude::*;
3
4cuneus::uniform_params! {
5 struct NeuronParams {
6 pixel_offset: f32,
7 pixel_offset2: f32,
8 lights: f32,
9 exp: f32,
10 frame: f32,
11 col1: f32,
12 col2: f32,
13 decay: f32}
14}
15
16struct NeuronShader {
17 base: RenderKit,
18 compute_shader: ComputeShader,
19 current_params: NeuronParams}
20
21impl ShaderManager for NeuronShader {
22 fn init(core: &Core) -> Self {
23 let initial_params = NeuronParams {
24 pixel_offset: -1.0,
25 pixel_offset2: 1.0,
26 lights: 2.2,
27 exp: 4.0,
28 frame: 1.0,
29 col1: 100.0,
30 col2: 1.0,
31 decay: 1.0};
32 let base = RenderKit::new(core);
33
34 let passes = vec![
36 PassDescription::new("geometry", &[]), PassDescription::new("gradient", &["geometry"]), PassDescription::new("trace", &["trace", "gradient"]), PassDescription::new("main_image", &["trace"]),
40 ];
41
42 let config = ComputeShader::builder()
43 .with_entry_point("geometry")
44 .with_multi_pass(&passes)
45 .with_custom_uniforms::<NeuronParams>()
46 .with_workgroup_size([16, 16, 1])
47 .with_texture_format(COMPUTE_TEXTURE_FORMAT_RGBA16)
48 .with_label("2D Neuron Unified")
49 .build();
50
51 let compute_shader = cuneus::compute_shader!(core, "shaders/2dneuron.wgsl", config);
52
53
54 compute_shader.set_custom_params(initial_params, &core.queue);
55
56 Self {
57 base,
58 compute_shader,
59 current_params: initial_params}
60 }
61
62 fn update(&mut self, core: &Core) {
63 self.compute_shader.handle_export(core, &mut self.base);
65
66 let current_time = self.base.controls.get_time(&self.base.start_time);
68 let delta = 1.0 / 60.0;
69 self.compute_shader
70 .set_time(current_time, delta, &core.queue);
71 }
72
73 fn resize(&mut self, core: &Core) {
74 self.base.default_resize(core, &mut self.compute_shader);
75 }
76
77 fn render(&mut self, core: &Core) -> Result<(), cuneus::SurfaceError> {
78 let mut frame = self.base.begin_frame(core)?;
79
80 let mut params = self.current_params;
81 let mut changed = false;
82 let mut should_start_export = false;
83 let mut export_request = self.base.export_manager.get_ui_request();
84 let mut controls_request = self
85 .base
86 .controls
87 .get_ui_request(&self.base.start_time, &core.size, self.base.fps_tracker.fps());
88
89 let full_output = if self.base.key_handler.show_ui {
90 self.base.render_ui(core, |ctx| {
91 RenderKit::apply_default_style(ctx);
92
93 egui::Window::new("2D Neuron")
94 .collapsible(true)
95 .resizable(true)
96 .default_width(280.0)
97 .show(ctx, |ui| {
98 egui::CollapsingHeader::new("Neuron Parameters")
99 .default_open(true)
100 .show(ui, |ui| {
101 changed |= ui
102 .add(
103 egui::Slider::new(&mut params.pixel_offset, -3.14..=3.14)
104 .text("Pixel Offset Y"),
105 )
106 .changed();
107 changed |= ui
108 .add(
109 egui::Slider::new(&mut params.pixel_offset2, -3.14..=3.14)
110 .text("Pixel Offset X"),
111 )
112 .changed();
113 changed |= ui
114 .add(
115 egui::Slider::new(&mut params.lights, 0.0..=12.2)
116 .text("Lights"),
117 )
118 .changed();
119 changed |= ui
120 .add(
121 egui::Slider::new(&mut params.exp, 1.0..=120.0).text("Exp"),
122 )
123 .changed();
124 });
125
126 egui::CollapsingHeader::new("Visual Settings")
127 .default_open(false)
128 .show(ui, |ui| {
129 changed |= ui
130 .add(
131 egui::Slider::new(&mut params.frame, 0.0..=5.2)
132 .text("Frame"),
133 )
134 .changed();
135 changed |= ui
136 .add(
137 egui::Slider::new(&mut params.col1, 0.0..=150.0)
138 .text("Iterations"),
139 )
140 .changed();
141 changed |= ui
142 .add(
143 egui::Slider::new(&mut params.col2, 0.0..=20.0)
144 .text("Color 2"),
145 )
146 .changed();
147 changed |= ui
148 .add(
149 egui::Slider::new(&mut params.decay, 0.0..=1.0)
150 .text("Feedback"),
151 )
152 .changed();
153 });
154
155 ui.separator();
156 ShaderControls::render_controls_widget(ui, &mut controls_request);
157
158 ui.separator();
159 should_start_export =
160 ExportManager::render_export_ui_widget(ui, &mut export_request);
161
162 ui.separator();
163 ui.label(format!("Frame: {}", self.compute_shader.current_frame));
164 ui.label("Multi-buffer neuron with particle tracing");
165 });
166 })
167 } else {
168 self.base.render_ui(core, |_ctx| {})
169 };
170
171 if controls_request.should_clear_buffers {
173 self.compute_shader.current_frame = 0;
175 }
176
177 self.compute_shader.dispatch(&mut frame.encoder, core);
179
180 self.base.renderer.render_to_view(&mut frame.encoder, &frame.view, &self.compute_shader.get_output_texture().bind_group);
181
182 self.base.apply_control_request(controls_request);
183 self.base.export_manager.apply_ui_request(export_request);
184
185 if changed {
186 self.current_params = params;
187 self.compute_shader.set_custom_params(params, &core.queue);
188 }
189
190 if should_start_export {
191 self.base.export_manager.start_export();
192 }
193
194 self.base.end_frame(core, frame, full_output);
195
196 Ok(())
197 }
198
199 fn handle_input(&mut self, core: &Core, event: &WindowEvent) -> bool {
200 self.base.default_handle_input(core, event)
201 }
202}
203
204fn main() -> Result<(), Box<dyn std::error::Error>> {
205 env_logger::init();
206 let (app, event_loop) = ShaderApp::new("2D Neuron", 600, 800);
207 app.run(event_loop, NeuronShader::init)
208}