1
0
Fork 0

steal some stuff from sotrh/learn-wgpu/tree/cleanup-nov2020

This commit is contained in:
Adrian Hedqvist 2020-11-24 00:50:14 +01:00
parent 003338b828
commit 51e8263503
9 changed files with 605 additions and 489 deletions

784
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -8,18 +8,18 @@ edition = "2018"
[dependencies]
wgpu = "0.6.0"
winit = "0.22.2"
image = "0.23.9"
futures = "0.3.5"
bytemuck = "1.4.1"
anyhow = "1.0.33"
env_logger = "0.7.1"
log = "0.4.11"
tobj = "2.0.2"
cgmath = "0.17.0"
winit = "0.23.0"
image = "0.23"
futures = "0.3"
bytemuck = { version="1.4", features = ["derive"] }
anyhow = "1"
env_logger = "0.8"
log = "0.4"
tobj = "2"
cgmath = "0.17"
[build-dependencies]
shaderc = "0.6.2"
fs_extra = "1.2"
anyhow = "1.0.33"
glob = "0.3.0"
shaderc = "0.7"
fs_extra = "1"
anyhow = "1"
glob = "0.3"

View file

@ -41,8 +41,6 @@ impl ShaderData {
}
fn main() -> Result<()> {
println!("cargo:rerun-if-changed=src/*");
let mut shader_paths = [
glob("./src/**/*.vert")?,
glob("./src/**/*.frag")?,
@ -58,6 +56,10 @@ fn main() -> Result<()> {
let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
for shader in shaders? {
println!(
"cargo:rerun-if-changed={}",
&shader.src_path.as_os_str().to_str().unwrap()
);
let compiled = compiler.compile_into_spirv(
&shader.src,
shader.kind,

View file

@ -88,27 +88,33 @@ impl CameraController {
}
pub fn update_camera(&self, camera: &mut Camera) {
let forward = camera.target - camera.eye;
let forward_norm = forward.normalize();
let forward_mag = forward.magnitude();
let mut forward = camera.target - camera.eye;
let mut forward_norm = forward.normalize();
let mut forward_mag = forward.magnitude();
// Prevents glitching when camera gets too close to the
// center of the scene.
if self.is_forward_pressed && forward_mag > self.speed {
camera.eye += forward_norm * self.speed;
}
if self.is_backward_pressed {
camera.eye -= forward_norm * self.speed;
if self.is_forward_pressed != self.is_backward_pressed {
// Prevents glitching when camera gets too close to the
// center of the scene.
if self.is_forward_pressed && forward_mag > self.speed {
camera.eye += forward_norm * self.speed;
}
else if self.is_backward_pressed {
camera.eye -= forward_norm * self.speed;
}
forward = camera.target - camera.eye;
forward_norm = forward.normalize();
forward_mag = forward.magnitude();
}
let right = forward_norm.cross(camera.up);
if self.is_right_pressed {
camera.eye = camera.target - (forward - right * self.speed).normalize() * forward_mag;
}
if self.is_left_pressed {
camera.eye = camera.target - (forward + right * self.speed).normalize() * forward_mag;
if self.is_right_pressed != self.is_left_pressed {
if self.is_right_pressed {
camera.eye = camera.target - (forward - right * self.speed).normalize() * forward_mag;
}
else if self.is_left_pressed {
camera.eye = camera.target - (forward + right * self.speed).normalize() * forward_mag;
}
}
}
}

View file

@ -1,21 +1,18 @@
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Light {
pub position: cgmath::Vector3<f32>,
pub position: [f32; 3],
// according to the tutorial I'm following, uniforms require 16 byte padding. So that's why this thing is here.
_padding: u32,
pub color: cgmath::Vector3<f32>,
pub color: [f32; 3],
}
impl Light {
pub fn new(position: cgmath::Vector3<f32>, color: cgmath::Vector3<f32>) -> Self {
Self {
position,
position: position.into(),
_padding: 0,
color,
color: color.into(),
}
}
}
unsafe impl bytemuck::Pod for Light {}
unsafe impl bytemuck::Zeroable for Light {}

View file

@ -8,13 +8,20 @@ use winit::{
use cgmath::prelude::*;
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.0, 0.0, 0.5, 1.0,
);
mod camera;
mod light;
mod model;
mod texture;
type Vec3 = cgmath::Vector3<f32>;
type Mat4 = cgmath::Matrix4<f32>;
const NUM_INSTANCES_PER_ROW: u32 = 11;
@ -56,7 +63,15 @@ fn main() {
}
}
Event::RedrawRequested(_) => {
state.render();
match state.render() {
Ok(_) => {}
Err(wgpu::SwapChainError::Lost) => state.resize(state.size),
Err(wgpu::SwapChainError::OutOfMemory) => {
eprintln!("Recieved a swapchain OOM error, exiting...");
*control_flow = ControlFlow::Exit;
}
Err(_) => {}
};
}
Event::MainEventsCleared => {
state.update();
@ -67,29 +82,26 @@ fn main() {
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct Uniforms {
view_position: cgmath::Vector4<f32>,
view_proj: Mat4,
view_position: [f32; 4],
view_proj: [[f32; 4]; 4],
}
impl Uniforms {
fn new() -> Self {
Self {
view_position: Zero::zero(),
view_proj: cgmath::Matrix4::identity(),
view_position: [0.0; 4],
view_proj: cgmath::Matrix4::identity().into(),
}
}
fn update_view_proj(&mut self, camera: &camera::Camera) {
self.view_position = camera.eye.to_homogeneous();
self.view_proj = camera.build_view_projection_matrix();
self.view_position = camera.eye.to_homogeneous().into();
self.view_proj = (OPENGL_TO_WGPU_MATRIX * camera.build_view_projection_matrix()).into();
}
}
unsafe impl bytemuck::Pod for Uniforms {}
unsafe impl bytemuck::Zeroable for Uniforms {}
struct Instance {
position: cgmath::Vector3<f32>,
rotation: cgmath::Quaternion<f32>,
@ -98,21 +110,58 @@ struct Instance {
impl Instance {
fn to_raw(&self) -> InstanceRaw {
InstanceRaw {
model: cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation),
model: (cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation))
.into(),
}
}
}
impl model::Vertex for InstanceRaw {
fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> {
use std::mem;
wgpu::VertexBufferDescriptor {
stride: mem::size_of::<InstanceRaw>() as wgpu::BufferAddress,
// We need to switch from using a step mode of Vertex to Instance
// This means that our shaders will only change to use the next
// instance when the shader starts processing a new instance
step_mode: wgpu::InputStepMode::Instance,
attributes: &[
wgpu::VertexAttributeDescriptor {
offset: 0,
// While our vertex shader only uses locations 0, and 1 now, in later tutorials we'll
// be using 2, 3, and 4, for Vertex. We'll start at slot 5 not conflict with them later
shader_location: 5,
format: wgpu::VertexFormat::Float4,
},
// A mat4 takes up 4 vertex slots as it is technically 4 vec4s. We need to define a slot
// for each vec4. We don't have to do this in code though.
wgpu::VertexAttributeDescriptor {
offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
shader_location: 6,
format: wgpu::VertexFormat::Float4,
},
wgpu::VertexAttributeDescriptor {
offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress,
shader_location: 7,
format: wgpu::VertexFormat::Float4,
},
wgpu::VertexAttributeDescriptor {
offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
shader_location: 8,
format: wgpu::VertexFormat::Float4,
},
],
}
}
}
#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct InstanceRaw {
model: Mat4,
model: [[f32; 4]; 4],
}
unsafe impl bytemuck::Pod for InstanceRaw {}
unsafe impl bytemuck::Zeroable for InstanceRaw {}
struct State {
_instance: wgpu::Instance,
surface: wgpu::Surface,
@ -133,7 +182,7 @@ struct State {
obj_model: Model,
instances: Vec<Instance>,
_instance_buffer: wgpu::Buffer,
instance_buffer: wgpu::Buffer,
light: light::Light,
light_buffer: wgpu::Buffer,
@ -238,9 +287,9 @@ impl State {
(0..NUM_INSTANCES_PER_ROW).map(move |x| {
let x = SPACE_BETWEEN * (x as f32 - NUM_INSTANCES_PER_ROW as f32 / 2.0);
let z = SPACE_BETWEEN * (z as f32 - NUM_INSTANCES_PER_ROW as f32 / 2.0);
let position: Vec3 = (x, 0.0, z).into();
let position = Vec3 {x, y: 0.0, z};
let rotation = if Vec3::zero() == position {
let rotation = if position.is_zero() {
cgmath::Quaternion::from_axis_angle(Vec3::unit_z(), cgmath::Deg(0.0))
} else {
cgmath::Quaternion::from_axis_angle(position.normalize(), cgmath::Deg(45.0))
@ -254,13 +303,13 @@ impl State {
let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("instance_buffer"),
contents: bytemuck::cast_slice(&instance_data),
usage: wgpu::BufferUsage::STORAGE,
usage: wgpu::BufferUsage::VERTEX,
});
let camera = camera::Camera {
eye: (0.0, 1.0, 2.0).into(),
eye: (0.0, 5.0, -10.0).into(),
target: (0.0, 0.0, 0.0).into(),
up: (0.0, 1.0, 0.0).into(),
up: cgmath::Vector3::unit_y(),
aspect: sc_desc.width as f32 / sc_desc.height as f32,
fovy: 45.0,
znear: 0.1,
@ -281,42 +330,24 @@ impl State {
let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("uniform_bind_group_layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer {
dynamic: false,
min_binding_size: None,
},
count: None,
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer {
dynamic: false,
min_binding_size: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::StorageBuffer {
dynamic: false,
min_binding_size: None,
readonly: true,
},
count: None,
},
],
count: None,
}],
});
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("uniform_bind_group"),
layout: &uniform_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)),
},
],
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
}],
});
// Lights
@ -367,7 +398,7 @@ impl State {
&layout,
sc_desc.format,
Some(texture::Texture::DEPTH_FORMAT),
&[model::ModelVertex::desc()],
&[model::ModelVertex::desc(), InstanceRaw::desc()],
wgpu::include_spirv!("shader.vert.spv"),
wgpu::include_spirv!("shader.frag.spv"),
)
@ -414,7 +445,7 @@ impl State {
light_bind_group,
light_render_pipeline,
instances,
_instance_buffer: instance_buffer,
instance_buffer,
render_pipeline,
size,
}
@ -460,20 +491,19 @@ impl State {
std::mem::size_of::<Uniforms>() as wgpu::BufferAddress,
);
let old_pos: cgmath::Vector3<f32> = self.light.position.into();
self.light.position =
cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0))
* self.light.position;
(cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0))
* old_pos)
.into();
self.queue
.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light]));
self.queue.submit(Some(encoder.finish()));
}
fn render(&mut self) {
let frame = self
.swap_chain
.get_current_frame()
.expect("Failed getting frame");
fn render(&mut self) -> Result<(), wgpu::SwapChainError> {
let frame = self.swap_chain.get_current_frame()?;
let mut encoder = self
.device
@ -505,6 +535,7 @@ impl State {
}),
});
render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..));
render_pass.set_pipeline(&self.light_render_pipeline);
render_pass.draw_light_model_instanced(
&self.obj_model,
@ -524,6 +555,8 @@ impl State {
drop(render_pass);
self.queue.submit(Some(encoder.finish()));
Ok(())
}
}

