Skip to content

Commit

Permalink
mandelbrot_&_chladni_WGPU_shader
Browse files Browse the repository at this point in the history
  • Loading branch information
altunenes committed Feb 13, 2024
1 parent 114a5c2 commit 7c8c810
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,6 @@ path = "src/galaxy.rs"
name = "mandelbrotgpu"
path = "src/mandelbrotgpu.rs"

[[bin]]
name = "chladniwgpu"
path = "src/chladniwgpu.rs"
45 changes: 45 additions & 0 deletions shaders/chladniwgpu.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//One of my oldest shaders, you can find the online versions of both GLSL and WGSL online:
//https://compute.toys/view/713; https://www.shadertoy.com/view/ctK3DV
// Global constants
const PI: f32 = 3.14159265;
const L: f32 = 0.7; // Plate size is now correctly declared as a constant

// TimeUniform definition based on my wgpu rust code
struct TimeUniform {
time: f32,
};
@group(1) @binding(0)
var<uniform> u_time: TimeUniform;
fn implicit(x: f32, y: f32) -> f32 {
let t: f32 = u_time.time / 4.0;
let n1: f32 = 6.0 + 3.0 * sin(t);
let m1: f32 = 4.0 + 3.0 * cos(t);
let n2: f32 = 5.0 + 2.5 * cos(2.0 * t);
let m2: f32 = 3.0 + 2.5 * sin(2.0 * t);
let val1: f32 = cos(n1 * PI * x / L) * cos(m1 * PI * y / L) -
cos(m1 * PI * x / L) * cos(n1 * PI * y / L);
let val2: f32 = cos(n2 * PI * x / L) * cos(m2 * PI * y / L) -
cos(m2 * PI * x / L) * cos(n2 * PI * y / L);
return val1 + val2;
}
fn delf_delx(x: f32, y: f32) -> f32 {
let dx: f32 = 0.001; // Use `let` for local immutable bindings
return (implicit(x + dx, y) - implicit(x - dx, y)) / (2.5 * dx);
}
fn delf_dely(x: f32, y: f32) -> f32 {
let dy: f32 = 0.001; // Use `let` for local immutable bindings
return (implicit(x, y + dy) - implicit(x, y - dy)) / (2.5 * dy);
}
fn gradient(x: f32, y: f32) -> vec2<f32> {
return vec2<f32>(delf_delx(x, y), delf_dely(x, y));
}
@fragment
fn main(@builtin(position) FragCoord: vec4<f32>) -> @location(0) vec4<f32> {
let resolution: vec2<f32> = vec2<f32>(800.0, 450.0); // Assume a fixed resolution
let uv: vec2<f32> = (FragCoord.xy - 0.5 * resolution) / min(resolution.y, resolution.x);
let g: vec2<f32> = gradient(uv.x, uv.y);
let unit: f32 = 25.0 / resolution.y;
let sharpVal: f32 = smoothstep(-unit, unit, abs(implicit(uv.x, uv.y)) / sqrt(g.x * g.x + g.y * g.y));
let col: vec3<f32> = 0.5 + 0.5 * cos(u_time.time + vec3<f32>(0.6, 0.8, 1.0) + 2.0 * PI * vec3<f32>(sharpVal));
return vec4<f32>(col, 1.0);
}
126 changes: 126 additions & 0 deletions src/chladniwgpu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//One of my oldest shaders, you can find the online versions of both GLSL and WGSL online:
//https://compute.toys/view/713; https://www.shadertoy.com/view/ctK3DV
//Note CPU version: chladni.rs
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();
// 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/chladniwgpu.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::<f32>() 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::<Vertex>(&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::<f32>() 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 as f32;
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) }
}

0 comments on commit 7c8c810

Please sign in to comment.