Add simple shading

This commit is contained in:
Marco Thomas
2021-08-23 16:17:21 +02:00
parent 16c8cdfb91
commit c79954896a
4 changed files with 220 additions and 6 deletions

View File

@@ -10,3 +10,4 @@ I will try to keep this list up to date with all resources, which
I use for learning.
- https://sotrh.github.io/learn-wgpu/#what-is-wgpu (Starting point for this project)
- https://github.com/sotrh/learn-wgpu (Matching GitHub Repo)
- https://www.youtube.com/watch?v=vV8mwo65kR8 (Building WebGPU with Rust Talk by FOSDEM)

27
src/challenge_shader.wgsl Normal file
View File

@@ -0,0 +1,27 @@
// Vertex shader
struct VertexOutput {
[[builtin(position)]] clip_coordinates: vec4<f32>;
[[location(0)]] position: vec2<f32>;
};
[[stage(vertex)]]
fn main(
[[builtin(vertex_index)]] in_vertex_index: u32,
) -> VertexOutput {
var out: VertexOutput;
let x = f32(1 - i32(in_vertex_index)) * 0.5;
let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5;
out.clip_coordinates = vec4<f32>(x, y, 0.0, 1.0);
out.position = vec2<f32>(x, y);
return out;
}
// Fragment shader
[[stage(fragment)]]
fn main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
let r = in.position.x;
let g = in.position.y;
return vec4<f32>(r, g, 0.1, 1.0);
}

23
src/shader.wgsl Normal file
View File

@@ -0,0 +1,23 @@
// Vertex shader
struct VertexOutput {
[[builtin(position)]] clip_position: vec4<f32>;
};
[[stage(vertex)]]
fn main(
[[builtin(vertex_index)]] in_vertex_index: u32,
) -> VertexOutput {
var out: VertexOutput;
let x = f32(1 - i32(in_vertex_index)) * 0.5;
let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5;
out.clip_position = vec4<f32>(x, y, 0.0, 1.0);
return out;
}
// Fragment shader
[[stage(fragment)]]
fn main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
return vec4<f32>(0.3, 0.2, 0.1, 1.0);
}

View File

@@ -1,3 +1,4 @@
use wgpu::PrimitiveTopology;
use winit::{
event::*,
window::Window,
@@ -12,24 +13,32 @@ pub struct State {
swap_chain: wgpu::SwapChain,
pub size: winit::dpi::PhysicalSize<u32>,
clear_color: wgpu::Color,
render_pipeline: wgpu::RenderPipeline,
challenge_render_pipeline: wgpu::RenderPipeline,
use_challenge_render_pipeline: bool,
}
impl State {
pub async fn new(window: &Window) -> Self {
// actual screen size
let size = window.inner_size();
// handle to gpu
// PRIMARY, VULKAN, DX12, METAL, BROWSER_WEBGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
// a surface to draw to
let surface = unsafe { instance.create_surface(window) };
// get a working adapter for the current system
// get a physical adapter for the current system
let adapter = instance.request_adapter(
&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
compatible_surface: Some(&surface),
},
).await.unwrap();
// adapter device and queue
// logical device and command queue to work with
let (device, queue) = adapter.request_device(
&wgpu::DeviceDescriptor {
// no special features required
@@ -40,6 +49,7 @@ impl State {
},
None, // Trace path
).await.unwrap();
// description of the swap_chain
let sc_desc = wgpu::SwapChainDescriptor {
// how textures will be used
@@ -52,9 +62,139 @@ impl State {
// how to sync with the display
present_mode: wgpu::PresentMode::Fifo,
};
// actually create a swap_chain
let swap_chain = device.create_swap_chain(&surface, &sc_desc);
// load shader file
let shader = device.create_shader_module(
&wgpu::ShaderModuleDescriptor {
label: Some("Shader"),
flags: wgpu::ShaderFlags::all(),
source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
}
);
// TODO
let render_pipeline_layout = device.create_pipeline_layout(
&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layput"),
bind_group_layouts: &[],
push_constant_ranges: &[],
}
);
// add everything to the render_pipeline
let render_pipeline = device.create_render_pipeline(
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
// function name in shader.wgsl for [[stage(vertex)]]
entry_point: "main",
// already specified in the shader
buffers: &[],
},
// needed to sotre color data to swap_chain
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "main",
// setup of color outputs
targets: &[wgpu::ColorTargetState {
format: sc_desc.format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrite::ALL,
}],
}),
primitive: wgpu::PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
// triangle facing forward when Counter Clock Wise
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
polygon_mode: wgpu::PolygonMode::Fill,
clamp_depth: false,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
// only use 1 sample => no extra sampling
count: 1,
// use all
mask: !0,
// not using anti aliasing
alpha_to_coverage_enabled: false,
}
}
);
// overwrite challenge shader file to shader
let shader = device.create_shader_module(
&wgpu::ShaderModuleDescriptor {
label: Some("Shader"),
flags: wgpu::ShaderFlags::all(),
source: wgpu::ShaderSource::Wgsl(
include_str!("challenge_shader.wgsl").into()),
}
);
// TODO
let render_pipeline_layout = device.create_pipeline_layout(
&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layput"),
bind_group_layouts: &[],
push_constant_ranges: &[],
}
);
// add everything to the challnge_render_pipeline
let challenge_render_pipeline = device.create_render_pipeline(
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
// function name in shader.wgsl for [[stage(vertex)]]
entry_point: "main",
// already specified in the shader
buffers: &[],
},
// needed to sotre color data to swap_chain
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "main",
// setup of color outputs
targets: &[wgpu::ColorTargetState {
format: sc_desc.format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrite::ALL,
}],
}),
primitive: wgpu::PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
// triangle facing forward when Counter Clock Wise
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
polygon_mode: wgpu::PolygonMode::Fill,
clamp_depth: false,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
// only use 1 sample => no extra sampling
count: 1,
// use all
mask: !0,
// not using anti aliasing
alpha_to_coverage_enabled: false,
}
}
);
Self {
surface,
device,
@@ -63,6 +203,9 @@ impl State {
swap_chain,
size,
clear_color: wgpu::Color { r: 0.6, g: 0.6, b: 0.1, a: 1.0 },
render_pipeline,
challenge_render_pipeline,
use_challenge_render_pipeline: false,
}
}
@@ -80,10 +223,21 @@ impl State {
pub fn input(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::CursorMoved { position, .. } => {
let x = (position.x as f64) / 1000 as f64;
let y = (position.y as f64) / 1000 as f64;
let x = (position.x as f64) / self.size.width as f64;
let y = (position.y as f64) / self.size.height as f64;
self.clear_color = wgpu::Color { r: x, g: x, b: y, a: 1.0};
true
},
WindowEvent::KeyboardInput {
input: KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Space),
..
},
..
} => {
self.use_challenge_render_pipeline = !self.use_challenge_render_pipeline;
true
}
_ => false
}
@@ -106,7 +260,7 @@ impl State {
label: Some("Render Encoder"),
});
// create a render_pass
let _render_pass = encoder.begin_render_pass(
let mut render_pass = encoder.begin_render_pass(
&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[
@@ -122,8 +276,17 @@ impl State {
depth_stencil_attachment: None,
});
// set pipeline
if self.use_challenge_render_pipeline {
render_pass.set_pipeline(&self.challenge_render_pipeline);
} else {
render_pass.set_pipeline(&self.render_pipeline);
}
// draw triangle
render_pass.draw(0..3, 0..1);
// drop so encoder isn't borrowed mutually anymore
drop(_render_pass);
drop(render_pass);
// submit finished command buffers
self.queue.submit(std::iter::once(encoder.finish()));