From c3394e35663564776150b44a10d37dadde3dec0c Mon Sep 17 00:00:00 2001 From: altunenes Date: Sat, 30 Mar 2024 14:44:37 +0300 Subject: [PATCH] dark clouds --- Cargo.toml | 4 ++ shaders/darkclouds.wgsl | 84 +++++++++++++++++++++++++++ src/darkclouds.rs | 122 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 shaders/darkclouds.wgsl create mode 100644 src/darkclouds.rs diff --git a/Cargo.toml b/Cargo.toml index 5dbdb1d..da62605 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -341,3 +341,7 @@ path = "src/pixelflow.rs" name = "gaborlbrot" path = "src/gaborlbrot.rs" +[[bin]] +name = "darkclouds" +path = "src/darkclouds.rs" + diff --git a/shaders/darkclouds.wgsl b/shaders/darkclouds.wgsl new file mode 100644 index 0000000..0de4301 --- /dev/null +++ b/shaders/darkclouds.wgsl @@ -0,0 +1,84 @@ +const PI: f32 = 3.141592653589793; +struct TimeUniform { + time: f32, +}; +@group(1) @binding(0) +var u_time: TimeUniform; + +fn random(st: vec2) -> vec2 { + let st_transformed: vec2 = vec2( + dot(st, vec2(127.1, 311.7)), + dot(st, vec2(269.5, 183.3)) + ); + return -1.0 + 2.0 * fract(sin(st_transformed) * 43758.5453123); +} + +fn oscillate(minValue: f32, maxValue: f32, interval: f32, currentTime: f32) -> f32 { + return minValue + (maxValue - minValue) * 0.5 * (sin(2.0 * PI * currentTime / interval) + 1.0); +} + +fn noise(st: vec2) -> f32 { + let i: vec2 = floor(st); + let f: vec2 = fract(st); + let u: vec2 = f * f * ((3.0 - 2.0) * f); + return mix(mix(dot(random(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)), + dot(random(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)), u.x), + mix(dot(random(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)), + dot(random(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)), u.x), u.y); +} +fn rotate2D(r: f32) -> mat2x2 { + return mat2x2(cos(r), -sin(r), + sin(r), cos(r)); +} +fn sinh(x: f32) -> f32 { + return (exp(x) - exp(-x)) / 2.0; +} +@fragment +fn main(@builtin(position) FragCoord: vec4) -> @location(0) vec4 { + let iResolution: vec2 = vec2(640.0, 480.0); + let uv: vec2 = 3.0*(FragCoord.xy - 0.5 * iResolution) / min(iResolution.x, iResolution.y); + + var col: vec3 = vec3(0.0); + let t: f32 = u_time.time; + let oscillationFactors: vec3 = vec3(oscillate(0.5, 0.51, 15.0, t), oscillate(2.0, 2.51, 15.0, t), oscillate(0.5, 0.51, 15.0, t)); + + var N: vec2 = vec2(0.0); + var p: vec2 = uv + t / 20.0; + var S: f32 = oscillationFactors.y; + let m: mat2x2 = rotate2D(oscillationFactors.x); + let branchFactor: f32 = 1.78; + +var n: vec2 = vec2(0.0, 0.0); + +for (var j: i32 = 0; j < 45; j = j + 1) { + p *= m; + n *= m; + + let q: vec2 = p * S * f32(j) + n + t; + n += branchFactor * cos(q); + N += branchFactor * cos(q) / S * oscillationFactors.z; + + S *= 1.4 * sinh(0.9); +} + + let colorOffset: vec3 = vec3( + 0.1 * smoothstep(0.4, 1.0, sin(N.x)), + 0.5 * smoothstep(1.0, 1.0, sin(N.x)), + 0.1 * smoothstep(0.5, 1.0, cos(N.x)) + ); + + let flowColorChange: vec3 = vec3( + 1.5 * cos(1.0 * t + N.x), + 0.5 * sin(1.0 * t + N.y), + 1.5 * cos(1.0 * t + N.y) + ); + + let flowIntensity: vec3 = vec3( + 0.1 / length(1.03 * N), + smoothstep(5.5, 25.0, N.x), + smoothstep(5.5, 1.0, N.y) + ); + col = (vec3(0.5, 0.0, 2.1) * colorOffset + flowColorChange + 4.5 * flowIntensity) * + ((0.5 * N.x * 0.5 * N.y) + .0015 / length(1.0 * N)); + return vec4(col, 1.0); +} \ No newline at end of file diff --git a/src/darkclouds.rs b/src/darkclouds.rs new file mode 100644 index 0000000..b194fe7 --- /dev/null +++ b/src/darkclouds.rs @@ -0,0 +1,122 @@ +use nannou::prelude::*; +struct Model { + bind_group: wgpu::BindGroup, + render_pipeline: wgpu::RenderPipeline, + vertex_buffer: wgpu::Buffer, + time_uniform: wgpu::Buffer, + time_bind_group: wgpu::BindGroup, +} +#[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).run(); +} +fn model(app: &App) -> Model { + let w_id = app.new_window().size(800, 450).view(view).build().unwrap(); + 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/darkclouds.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 bind_group_layout = wgpu::BindGroupLayoutBuilder::new().build(device); + let bind_group = wgpu::BindGroupBuilder::new().build(device, &bind_group_layout); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Pipeline Layout"), + bind_group_layouts: &[&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"), + }); + Model { + bind_group, + vertex_buffer, + render_pipeline, + time_uniform, + time_bind_group, + } +} +fn view(app: &App, model: &Model, frame: Frame) { + let mut encoder = frame.command_encoder(); + let time = app.time; + let time_bytes = time.to_ne_bytes(); + let binding = app.main_window(); + let queue = binding.queue(); + 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.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 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