1use cuneus::compute::{ComputeShader, PassDescription, COMPUTE_TEXTURE_FORMAT_RGBA16};
4use cuneus::{Core, RenderKit, ShaderApp, ShaderControls, ShaderManager};
5use cuneus::{ExportManager, UniformProvider};
6use cuneus::WindowEvent;
7
8#[repr(C)]
9#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
10struct CurrentsParams {
11 sphere_radius: f32,
12 sphere_pos_x: f32,
13 sphere_pos_y: f32,
14 critic2_interval: f32,
15 critic2_pause: f32,
16 critic3_interval: f32,
17 metallic_reflection: f32,
18 line_intensity: f32,
19 pattern_scale: f32,
20 noise_strength: f32,
21 gradient_r: f32,
22 gradient_g: f32,
23 gradient_b: f32,
24 gradient_w: f32,
25 line_color_r: f32,
26 line_color_g: f32,
27 line_color_b: f32,
28 line_color_w: f32,
29 gradient_intensity: f32,
30 line_intensity_final: f32,
31 c2_min: f32,
32 c2_max: f32,
33 c3_min: f32,
34 c3_max: f32,
35 fbm_scale: f32,
36 fbm_offset: f32,
37 gamma: f32,
38}
39
40impl Default for CurrentsParams {
41 fn default() -> Self {
42 Self {
43 sphere_radius: 0.2,
44 sphere_pos_x: 0.0,
45 sphere_pos_y: -0.2,
46 critic2_interval: 10.0,
47 critic2_pause: 5.0,
48 critic3_interval: 10.0,
49 metallic_reflection: 1.8,
50 line_intensity: 0.8,
51 pattern_scale: 150.0,
52 noise_strength: 1.0,
53 gradient_r: 1.0,
54 gradient_g: 2.0,
55 gradient_b: 3.0,
56 gradient_w: 4.0,
57 line_color_r: 1.0,
58 line_color_g: 2.0,
59 line_color_b: 3.0,
60 line_color_w: 4.0,
61 gradient_intensity: 1.5,
62 line_intensity_final: 1.5,
63 c2_min: 333.0,
64 c2_max: 1.0,
65 c3_min: 1.0,
66 c3_max: 3.0,
67 fbm_scale: 4.0,
68 fbm_offset: 1.0,
69 gamma: 2.1,
70 }
71 }
72}
73
74impl UniformProvider for CurrentsParams {
75 fn as_bytes(&self) -> &[u8] {
76 bytemuck::bytes_of(self)
77 }
78}
79
80struct CurrentsShader {
81 base: RenderKit,
82 compute_shader: ComputeShader,
83 current_params: CurrentsParams,
84}
85
86impl CurrentsShader {
87 fn clear_buffers(&mut self, core: &Core) {
88 self.compute_shader.clear_all_buffers(core);
89 }
90}
91
92impl ShaderManager for CurrentsShader {
93 fn init(core: &Core) -> Self {
94 let base = RenderKit::new(core);
95
96 let passes = vec![
98 PassDescription::new("pattern", &["pattern"]), PassDescription::new("trace_fine", &["trace_fine", "pattern"]), PassDescription::new("trace_coarse", &["trace_coarse", "pattern"]), PassDescription::new("accumulate", &["accumulate", "trace_coarse", "trace_fine"]), PassDescription::new("main_image", &["accumulate"]), ];
104
105 let config = ComputeShader::builder()
106 .with_entry_point("pattern")
107 .with_multi_pass(&passes)
108 .with_custom_uniforms::<CurrentsParams>()
109 .with_workgroup_size([16, 16, 1])
110 .with_texture_format(COMPUTE_TEXTURE_FORMAT_RGBA16)
111 .with_label("Currents Multi-Pass")
112 .build();
113
114 let compute_shader = cuneus::compute_shader!(core, "shaders/currents.wgsl", config);
115
116 let initial_params = CurrentsParams::default();
117 let shader = Self {
118 base,
119 compute_shader,
120 current_params: initial_params,
121 };
122
123 shader
125 .compute_shader
126 .set_custom_params(initial_params, &core.queue);
127
128 shader
129 }
130
131 fn update(&mut self, core: &Core) {
132 let current_time = self.base.controls.get_time(&self.base.start_time);
134 let delta = 1.0 / 60.0;
135 self.compute_shader
136 .set_time(current_time, delta, &core.queue);
137 self.compute_shader.handle_export(core, &mut self.base);
139 }
140
141 fn resize(&mut self, core: &Core) {
142 self.base.default_resize(core, &mut self.compute_shader);
143 }
144
145 fn render(&mut self, core: &Core) -> Result<(), cuneus::SurfaceError> {
146 let mut frame = self.base.begin_frame(core)?;
147
148 let mut controls_request = self
149 .base
150 .controls
151 .get_ui_request(&self.base.start_time, &core.size, self.base.fps_tracker.fps());
152
153 let mut params = self.current_params;
155 let mut changed = false;
156 let mut should_start_export = false;
157 let mut export_request = self.base.export_manager.get_ui_request();
158
159 let full_output = if self.base.key_handler.show_ui {
160 self.base.render_ui(core, |ctx| {
161 RenderKit::apply_default_style(ctx);
162
163 egui::Window::new("Multi-Buffer Ping-Pong Example")
164 .collapsible(true)
165 .resizable(true)
166 .default_width(280.0)
167 .show(ctx, |ui| {
168 egui::CollapsingHeader::new("Sphere Settings")
170 .default_open(false)
171 .show(ui, |ui| {
172 changed |= ui
173 .add(
174 egui::Slider::new(&mut params.sphere_radius, 0.05..=0.5)
175 .text("Sphere Radius"),
176 )
177 .changed();
178 changed |= ui
179 .add(
180 egui::Slider::new(&mut params.sphere_pos_x, -1.0..=1.0)
181 .text("Sphere X"),
182 )
183 .changed();
184 changed |= ui
185 .add(
186 egui::Slider::new(&mut params.sphere_pos_y, -1.0..=1.0)
187 .text("Sphere Y"),
188 )
189 .changed();
190 changed |= ui
191 .add(
192 egui::Slider::new(
193 &mut params.metallic_reflection,
194 0.5..=3.0,
195 )
196 .text("Metallic Reflection"),
197 )
198 .changed();
199 });
200
201 egui::CollapsingHeader::new("Pattern Control")
202 .default_open(false)
203 .show(ui, |ui| {
204 changed |= ui
205 .add(
206 egui::Slider::new(&mut params.pattern_scale, 50.0..=300.0)
207 .text("Pattern Scale"),
208 )
209 .changed();
210 changed |= ui
211 .add(
212 egui::Slider::new(&mut params.critic2_interval, 5.0..=20.0)
213 .text("Flow Interval"),
214 )
215 .changed();
216 changed |= ui
217 .add(
218 egui::Slider::new(&mut params.critic2_pause, 1.0..=10.0)
219 .text("Flow Pause"),
220 )
221 .changed();
222 changed |= ui
223 .add(
224 egui::Slider::new(&mut params.critic3_interval, 5.0..=20.0)
225 .text("Scale Interval"),
226 )
227 .changed();
228 changed |= ui
229 .add(
230 egui::Slider::new(&mut params.noise_strength, 0.5..=5.0)
231 .text("Noise Strength"),
232 )
233 .changed();
234 });
235
236 egui::CollapsingHeader::new("Noise")
237 .default_open(false)
238 .show(ui, |ui| {
239 ui.label("Oscillator 2 (c2):");
240 changed |= ui
241 .add(
242 egui::Slider::new(&mut params.c2_min, 1.0..=500.0)
243 .text("C2 Min"),
244 )
245 .changed();
246 changed |= ui
247 .add(
248 egui::Slider::new(&mut params.c2_max, 0.1..=10.0)
249 .text("C2 Max"),
250 )
251 .changed();
252
253 ui.separator();
254 ui.label("Oscillator 3 (c3):");
255 changed |= ui
256 .add(
257 egui::Slider::new(&mut params.c3_min, 0.1..=10.0)
258 .text("C3 Min"),
259 )
260 .changed();
261 changed |= ui
262 .add(
263 egui::Slider::new(&mut params.c3_max, 0.5..=10.0)
264 .text("C3 Max"),
265 )
266 .changed();
267
268 ui.separator();
269 ui.label("FBM Noise:");
270 changed |= ui
271 .add(
272 egui::Slider::new(&mut params.fbm_scale, 1.0..=10.0)
273 .text("FBM Scale"),
274 )
275 .changed();
276 changed |= ui
277 .add(
278 egui::Slider::new(&mut params.fbm_offset, 0.1..=5.0)
279 .text("FBM Offset"),
280 )
281 .changed();
282 });
283
284 egui::CollapsingHeader::new("Colors & Post-Processing")
285 .default_open(false)
286 .show(ui, |ui| {
287 ui.horizontal(|ui| {
288 ui.label("Gradient:");
289 let mut color =
290 [params.gradient_r, params.gradient_g, params.gradient_b];
291 if ui.color_edit_button_rgb(&mut color).changed() {
292 params.gradient_r = color[0];
293 params.gradient_g = color[1];
294 params.gradient_b = color[2];
295 changed = true;
296 }
297 });
298
299 ui.horizontal(|ui| {
300 ui.label("Lines:");
301 let mut color = [
302 params.line_color_r,
303 params.line_color_g,
304 params.line_color_b,
305 ];
306 if ui.color_edit_button_rgb(&mut color).changed() {
307 params.line_color_r = color[0];
308 params.line_color_g = color[1];
309 params.line_color_b = color[2];
310 changed = true;
311 }
312 });
313
314 ui.separator();
315 changed |= ui
316 .add(
317 egui::Slider::new(
318 &mut params.gradient_intensity,
319 0.1..=2.0,
320 )
321 .text("Gradient Intensity"),
322 )
323 .changed();
324 changed |= ui
325 .add(
326 egui::Slider::new(
327 &mut params.line_intensity_final,
328 0.1..=2.0,
329 )
330 .text("Line Final Intensity"),
331 )
332 .changed();
333
334 ui.separator();
335 changed |= ui
336 .add(
337 egui::Slider::new(&mut params.line_intensity, 0.1..=3.0)
338 .text("Line Intensity"),
339 )
340 .changed();
341 changed |= ui
342 .add(
343 egui::Slider::new(&mut params.gamma, 0.1..=4.0)
344 .text("Gamma Correction"),
345 )
346 .changed();
347 });
348
349 ui.separator();
350
351 ShaderControls::render_controls_widget(ui, &mut controls_request);
352
353 ui.separator();
354
355 should_start_export =
356 ExportManager::render_export_ui_widget(ui, &mut export_request);
357
358 ui.separator();
359 ui.label(format!("Frame: {}", self.compute_shader.current_frame));
360 });
361 })
362 } else {
363 self.base.render_ui(core, |_ctx| {})
364 };
365
366 self.base.export_manager.apply_ui_request(export_request);
367 if controls_request.should_clear_buffers {
368 self.clear_buffers(core);
369 }
370 self.base.apply_control_request(controls_request);
371
372 if changed {
373 self.current_params = params;
374 self.compute_shader.set_custom_params(params, &core.queue);
375 self.compute_shader.current_frame = 0;
377 }
378
379 if should_start_export {
380 self.base.export_manager.start_export();
381 }
382
383 self.compute_shader.dispatch(&mut frame.encoder, core);
386
387 self.base.renderer.render_to_view(&mut frame.encoder, &frame.view, &self.compute_shader.get_output_texture().bind_group);
388
389 self.base.end_frame(core, frame, full_output);
390
391 Ok(())
392 }
393
394 fn handle_input(&mut self, core: &Core, event: &WindowEvent) -> bool {
395 self.base.default_handle_input(core, event)
396 }
397}
398
399fn main() -> Result<(), Box<dyn std::error::Error>> {
400 env_logger::init();
401 let (app, event_loop) = ShaderApp::new("Photon Tracing", 800, 600);
402
403 app.run(event_loop, CurrentsShader::init)
404}