View file

@ -9,13 +9,13 @@ pub trait Vertex {
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct ModelVertex {
position: cgmath::Vector3<f32>,
tex_coords: cgmath::Vector2<f32>,
normal: cgmath::Vector3<f32>,
tangent: cgmath::Vector3<f32>,
bitangent: cgmath::Vector3<f32>,
position: [f32; 3],
tex_coords: [f32; 2],
normal: [f32; 3],
tangent: [f32; 3],
bitangent: [f32; 3],
}
impl Vertex for ModelVertex {
@ -56,9 +56,6 @@ impl Vertex for ModelVertex {
}
}
unsafe impl bytemuck::Pod for ModelVertex {}
unsafe impl bytemuck::Zeroable for ModelVertex {}
pub struct Material {
pub name: String,
pub diffuse_texture: texture::Texture,
@ -162,21 +159,19 @@ impl Model {
let mut vertices = Vec::with_capacity(m.mesh.positions.len() / 3);
for i in 0..m.mesh.positions.len() / 3 {
vertices.push(ModelVertex {
position: (
position: [
m.mesh.positions[i * 3],
m.mesh.positions[i * 3 + 1],
m.mesh.positions[i * 3 + 2],
)
.into(),
tex_coords: (m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]).into(),
normal: (
],
tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]],
normal: [
m.mesh.normals[i * 3],
m.mesh.normals[i * 3 + 1],
m.mesh.normals[i * 3 + 2],
)
.into(),
tangent: [0.0; 3].into(),
bitangent: [0.0; 3].into(),
],
tangent: [0.0; 3],
bitangent: [0.0; 3],
});
}
@ -185,23 +180,31 @@ impl Model {
let v1 = vertices[c[1] as usize];
let v2 = vertices[c[2] as usize];
let delta_pos1 = v1.position - v0.position;
let delta_pos2 = v2.position - v0.position;
let pos0: cgmath::Vector3<_> = v0.position.into();
let pos1: cgmath::Vector3<_> = v1.position.into();
let pos2: cgmath::Vector3<_> = v2.position.into();
let delta_uv1 = v1.tex_coords - v0.tex_coords;
let delta_uv2 = v2.tex_coords - v0.tex_coords;
let uv0: cgmath::Vector2<_> = v0.tex_coords.into();
let uv1: cgmath::Vector2<_> = v1.tex_coords.into();
let uv2: cgmath::Vector2<_> = v2.tex_coords.into();
let delta_pos1 = pos1 - pos0;
let delta_pos2 = pos2 - pos0;
let delta_uv1 = uv1 - uv0;
let delta_uv2 = uv2 - uv0;
let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r;
let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r;
vertices[c[0] as usize].tangent = tangent;
vertices[c[1] as usize].tangent = tangent;
vertices[c[2] as usize].tangent = tangent;
vertices[c[0] as usize].tangent = tangent.into();
vertices[c[1] as usize].tangent = tangent.into();
vertices[c[2] as usize].tangent = tangent.into();
vertices[c[0] as usize].bitangent = bitangent;
vertices[c[1] as usize].bitangent = bitangent;
vertices[c[2] as usize].bitangent = bitangent;
vertices[c[0] as usize].bitangent = bitangent.into();
vertices[c[1] as usize].bitangent = bitangent.into();
vertices[c[2] as usize].bitangent = bitangent.into();
}
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {

View file

@ -5,6 +5,7 @@ layout(location=1) in vec2 a_tex_coords;
layout(location=2) in vec3 a_normal;
layout(location=3) in vec3 a_tangent;
layout(location=4) in vec3 a_bitangent;
layout(location=5) in mat4 model_matrix;
layout(location=0) out vec2 v_tex_coords;
layout(location=1) out vec3 v_position;
@ -16,9 +17,6 @@ layout(set=1, binding=0) uniform Uniforms {
mat4 u_view_proj; // TODO: pass in view and projection matrix seperately
};
layout(set=1, binding=1) buffer Instances {
mat4 s_models[];
};
layout(set=2, binding=0) uniform Light {
vec3 light_position;
@ -27,7 +25,6 @@ layout(set=2, binding=0) uniform Light {
void main() {
v_tex_coords = a_tex_coords;
mat4 model_matrix = s_models[gl_InstanceIndex];
mat3 normal_matrix = mat3(transpose(inverse(model_matrix))); // TODO: calculate this on CPU and pass in as an uniform
vec3 normal = normalize(normal_matrix * a_normal);

View file

@ -28,7 +28,7 @@ impl Texture {
is_normal_map: bool,
label: Option<&str>,
) -> Result<Self> {
let rgba = img.to_rgba();
let rgba = img.to_rgba8();
let (width, height) = img.dimensions();
let size = wgpu::Extent3d {