1use cuneus::compute::*;
2use cuneus::prelude::*;
3
4cuneus::uniform_params! {
5 struct SinhParams {
6 aa: i32,
7 camera_x: f32,
8 camera_y: f32,
9 camera_z: f32,
10 orbit_speed: f32,
11 magic_number: f32,
12 cv_min: f32,
13 cv_max: f32,
14 os_base: f32,
15 os_scale: f32,
16 base_color_r: f32,
17 base_color_g: f32,
18 base_color_b: f32,
19 light_color_r: f32,
20 light_color_g: f32,
21 light_color_b: f32,
22 ambient_r: f32,
23 ambient_g: f32,
24 ambient_b: f32,
25 gamma: f32,
26 iterations: i32,
27 bound: f32,
28 fractal_scale: f32,
29 vignette_offset: f32}
30}
31
32struct SinhShader {
33 base: RenderKit,
34 compute_shader: ComputeShader,
35 current_params: SinhParams}
36
37impl SinhShader {
38 fn clear_buffers(&mut self, core: &Core) {
39 self.compute_shader.clear_all_buffers(core);
40 }
41}
42
43impl ShaderManager for SinhShader {
44 fn init(core: &Core) -> Self {
45 let base = RenderKit::new(core);
47
48 let initial_params = SinhParams {
49 aa: 2,
50 camera_x: 0.1,
51 camera_y: 10.0,
52 camera_z: 10.0,
53 orbit_speed: 0.3,
54 magic_number: 36.0,
55 cv_min: 2.197,
56 cv_max: 2.99225,
57 os_base: 0.00004,
58 os_scale: 0.02040101,
59 base_color_r: 0.5,
60 base_color_g: 0.25,
61 base_color_b: 0.05,
62 light_color_r: 0.8,
63 light_color_g: 1.0,
64 light_color_b: 0.3,
65 ambient_r: 1.2,
66 ambient_g: 1.0,
67 ambient_b: 0.8,
68 gamma: 0.4,
69 iterations: 65,
70 bound: 12.25,
71 fractal_scale: 0.05,
72 vignette_offset: 0.0};
73
74 let config = ComputeShader::builder()
75 .with_entry_point("main")
76 .with_custom_uniforms::<SinhParams>()
77 .with_workgroup_size([16, 16, 1])
78 .with_texture_format(COMPUTE_TEXTURE_FORMAT_RGBA16)
79 .with_label("Sinh Unified")
80 .build();
81
82 let compute_shader = cuneus::compute_shader!(core, "shaders/sinh.wgsl", config);
83
84
85 compute_shader.set_custom_params(initial_params, &core.queue);
86
87 Self {
88 base,
89 compute_shader,
90 current_params: initial_params}
91 }
92
93 fn update(&mut self, core: &Core) {
94 self.compute_shader.handle_export(core, &mut self.base);
96 }
97 fn resize(&mut self, core: &Core) {
98 self.base.default_resize(core, &mut self.compute_shader);
99 }
100 fn render(&mut self, core: &Core) -> Result<(), cuneus::SurfaceError> {
101 let mut frame = self.base.begin_frame(core)?;
102
103 let mut params = self.current_params;
104 let mut changed = false;
105 let mut should_start_export = false;
106 let mut export_request = self.base.export_manager.get_ui_request();
107 let mut controls_request = self
108 .base
109 .controls
110 .get_ui_request(&self.base.start_time, &core.size, self.base.fps_tracker.fps());
111 let full_output = if self.base.key_handler.show_ui {
112 self.base.render_ui(core, |ctx| {
113 RenderKit::apply_default_style(ctx);
114
115 egui::Window::new("Sinh")
116 .collapsible(true)
117 .resizable(true)
118 .default_width(280.0)
119 .show(ctx, |ui| {
120 egui::CollapsingHeader::new("Rendering")
121 .default_open(true)
122 .show(ui, |ui| {
123 changed |= ui
124 .add(egui::Slider::new(&mut params.aa, 1..=4).text("AA"))
125 .changed();
126 changed |= ui
127 .add(
128 egui::Slider::new(&mut params.gamma, 0.2..=1.1)
129 .text("Gamma"),
130 )
131 .changed();
132 changed |= ui
133 .add(
134 egui::Slider::new(&mut params.vignette_offset, 0.0..=1.0)
135 .text("Vignette"),
136 )
137 .changed();
138 });
139
140 egui::CollapsingHeader::new("Camera")
141 .default_open(false)
142 .show(ui, |ui| {
143 changed |= ui
144 .add(
145 egui::Slider::new(&mut params.camera_x, -1.0..=1.0)
146 .text("X"),
147 )
148 .changed();
149 changed |= ui
150 .add(
151 egui::Slider::new(&mut params.camera_y, 5.0..=20.0)
152 .text("Y"),
153 )
154 .changed();
155 changed |= ui
156 .add(
157 egui::Slider::new(&mut params.camera_z, 5.0..=20.0)
158 .text("Z"),
159 )
160 .changed();
161 changed |= ui
162 .add(
163 egui::Slider::new(&mut params.orbit_speed, 0.0..=1.0)
164 .text("speed"),
165 )
166 .changed();
167 });
168
169 egui::CollapsingHeader::new("Fractal")
170 .default_open(false)
171 .show(ui, |ui| {
172 changed |= ui
173 .add(
174 egui::Slider::new(&mut params.iterations, 10..=100)
175 .text("Iterations"),
176 )
177 .changed();
178 changed |= ui
179 .add(
180 egui::Slider::new(&mut params.bound, 1.0..=25.0)
181 .text("Bound"),
182 )
183 .changed();
184 changed |= ui
185 .add(
186 egui::Slider::new(&mut params.magic_number, 1.0..=100.0)
187 .text("Magic Number"),
188 )
189 .changed();
190 changed |= ui
191 .add(
192 egui::Slider::new(&mut params.cv_min, 1.0..=3.0)
193 .text("CV Min"),
194 )
195 .changed();
196 changed |= ui
197 .add(
198 egui::Slider::new(&mut params.cv_max, 2.0..=4.0)
199 .text("CV Max"),
200 )
201 .changed();
202 changed |= ui
203 .add(
204 egui::Slider::new(&mut params.os_base, 0.00001..=0.001)
205 .logarithmic(true)
206 .text("OS Base"),
207 )
208 .changed();
209 changed |= ui
210 .add(
211 egui::Slider::new(&mut params.os_scale, 0.001..=0.1)
212 .text("OS Scale"),
213 )
214 .changed();
215 ui.separator();
216 changed |= ui
217 .add(
218 egui::Slider::new(&mut params.fractal_scale, 0.01..=1.0)
219 .text("Fractal Scale"),
220 )
221 .changed();
222 });
223
224 egui::CollapsingHeader::new("Colors")
225 .default_open(false)
226 .show(ui, |ui| {
227 ui.horizontal(|ui| {
228 ui.label("Base Color:");
229 let mut color = [
230 params.base_color_r,
231 params.base_color_g,
232 params.base_color_b,
233 ];
234 if ui.color_edit_button_rgb(&mut color).changed() {
235 params.base_color_r = color[0];
236 params.base_color_g = color[1];
237 params.base_color_b = color[2];
238 changed = true;
239 }
240 });
241
242 ui.horizontal(|ui| {
243 ui.label("Light Color:");
244 let mut color = [
245 params.light_color_r,
246 params.light_color_g,
247 params.light_color_b,
248 ];
249 if ui.color_edit_button_rgb(&mut color).changed() {
250 params.light_color_r = color[0];
251 params.light_color_g = color[1];
252 params.light_color_b = color[2];
253 changed = true;
254 }
255 });
256
257 ui.horizontal(|ui| {
258 ui.label("Ambient Color:");
259 let mut color =
260 [params.ambient_r, params.ambient_g, params.ambient_b];
261 if ui.color_edit_button_rgb(&mut color).changed() {
262 params.ambient_r = color[0];
263 params.ambient_g = color[1];
264 params.ambient_b = color[2];
265 changed = true;
266 }
267 });
268 });
269
270 ui.separator();
271 ShaderControls::render_controls_widget(ui, &mut controls_request);
272 ui.separator();
273 should_start_export =
274 ExportManager::render_export_ui_widget(ui, &mut export_request);
275 });
276 })
277 } else {
278 self.base.render_ui(core, |_ctx| {})
279 };
280
281 self.base.export_manager.apply_ui_request(export_request);
282 if controls_request.should_clear_buffers {
283 self.clear_buffers(core);
284 }
285 self.base.apply_control_request(controls_request);
286
287 let current_time = self.base.controls.get_time(&self.base.start_time);
288
289 let delta = 1.0 / 60.0;
290 self.compute_shader
291 .set_time(current_time, delta, &core.queue);
292
293 if changed {
294 self.current_params = params;
295 self.compute_shader.set_custom_params(params, &core.queue);
296 }
297
298 if should_start_export {
299 self.base.export_manager.start_export();
300 }
301
302 self.compute_shader.dispatch(&mut frame.encoder, core);
303
304 self.base.renderer.render_to_view(&mut frame.encoder, &frame.view, &self.compute_shader.get_output_texture().bind_group);
305
306 self.base.end_frame(core, frame, full_output);
307
308 Ok(())
309 }
310
311 fn handle_input(&mut self, core: &Core, event: &WindowEvent) -> bool {
312 self.base.default_handle_input(core, event)
313 }
314}
315
316fn main() -> Result<(), Box<dyn std::error::Error>> {
317 env_logger::init();
318 let (app, event_loop) = cuneus::ShaderApp::new("Sinh 3D", 800, 300);
319
320 app.run(event_loop, SinhShader::init)
321}