-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
582 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
use wgpu::util::DeviceExt; | ||
|
||
use crate::frame_rate::FrameRate; | ||
use crate::program::{Program, ProgramError}; | ||
use crate::shader_builder::ShaderBuilder; | ||
|
||
/// A simple struct to store a wgpu pass with a uniform buffer. | ||
#[derive(Debug)] | ||
pub struct Pass { | ||
/// Pipeline that will be called to render the pass | ||
pub pipeline: wgpu::RenderPipeline, | ||
/// Buffer bind group for this pass. | ||
pub bind_group: wgpu::BindGroup, | ||
/// Single uniform buffer for this pass. | ||
pub uniform_buf: wgpu::Buffer, | ||
// Index buffer. | ||
pub index_buffer: wgpu::Buffer, | ||
// Vertex buffer. | ||
pub vertex_buffer: wgpu::Buffer, | ||
// | ||
pub index_count: u32, | ||
} | ||
|
||
#[repr(C)] | ||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] | ||
struct Vertex { | ||
position: [f32; 3], | ||
} | ||
|
||
// lib.rs | ||
impl Vertex { | ||
fn desc() -> wgpu::VertexBufferLayout<'static> { | ||
wgpu::VertexBufferLayout { | ||
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress, | ||
step_mode: wgpu::VertexStepMode::Vertex, | ||
attributes: &[wgpu::VertexAttribute { | ||
offset: 0, | ||
shader_location: 0, | ||
format: wgpu::VertexFormat::Float32x3, | ||
}], | ||
} | ||
} | ||
} | ||
|
||
/// Demo raymarching program. | ||
/// Everything is done in the shader. | ||
/// Provides both 2d and 3d raymarching. | ||
#[derive(Debug)] | ||
pub struct DemoRaymarchingProgram { | ||
render_pass: Pass, | ||
_start_time: instant::Instant, // std::time::Instant is not compatible with wasm | ||
last_update: instant::Instant, | ||
elapsed: f32, // elapsed take the speed into consideration | ||
frame_rate: FrameRate, | ||
size: [f32; 2], | ||
} | ||
|
||
impl Program for DemoRaymarchingProgram { | ||
/// Create program. | ||
/// Assume the `render_pipeline` will be properly initialized. | ||
fn init( | ||
surface: &wgpu::Surface, | ||
device: &wgpu::Device, | ||
adapter: &wgpu::Adapter, | ||
) -> Result<Self, ProgramError> { | ||
let render_pass = Self::create_render_pass(surface, device, adapter)?; | ||
|
||
Ok(Self { | ||
render_pass, | ||
_start_time: instant::Instant::now(), | ||
last_update: instant::Instant::now(), | ||
elapsed: 0.0, | ||
frame_rate: FrameRate::new(200), | ||
size: [0.0, 0.0], | ||
}) | ||
} | ||
|
||
/// Get program name. | ||
fn get_name(&self) -> &'static str { | ||
"Demo raymarching" | ||
} | ||
|
||
/// Recreate render pass. | ||
fn update_passes( | ||
&mut self, | ||
surface: &wgpu::Surface, | ||
device: &wgpu::Device, | ||
adapter: &wgpu::Adapter, | ||
) -> Result<(), ProgramError> { | ||
self.render_pass = Self::create_render_pass(surface, device, adapter)?; | ||
Ok(()) | ||
} | ||
|
||
// Resize owned textures if needed, nothing for the demo here. | ||
fn resize( | ||
&mut self, | ||
surface_configuration: &wgpu::SurfaceConfiguration, | ||
_device: &wgpu::Device, | ||
_queue: &wgpu::Queue, | ||
) { | ||
self.size[0] = surface_configuration.width as f32; | ||
self.size[1] = surface_configuration.height as f32; | ||
} | ||
|
||
/// Update program before rendering. | ||
fn update(&mut self, queue: &wgpu::Queue) { | ||
// Set the edge count of the regular raymarching. | ||
// This is not exposed in the ui on purpose to demonstrate the rust hot reload. | ||
|
||
// update elapsed time, taking speed into consideration. | ||
let last_frame_duration = self.last_update.elapsed().as_secs_f32(); | ||
self.elapsed += last_frame_duration; | ||
self.frame_rate.update(last_frame_duration); | ||
self.last_update = instant::Instant::now(); | ||
queue.write_buffer( | ||
&self.render_pass.uniform_buf, | ||
0, | ||
bytemuck::cast_slice(&[self.elapsed, self.size[0], self.size[1], 0.0]), | ||
); | ||
} | ||
|
||
/// Render program. | ||
fn render(&self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) { | ||
// Create a command encoder. | ||
let mut encoder = | ||
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); | ||
|
||
{ | ||
// render pass. | ||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { | ||
label: None, | ||
color_attachments: &[Some(wgpu::RenderPassColorAttachment { | ||
view, | ||
resolve_target: None, | ||
ops: wgpu::Operations { | ||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), | ||
store: true, | ||
}, | ||
})], | ||
depth_stencil_attachment: None, | ||
}); | ||
render_pass.set_pipeline(&self.render_pass.pipeline); | ||
render_pass.set_bind_group(0, &self.render_pass.bind_group, &[]); | ||
render_pass.set_vertex_buffer(0, self.render_pass.vertex_buffer.slice(..)); | ||
render_pass.set_index_buffer( | ||
self.render_pass.index_buffer.slice(..), | ||
wgpu::IndexFormat::Uint16, | ||
); // 1. | ||
render_pass.draw_indexed(0..self.render_pass.index_count, 0, 0..1); // 2. | ||
} | ||
|
||
queue.submit(Some(encoder.finish())); | ||
} | ||
|
||
/// Draw ui with egui. | ||
fn draw_ui(&mut self, ui: &mut egui::Ui) { | ||
ui.heading("Settings"); | ||
ui.separator(); | ||
ui.label(std::format!("framerate: {:.0}fps", self.frame_rate.get())); | ||
} | ||
} | ||
|
||
impl DemoRaymarchingProgram { | ||
/// Create render pipeline. | ||
/// In debug mode it will return a `ProgramError` if it failed compiling a shader | ||
/// In release/wasm, il will crash since wgpu does not return errors in such situations. | ||
fn create_render_pipeline( | ||
surface: &wgpu::Surface, | ||
device: &wgpu::Device, | ||
adapter: &wgpu::Adapter, | ||
uniforms_bind_group_layout: &wgpu::BindGroupLayout, | ||
) -> Result<wgpu::RenderPipeline, ProgramError> { | ||
let shader = ShaderBuilder::create_module(device, "demo_raymarching/draw.wgsl")?; | ||
// let shader = ShaderBuilder::create_module(device, "test_preprocessor/draw.wgsl")?; // uncomment to test preprocessor | ||
|
||
let swapchain_capabilities = surface.get_capabilities(adapter); | ||
let swapchain_format = swapchain_capabilities.formats[0]; | ||
|
||
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { | ||
label: Some("Render Pipeline Layout"), | ||
bind_group_layouts: &[uniforms_bind_group_layout], | ||
push_constant_ranges: &[], | ||
}); | ||
|
||
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { | ||
label: None, | ||
layout: Some(&layout), | ||
vertex: wgpu::VertexState { | ||
module: &shader, | ||
entry_point: "vs_main", | ||
buffers: &[Vertex::desc()], | ||
}, | ||
fragment: Some(wgpu::FragmentState { | ||
module: &shader, | ||
entry_point: "fs_main", | ||
targets: &[Some(swapchain_format.into())], | ||
}), | ||
primitive: wgpu::PrimitiveState::default(), | ||
depth_stencil: None, | ||
multisample: wgpu::MultisampleState::default(), | ||
multiview: None, | ||
}); | ||
|
||
Ok(pipeline) | ||
} | ||
|
||
/// Create render pass. | ||
/// Will return an error in debug, and crash in release/wasm if a shader is malformed. | ||
fn create_render_pass( | ||
surface: &wgpu::Surface, | ||
device: &wgpu::Device, | ||
adapter: &wgpu::Adapter, | ||
) -> Result<Pass, ProgramError> { | ||
// create uniform buffer. | ||
let uniforms = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { | ||
label: Some("Camera Buffer"), | ||
contents: bytemuck::cast_slice(&[0.0, 0.0, 0.0, 0.0]), | ||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, | ||
}); | ||
|
||
let uniforms_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: None, | ||
}, | ||
count: None, | ||
}], | ||
label: Some("uniforms_bind_group_layout"), | ||
}); | ||
|
||
let uniforms_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { | ||
layout: &uniforms_bind_group_layout, | ||
entries: &[wgpu::BindGroupEntry { | ||
binding: 0, | ||
resource: uniforms.as_entire_binding(), | ||
}], | ||
label: Some("uniforms_bind_group"), | ||
}); | ||
|
||
// lib.rs | ||
const VERTICES: &[Vertex] = &[ | ||
Vertex { | ||
position: [-1.0, -1.0, 0.0], | ||
}, | ||
Vertex { | ||
position: [-1.0, 1.0, 0.0], | ||
}, | ||
Vertex { | ||
position: [1.0, 1.0, 0.0], | ||
}, | ||
Vertex { | ||
position: [1.0, -1.0, 0.0], | ||
}, | ||
]; | ||
|
||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { | ||
label: Some("Vertex Buffer"), | ||
contents: bytemuck::cast_slice(VERTICES), | ||
usage: wgpu::BufferUsages::VERTEX, | ||
}); | ||
|
||
const INDICES: &[u16] = &[1, 0, 2, 2, 0, 3]; | ||
let index_count = INDICES.len() as u32; | ||
|
||
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { | ||
label: Some("Index Buffer"), | ||
contents: bytemuck::cast_slice(INDICES), | ||
usage: wgpu::BufferUsages::INDEX, | ||
}); | ||
|
||
let pipeline = | ||
Self::create_render_pipeline(surface, device, adapter, &uniforms_bind_group_layout)?; | ||
|
||
Ok(Pass { | ||
pipeline, | ||
bind_group: uniforms_bind_group, | ||
uniform_buf: uniforms, | ||
index_buffer, | ||
vertex_buffer, | ||
index_count, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Vertex shader | ||
struct Uniforms { | ||
elapsed: f32, | ||
width: f32, | ||
height: f32, | ||
_padding: f32, // padding to 16 bytes, required for WebGL. | ||
}; | ||
|
||
struct VertexInput { | ||
@location(0) position: vec3<f32>, | ||
}; | ||
|
||
struct VertexOutput { | ||
@builtin(position) clip_position: vec4<f32>, | ||
}; | ||
|
||
@group(0) @binding(0) | ||
var<uniform> uniforms: Uniforms; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#import "demo_raymarching/common.wgsl" | ||
|
||
#import "demo_raymarching/draw_2d.wgsl" | ||
#import "demo_raymarching/draw_3d.wgsl" | ||
|
||
@vertex | ||
fn vs_main( | ||
model: VertexInput, | ||
) -> VertexOutput { | ||
var out: VertexOutput; | ||
out.clip_position = vec4<f32>(model.position, 1.0); | ||
return out; | ||
} | ||
|
||
@fragment | ||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { | ||
// let color = sdf_2d(in.clip_position.xy); | ||
|
||
let xy = in.clip_position.xy / vec2<f32>(uniforms.width, uniforms.height); | ||
let color = sdf_3d(in.clip_position.xy); | ||
return color; | ||
} | ||
|
||
|
Oops, something went wrong.