diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d58db04..e8b330e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - bin: [footsteps, blobs, fibonacci, triangles, peace, attractors, waves, waves2, cafe_wall, munker, pdiamond, lilac, fourier, nblur, pixelrain, pina, hole,neuralnet, bhole, oscillation, chladni, hilbert, hilbertimg, snowflake, mandelbrot, leviant, eyes, imgblob, scramble, pixelate, rainbowimage,gaborill,munkerclock, voronoi, lorenz, ulam, dfft, butter2d, gaborwgpu, galaxy, chladniwgpu, snowflakewgpu, spiralimgwgpu, neuralnetwgpu, imlenswgpu, fbmflowgpu, lovewgpu, neurons, asahi, voronoiwgpu, fluid, asahi2, sinh, tree, expmandelbrotgpu, pupils, pixelflow,darkclouds, tunnel, neurons2, nebula, pixelsum, smoothneurons,gaussiansplat,gaborimage,rorschach,stripes,psychology,3dneuron,mandelbulb,dottedlines,ornaments,faketunnel,smoothvoro,wrapper,galaxy2,GPUattractor,peaceGPU,kleinian,adelson,orbittraps,golf] + bin: [footsteps, blobs, fibonacci, triangles, peace, attractors, waves, waves2, cafe_wall, munker, pdiamond, lilac, fourier, nblur, pixelrain, pina, hole,neuralnet, bhole, oscillation, chladni, hilbert, hilbertimg, snowflake, mandelbrot, leviant, eyes, imgblob, scramble, pixelate, rainbowimage,gaborill,munkerclock, voronoi, lorenz, ulam, dfft, butter2d, gaborwgpu, galaxy, chladniwgpu, snowflakewgpu, spiralimgwgpu, neuralnetwgpu, imlenswgpu, fbmflowgpu, lovewgpu, neurons, asahi, voronoiwgpu, fluid, asahi2, sinh, tree, expmandelbrotgpu, pupils, pixelflow,darkclouds, tunnel, neurons2, nebula, pixelsum, smoothneurons,gaussiansplat,gaborimage,rorschach,stripes,psychology,3dneuron,mandelbulb,dottedlines,ornaments,faketunnel,smoothvoro,wrapper,galaxy2,GPUattractor,peaceGPU,kleinian,adelson,orbittraps,golf,sdrect] include: - target: x86_64-unknown-linux-gnu ext: "" diff --git a/Cargo.toml b/Cargo.toml index c5a70a5..3a855b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -344,4 +344,8 @@ path = "src/orbittraps.rs" [[bin]] name= "golf" -path = "src/golf.rs" \ No newline at end of file +path = "src/golf.rs" + +[[bin]] +name = "sdrect" +path = "src/sdrect.rs" \ No newline at end of file diff --git a/shaders/sdrect.wgsl b/shaders/sdrect.wgsl new file mode 100644 index 0000000..91ff0f6 --- /dev/null +++ b/shaders/sdrect.wgsl @@ -0,0 +1,160 @@ +const PI: f32 = 3.14159265358979323846; +const LIGHT_INTENSITY: f32 = 1.2; +const RIM_POWER: f32 = 1.0; +const AO_STRENGTH: f32 = 0.05; +const ENV_LIGHT_STRENGTH: f32 = 0.4; +const IRIDESCENCE_POWER: f32 = 0.15; +const FALLOFF_DISTANCE: f32 = 2.5; +const VIGNETTE_STRENGTH: f32 = 0.25; + +struct TimeUniform { + time: f32, +}; +struct Params { + lambda: f32, + theta: f32, + sigma: f32, + gamma: f32, + blue: f32, +}; + +@group(0) @binding(1) +var params: Params; +@group(1) @binding(0) +var u_time: TimeUniform; + +fn rot(a: f32) -> mat2x2 { + let c = cos(a); + let s = sin(a); + return mat2x2(c, s, -s, c); +} + +fn osc(mn: f32, mx: f32, iv: f32, n: f32) -> f32 { + return mn + (mx - mn) * 0.5 * (sin(2.0 * PI * n / iv) + 1.0); +} + +fn sdRect(p: vec2, r: f32, a: f32) -> f32 { + let w = r * 0.8; + let h = r * 0.4; + var rp = p * rot(-a); + let d = abs(rp) - vec2(w, h); + let dst = min(max(d.x, d.y), 0.0) + length(max(d, vec2(0.0))); + return max(dst, 0.1); +} + +fn getVerts(uv: vec2, s: f32, a: f32) -> array, 4> { + var v: array, 4>; + let w = s * 0.8; + let h = s * 0.4; + v[0] = vec2(-w, h); + v[1] = vec2(w, h); + v[2] = vec2(w, -h); + v[3] = vec2(-w, -h); + let r = rot(a); + for(var i = 0; i < 4; i++) { + v[i] = r * v[i] + uv; + } + return v; +} + +fn getLI(uv: vec2, rect: f32, l: f32, t: f32, a: f32) -> f32 { + let v = getVerts(uv, l, a); + let ps1 = sin(l * 13.37 + t * 0.3); + let ps2 = cos(l * 7.54 - t * 0.4); + let ps3 = sin(l * 9.21 + t * 0.5); + let ps4 = cos(l * 11.13 + t * 0.6); + + let lp1 = v[0] + vec2(cos(t * 0.5 + ps1), sin(t * 0.7 + ps2)) * 0.3; + let lp2 = v[1] + vec2(sin(t * 0.3 + ps2), cos(t * 0.4 + ps3)) * 0.3; + let lp3 = v[2] + vec2(cos(t * 0.6 + ps3), sin(t * 0.5 + ps4)) * 0.3; + let lp4 = v[3] + vec2(sin(t * 0.4 + ps4), cos(t * 0.6 + ps1)) * 0.3; + + let d1 = length(uv - lp1) * (1.0 + 0.2 * sin(l * 15.0 + t)); + let d2 = length(uv - lp2) * (1.0 + 0.2 * cos(l * 12.0 - t)); + let d3 = length(uv - lp3) * (1.0 + 0.2 * sin(l * 9.0 + t * 0.7)); + let d4 = length(uv - lp4) * (1.0 + 0.2 * cos(l * 11.0 - t * 0.8)); + + let f1 = 2.0 / (4.0 + d1 * FALLOFF_DISTANCE); + let f2 = 2.0 / (4.0 + d2 * FALLOFF_DISTANCE); + let f3 = 2.0 / (4.0 + d3 * FALLOFF_DISTANCE); + let f4 = 2.0 / (4.0 + d4 * FALLOFF_DISTANCE); + + var w1 = 0.25 + 0.1 * sin(l * 11.0 + t); + var w2 = 0.25 + 0.1 * cos(l * 13.0 - t); + var w3 = 0.25 + 0.1 * sin(l * 17.0 + t * 0.5); + var w4 = 0.25 + 0.1 * cos(l * 15.0 - t * 0.7); + + let tw = w1 + w2 + w3 + w4; + w1 /= tw; + w2 /= tw; + w3 /= tw; + w4 /= tw; + + let ao = 1.4 - (l * AO_STRENGTH) * (1.0 + 0.2 * sin(l * 20.0 + t)); + let n = normalize(vec2(cos(a + l), sin(a + l))); + var rm = 1.1 - abs(dot(normalize(uv), n)); + rm = pow(rm, RIM_POWER); + + let vl = f1 * w1 + f2 * w2 + f3 * w3 + f4 * w4; + let sh = sin(l * 10.0 + t) * cos(l * 7.0 - t); + + return vl * (1.0 + 0.15 * sh) * ao * LIGHT_INTENSITY + rm * 0.4; +} + +fn getEL(uv: vec2, a: f32, l: f32, t: f32) -> f32 { + let ld = normalize(vec2(cos(t), sin(t))); + let n = normalize(vec2(cos(a), sin(a))); + var el = dot(n, ld); + el = el * 0.5 + 0.5; + let dp = 1.0 - (l / 1.5); + let le = sin(l * 4.0 + t) * 0.5 + 0.5; + return mix(el, le, 0.5) * dp * ENV_LIGHT_STRENGTH; +} + +@fragment +fn main(@builtin(position) fc: vec4) -> @location(0) vec4 { + var col = vec4(0.5); + var h: vec4; + let ss = 1.3 * vec2(1920.0, 1080.0); + let t = u_time.time * 0.5; + var ang = 0.25; + let fp = cos(t * 0.5) * PI * 0.25; + let gl = osc(0.4, 1.5, 8.0, u_time.time); + let asd = osc(0.3, 0.07, 25.0, u_time.time); + + var i: f32 = 1.5; + while(i > 0.003) { + let l = i * 1.0; + let fd = sin(t + l * 0.2) * cos(t * 0.5 + l * 0.1); + let w = cos(t * 0.7 + l * 0.15) * sin(t * 0.3 + i); + let ta = fp; + ang -= sin(ang - sin(ta)) * (0.5 + 0.5 * sin(l)); + let af = sign(sin(l * 0.5)) * sin(t + i * 2.0); + var uv = 2.7 * (fc.xy + fc.xy - ss) / ss.y; + uv.y += 0.5; + uv.x += 1.0; + uv *= rot(i + (ang + af) + fp); + let rect = sdRect(uv, i, ang + t * (1.0 + 0.2 * sin(l))); + let a = smoothstep(0.0, 0.2, (rect - 0.1) * ss.y * 0.15); + let li = getLI(uv, rect, l, t, ang); + let el = getEL(uv, ang, l, t); + let d1 = osc(0.5, 1.0, 5.0, u_time.time); + let d2 = osc(0.1, 1.0, 5.0, u_time.time); + let ci = 0.8 + 0.2 * sin(l * 24.37 + t) * cos(l * 12.54 - t * 0.4) * sin(ang * 3.0 + t * 0.7); + let cs = 0.2 + 0.1 * cos(l * 9.21 + t * 0.5) * sin(ang * 5.0 - t * 0.3); + h = sin(i / d2 + ang / d1 + vec4(params.blue, 2.0, 3.0, 1.0) + fd * 0.5) * cs + ci; + let lc = h * (li * gl + el); + let df = params.lambda- (i - 0.003) / (1.5 - 0.003); + let ir = sin(dot(uv, uv) * 4.0 + t) * IRIDESCENCE_POWER * df + 0.95; + var cwi = lc * vec4(ir, ir * 0.98, ir * 1.02, 1.0); + let d4 = osc(0.15, 0.2, 10.0, u_time.time); + let mf = d4 / (rect + params.sigma) * (1.0 - df * 0.25); + col = mix(cwi, col, a) * mix(vec4(1.0), h + params.theta * a * 0.1*(uv.x / rect + li), mf); + i -= asd; + } + + col = vec4(col.rgb * (params.gamma + gl * 0.4), 1.0); + let vuv = (fc.xy - 0.5 * vec2(1920.0, 1080.0)) / 1080.0; + let v = 1.0 - dot(vuv, vuv) * VIGNETTE_STRENGTH; + return vec4(col.r * v, col.g * v, col.b * v, col.a); +} \ No newline at end of file diff --git a/src/sdrect.rs b/src/sdrect.rs new file mode 100644 index 0000000..cbb8ff3 --- /dev/null +++ b/src/sdrect.rs @@ -0,0 +1,217 @@ +use nannou::prelude::*; +use nannou_egui::{self, egui, Egui}; +struct Model { + render_pipeline: wgpu::RenderPipeline, + vertex_buffer: wgpu::Buffer, + time_uniform: wgpu::Buffer, + time_bind_group: wgpu::BindGroup, + params_uniform: wgpu::Buffer, + params_bind_group: wgpu::BindGroup, + settings:Settings, + egui:Egui, +} +struct Settings { + lambda: f32, + theta: f32, + sigma: f32, + gamma:f32, + blue:f32, + show_ui: bool, +} +#[repr(C)] +#[derive(Clone, Copy)] +struct Vertex { + position: [f32; 2], +} +const VERTICES: [Vertex; 6] = [ + Vertex { position: [-1.0, -1.0] }, + Vertex { position: [ 1.0, -1.0] }, + Vertex { position: [-1.0, 1.0] }, + Vertex { position: [ 1.0, -1.0] }, + Vertex { position: [ 1.0, 1.0] }, + Vertex { position: [-1.0, 1.0] }, +]; +fn main() { + nannou::app(model) + .update(update) + .run(); +} +fn update(app: &App, model: &mut Model, update: Update) { + let egui = &mut model.egui; + egui.set_elapsed_time(update.since_start); + let ctx = egui.begin_frame(); + if app.keys.down.contains(&Key::H) { + model.settings.show_ui = !model.settings.show_ui; + } + egui::Window::new("Shader Settings").show(&ctx, |ui| { + ui.add(egui::Slider::new(&mut model.settings.lambda, 0.0..=3.14).text("df")); + ui.add(egui::Slider::new(&mut model.settings.theta, 0.0..=5.0).text("hue")); + ui.add(egui::Slider::new(&mut model.settings.sigma, 0.0..=1.0).text("mix?")); + ui.add(egui::Slider::new(&mut model.settings.gamma, 0.0..=2.0).text("gamma")); + ui.add(egui::Slider::new(&mut model.settings.blue, 0.0..=12.0).text("red")); + + }); + let params_data = [model.settings.lambda, model.settings.theta, model.settings.sigma,model.settings.gamma,model.settings.blue]; + let params_bytes = bytemuck::cast_slice(¶ms_data); + app.main_window().queue().write_buffer(&model.params_uniform, 0, ¶ms_bytes); +} +fn raw_window_event(app: &App, model: &mut Model, event: &nannou::winit::event::WindowEvent) { + model.egui.handle_raw_event(event); + if let nannou::winit::event::WindowEvent::KeyboardInput { input, .. } = event { + if let (Some(nannou::winit::event::VirtualKeyCode::F), true) = + (input.virtual_keycode, input.state == nannou::winit::event::ElementState::Pressed) + { + let window = app.main_window(); + let fullscreen = window.fullscreen().is_some(); + window.set_fullscreen(!fullscreen); + } + } +} +fn model(app: &App) -> Model { + let w_id = app.new_window().raw_event(raw_window_event). + size(512, 512).view(view).build().unwrap(); + // The gpu device associated with the window's swapchain + let window = app.window(w_id).unwrap(); + let device = window.device(); + let format = Frame::TEXTURE_FORMAT; + let sample_count = window.msaa_samples(); + let vs_desc = wgpu::include_wgsl!("../shaders/vs.wgsl"); + let fs_desc = wgpu::include_wgsl!("../shaders/sdrect.wgsl"); + let vs_mod = device.create_shader_module(vs_desc); + let fs_mod = device.create_shader_module(fs_desc); + let vertices_bytes = vertices_as_bytes(&VERTICES[..]); + let usage = wgpu::BufferUsages::VERTEX; + let vertex_buffer = device.create_buffer_init(&BufferInitDescriptor { + label: None, + contents: vertices_bytes, + usage, + }); + let time_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: wgpu::BufferSize::new(std::mem::size_of::() as _), + }, + count: None, + }, + ], + label: Some("time_bind_group_layout"), + }); + let params_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("params_bind_group_layout"), + entries: &[wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: wgpu::BufferSize::new((std::mem::size_of::() * 5) as _), + }, + count: None, + }], + }); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Pipeline Layout"), + bind_group_layouts: &[¶ms_bind_group_layout, &time_bind_group_layout], + push_constant_ranges: &[], + }); + let render_pipeline = wgpu::RenderPipelineBuilder::from_layout(&pipeline_layout, &vs_mod) + .fragment_shader(&fs_mod) + .color_format(format) + .add_vertex_buffer::(&wgpu::vertex_attr_array![0 => Float32x2]) + .sample_count(sample_count) + .build(device); + let time_uniform = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Time Uniform Buffer"), + size: std::mem::size_of::() as wgpu::BufferAddress, + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + let time_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &time_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: time_uniform.as_entire_binding(), + }, + ], + label: Some("time_bind_group"), + }); + let settings = Settings { + lambda: 1.0, + theta:0.5, + sigma:0.02, + gamma:0.7, + blue:1.0, + show_ui:true, + }; + let params_data = [settings.lambda, settings.theta, settings.sigma,settings.gamma,settings.blue]; + let params_bytes = bytemuck::cast_slice(¶ms_data); + let params_uniform = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Params Uniform"), + contents: params_bytes, + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + let params_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: ¶ms_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 1, + resource: params_uniform.as_entire_binding(), + }, + ], + label: Some("params_bind_group"), + }); + let window = app.window(w_id).unwrap(); + let egui = Egui::from_window(&window); + Model { + params_bind_group, + settings, + params_uniform, + egui, + vertex_buffer, + render_pipeline, + time_uniform, + time_bind_group, + } +} +fn view(app: &App, model: &Model, frame: Frame) { + let draw = app.draw(); + draw.background().color(BLACK); + let time = app.time; + let time_bytes = time.to_ne_bytes(); + let binding = app.main_window(); + let queue = binding.queue(); + { + let mut encoder = frame.command_encoder(); + queue.write_buffer(&model.time_uniform, 0, &time_bytes); + let mut render_pass = wgpu::RenderPassBuilder::new() + .color_attachment(frame.texture_view(), |color| color) + .begin(&mut encoder); + render_pass.set_bind_group(0, &model.params_bind_group, &[]); + render_pass.set_bind_group(1, &model.time_bind_group, &[]); + render_pass.set_pipeline(&model.render_pipeline); + render_pass.set_vertex_buffer(0, model.vertex_buffer.slice(..)); + let vertex_range = 0..VERTICES.len() as u32; + let instance_range = 0..1; + render_pass.draw(vertex_range, instance_range); + } + if model.settings.show_ui { + model.egui.draw_to_frame(&frame).unwrap(); + } + if app.keys.down.contains(&Key::Space) { + let file_path = app + .project_path() + .expect("failed to locate project directory") + .join("frames") + .join(format!("{:0}.png", app.elapsed_frames())); + app.main_window().capture_frame(file_path); + } +} +fn vertices_as_bytes(data: &[Vertex]) -> &[u8] { + unsafe { wgpu::bytes::from_slice(data) } +} \ No newline at end of file