Skip to main content

sdvert/
sdvert.rs

1use cuneus::prelude::ComputeShader;
2use cuneus::{Core, RenderKit, ShaderApp, ShaderManager};
3use cuneus::WindowEvent;
4cuneus::uniform_params! {
5    struct ShaderParams {
6    lambda: f32,
7    theta: f32,
8    alpha: f32,
9    sigma: f32,
10    gamma: f32,
11    blue: f32,
12    a: f32,
13    b: f32,
14    base_color_r: f32,
15    base_color_g: f32,
16    base_color_b: f32,
17    accent_color_r: f32,
18    accent_color_g: f32,
19    accent_color_b: f32,
20    background_r: f32,
21    background_g: f32,
22    background_b: f32,
23    gamma_correction: f32,
24    aces_tonemapping: f32,
25    _padding: f32}
26}
27
28struct Shader {
29    base: RenderKit,
30    compute_shader: ComputeShader,
31    current_params: ShaderParams}
32fn main() -> Result<(), Box<dyn std::error::Error>> {
33    env_logger::init();
34    let (app, event_loop) = ShaderApp::new("sdvert", 800, 600);
35    app.run(event_loop, Shader::init)
36}
37impl ShaderManager for Shader {
38    fn init(core: &Core) -> Self {
39        // Create texture display layout
40        let base = RenderKit::new(core);
41
42        let initial_params = ShaderParams {
43            sigma: 0.07,
44            gamma: 1.5,
45            blue: 1.0,
46            a: 2.0,
47            b: 0.5,
48            lambda: 3.0,
49            theta: 2.0,
50            alpha: 0.3,
51            base_color_r: 1.0,
52            base_color_g: 1.0,
53            base_color_b: 1.0,
54            accent_color_r: 1.0,
55            accent_color_g: 1.0,
56            accent_color_b: 1.0,
57            background_r: 0.6,
58            background_g: 0.9,
59            background_b: 0.9,
60            gamma_correction: 0.41,
61            aces_tonemapping: 0.4,
62            _padding: 0.0};
63
64        let config = ComputeShader::builder()
65            .with_entry_point("main")
66            .with_custom_uniforms::<ShaderParams>()
67            .build();
68
69        let compute_shader = cuneus::compute_shader!(core, "shaders/sdvert.wgsl", config);
70
71        compute_shader.set_custom_params(initial_params, &core.queue);
72
73        Self {
74            base,
75            compute_shader,
76            current_params: initial_params}
77    }
78
79    fn update(&mut self, core: &Core) {
80        // Update time
81        let current_time = self.base.controls.get_time(&self.base.start_time);
82        let delta = 1.0 / 60.0;
83        self.compute_shader
84            .set_time(current_time, delta, &core.queue);
85        // Handle export
86        self.compute_shader.handle_export(core, &mut self.base);
87    }
88
89    fn render(&mut self, core: &Core) -> Result<(), cuneus::SurfaceError> {
90        let mut frame = self.base.begin_frame(core)?;
91
92        let mut params = self.current_params;
93        let mut changed = false;
94        let mut should_start_export = false;
95        let mut export_request = self.base.export_manager.get_ui_request();
96        let mut controls_request = self
97            .base
98            .controls
99            .get_ui_request(&self.base.start_time, &core.size, self.base.fps_tracker.fps());
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("SDVert Controls")
106                    .collapsible(true)
107                    .resizable(true)
108                    .default_width(300.0)
109                    .show(ctx, |ui| {
110                        egui::CollapsingHeader::new("Geometry")
111                            .default_open(false)
112                            .show(ui, |ui| {
113                                changed |= ui
114                                    .add(
115                                        egui::Slider::new(&mut params.lambda, 1.0..=20.0)
116                                            .text("Vertices"),
117                                    )
118                                    .changed();
119                                changed |= ui
120                                    .add(
121                                        egui::Slider::new(&mut params.theta, 0.0..=10.0)
122                                            .text("Angle Scale"),
123                                    )
124                                    .changed();
125                                changed |= ui
126                                    .add(
127                                        egui::Slider::new(&mut params.gamma, 0.1..=3.0)
128                                            .text("Layer Size"),
129                                    )
130                                    .changed();
131                                changed |= ui
132                                    .add(
133                                        egui::Slider::new(&mut params.alpha, 0.001..=0.5)
134                                            .text("Layer Min"),
135                                    )
136                                    .changed();
137                                changed |= ui
138                                    .add(
139                                        egui::Slider::new(&mut params.sigma, 0.01..=0.5)
140                                            .text("Layer Max"),
141                                    )
142                                    .changed();
143                            });
144
145                        egui::CollapsingHeader::new("Shape Parameters")
146                            .default_open(false)
147                            .show(ui, |ui| {
148                                changed |= ui
149                                    .add(
150                                        egui::Slider::new(&mut params.a, 0.0..=5.0)
151                                            .text("Depth Factor"),
152                                    )
153                                    .changed();
154                                changed |= ui
155                                    .add(
156                                        egui::Slider::new(&mut params.b, 0.0..=5.0)
157                                            .text("Fold Pattern"),
158                                    )
159                                    .changed();
160                                changed |= ui
161                                    .add(
162                                        egui::Slider::new(&mut params.blue, 0.0..=5.0)
163                                            .text("Hue Shift"),
164                                    )
165                                    .changed();
166                            });
167
168                        egui::CollapsingHeader::new("Colors")
169                            .default_open(false)
170                            .show(ui, |ui| {
171                                ui.horizontal(|ui| {
172                                    ui.label("Base:");
173                                    let mut base_color = [
174                                        params.base_color_r,
175                                        params.base_color_g,
176                                        params.base_color_b,
177                                    ];
178                                    if ui.color_edit_button_rgb(&mut base_color).changed() {
179                                        params.base_color_r = base_color[0];
180                                        params.base_color_g = base_color[1];
181                                        params.base_color_b = base_color[2];
182                                        changed = true;
183                                    }
184                                });
185                                ui.horizontal(|ui| {
186                                    ui.label("Accent:");
187                                    let mut accent_color = [
188                                        params.accent_color_r,
189                                        params.accent_color_g,
190                                        params.accent_color_b,
191                                    ];
192                                    if ui.color_edit_button_rgb(&mut accent_color).changed() {
193                                        params.accent_color_r = accent_color[0];
194                                        params.accent_color_g = accent_color[1];
195                                        params.accent_color_b = accent_color[2];
196                                        changed = true;
197                                    }
198                                });
199                                ui.horizontal(|ui| {
200                                    ui.label("Background:");
201                                    let mut bg_color = [
202                                        params.background_r,
203                                        params.background_g,
204                                        params.background_b,
205                                    ];
206                                    if ui.color_edit_button_rgb(&mut bg_color).changed() {
207                                        params.background_r = bg_color[0];
208                                        params.background_g = bg_color[1];
209                                        params.background_b = bg_color[2];
210                                        changed = true;
211                                    }
212                                });
213                            });
214
215                        egui::CollapsingHeader::new("Post-Processing")
216                            .default_open(false)
217                            .show(ui, |ui| {
218                                changed |= ui
219                                    .add(
220                                        egui::Slider::new(&mut params.gamma_correction, 0.1..=3.0)
221                                            .text("Gamma Correction"),
222                                    )
223                                    .changed();
224                                changed |= ui
225                                    .add(
226                                        egui::Slider::new(&mut params.aces_tonemapping, 0.0..=2.0)
227                                            .text("ACES Tonemapping"),
228                                    )
229                                    .changed();
230                            });
231
232                        ui.separator();
233                        cuneus::ShaderControls::render_controls_widget(ui, &mut controls_request);
234
235                        ui.separator();
236                        should_start_export =
237                            cuneus::ExportManager::render_export_ui_widget(ui, &mut export_request);
238                    });
239            })
240        } else {
241            self.base.render_ui(core, |_ctx| {})
242        };
243
244        self.base.export_manager.apply_ui_request(export_request);
245        self.base.apply_control_request(controls_request);
246
247        if changed {
248            self.current_params = params;
249            self.compute_shader.set_custom_params(params, &core.queue);
250        }
251
252        if should_start_export {
253            self.base.export_manager.start_export();
254        }
255
256        // Create command encoder
257
258        // Update time uniform
259        let current_time = self.base.controls.get_time(&self.base.start_time);
260        let delta_time = 1.0 / 60.0;
261        self.compute_shader
262            .set_time(current_time, delta_time, &core.queue);
263
264        // Dispatch compute shader
265        self.compute_shader.dispatch(&mut frame.encoder, core);
266
267        self.base.renderer.render_to_view(&mut frame.encoder, &frame.view, &self.compute_shader.get_output_texture().bind_group);
268
269        self.base.end_frame(core, frame, full_output);
270
271        Ok(())
272    }
273    fn resize(&mut self, core: &Core) {
274        self.base.default_resize(core, &mut self.compute_shader);
275    }
276    fn handle_input(&mut self, core: &Core, event: &WindowEvent) -> bool {
277        self.base.default_handle_input(core, event)
278    }
279}