diff --git a/res/shaders/fog.wgsl b/res/shaders/fog.wgsl new file mode 100644 index 0000000..303edd1 --- /dev/null +++ b/res/shaders/fog.wgsl @@ -0,0 +1,46 @@ +#include globals.wgsl + +@vertex +fn vs_main( + model: VertexInput, + instance: InstanceInput, +) -> VertexOutput { + let model_matrix = mat4x4( + instance.model_matrix_0, + instance.model_matrix_1, + instance.model_matrix_2, + instance.model_matrix_3, + ); + let normal_matrix = mat3x3( + instance.normal_matrix_0, + instance.normal_matrix_1, + instance.normal_matrix_2, + ); + + let world_normal = normalize(normal_matrix * model.normal); + let world_tangent = normalize(normal_matrix * model.tangent); + let world_bitangent = normalize(normal_matrix * model.bitangent); + let tangent_matrix = transpose(mat3x3( + world_tangent, + world_bitangent, + world_normal, + )); + + let world_position = model_matrix * vec4(model.position, 1.0); + + var out: VertexOutput; + out.clip_position = camera.proj * camera.view * world_position; + out.tex_coords = model.tex_coords; + out.tangent_position = tangent_matrix * world_position.xyz; + out.tangent_light_position = tangent_matrix * light.position; + out.tangent_view_position = tangent_matrix * camera.position.xyz; + + out.world_position = world_position; + + return out; +} + +@fragment +fn fs_main(vert: VertexOutput) -> @location(0) vec4 { + return vec4(0.5, 0.5, 0.5, 0.1); +} diff --git a/src/core/instance.rs b/src/core/instance.rs index cee89b5..fc7f6af 100644 --- a/src/core/instance.rs +++ b/src/core/instance.rs @@ -3,6 +3,7 @@ use super::model::Vertex; pub struct Instance { pub position: cgmath::Vector3, pub rotation: cgmath::Quaternion, + pub scale: cgmath::Vector3, } #[repr(C)] @@ -16,6 +17,7 @@ impl Instance { pub fn to_raw(&self) -> InstanceRaw { InstanceRaw { model: (cgmath::Matrix4::from_translation(self.position) + * cgmath::Matrix4::from_nonuniform_scale(self.scale.x, self.scale.y, self.scale.z) * cgmath::Matrix4::from(self.rotation)) .into(), normal: cgmath::Matrix3::from(self.rotation).into(), diff --git a/src/core/mesh.rs b/src/core/mesh.rs index 60687ab..bccc1ae 100644 --- a/src/core/mesh.rs +++ b/src/core/mesh.rs @@ -155,4 +155,63 @@ impl Mesh { v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into(); } } + + /* + pub fn cube(device: &wgpu::Device, size: [f32; 3], name: &str, material_index: usize) -> Mesh { + #[rustfmt::skip] + let mut vertices = vec![ + // front + ModelVertex { position: [-size[0], -size[1], -size[2]], tex_coords: [0.0, 0.0], normal: [-size[0], -size[1], -size[2]], ..Default::default() }, + ModelVertex { position: [ size[0], -size[1], -size[2]], tex_coords: [0.0, 0.0], normal: [ size[0], -size[1], -size[2]], ..Default::default() }, + ModelVertex { position: [ size[0], size[1], -size[2]], tex_coords: [0.0, 0.0], normal: [ size[0], size[1], -size[2]], ..Default::default() }, + ModelVertex { position: [-size[0], size[1], -size[2]], tex_coords: [0.0, 0.0], normal: [-size[0], size[1], -size[2]], ..Default::default() }, + + // back + ModelVertex { position: [-size[0], -size[1], size[2]], tex_coords: [0.0, 0.0], normal: [-size[0], -size[1], size[2]], ..Default::default() }, + ModelVertex { position: [ size[0], -size[1], size[2]], tex_coords: [0.0, 0.0], normal: [ size[0], -size[1], size[2]], ..Default::default() }, + ModelVertex { position: [ size[0], size[1], size[2]], tex_coords: [0.0, 0.0], normal: [ size[0], size[1], size[2]], ..Default::default() }, + ModelVertex { position: [-size[0], size[1], size[2]], tex_coords: [0.0, 0.0], normal: [-size[0], size[1], size[2]], ..Default::default() }, + ]; + #[rustfmt::skip] + let indices = vec![ + // front + 0, 1, 2, + 2, 3, 0, + // back + 4, 6, 5, + 4, 7, 6, + // left + 0, 3, 4, + 3, 7, 4, + // right + 1, 2, 5, + 2, 6, 5, + // top + 2, 7, 3, + 2, 6, 7, + // bottom + 1, 4, 0, + 1, 5, 4 + ]; + Mesh::calc_tangents(&indices, &mut vertices); + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&format!("{:?} Vertex Buffer", name)), + contents: bytemuck::cast_slice(&vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&format!("{:?} Index Buffer", name)), + contents: bytemuck::cast_slice(&indices), + usage: wgpu::BufferUsages::INDEX, + }); + + Mesh { + name: name.to_string(), + vertex_buffer, + index_buffer, + num_elements: indices.len() as u32, + material: material_index, + } + } + */ } \ No newline at end of file diff --git a/src/core/pass.rs b/src/core/pass.rs index 0224b4f..c176737 100644 --- a/src/core/pass.rs +++ b/src/core/pass.rs @@ -19,6 +19,7 @@ impl RenderPass { vertex_layouts: &[VertexBufferLayout], label: &str, is_shadow: bool, + has_transparency: bool, ) -> Self { let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some((label.to_owned() + " pipeline Layout").as_str()), @@ -38,6 +39,7 @@ impl RenderPass { shader, label, is_shadow, + has_transparency, ); Self { pipeline } @@ -52,14 +54,25 @@ impl RenderPass { shader: wgpu::ShaderModuleDescriptor, label: &str, is_shadow: bool, + has_transparency: bool, ) -> wgpu::RenderPipeline { let shader = device.create_shader_module(shader); + let blend_comp = if has_transparency { + wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + } + } else { + wgpu::BlendComponent::REPLACE + }; + let fragment_targets = &[Some(wgpu::ColorTargetState { format: color_format.unwrap_or(wgpu::TextureFormat::Bgra8Unorm), blend: Some(wgpu::BlendState { - alpha: wgpu::BlendComponent::REPLACE, - color: wgpu::BlendComponent::REPLACE, + alpha: blend_comp, + color: blend_comp, }), write_mask: wgpu::ColorWrites::ALL, })]; @@ -104,8 +117,7 @@ impl RenderPass { slope_scale: 2.0, clamp: 0.0, } - } - else { wgpu::DepthBiasState::default() }, + } else { wgpu::DepthBiasState::default() }, }), multisample: wgpu::MultisampleState { count: 1, diff --git a/src/core/state.rs b/src/core/state.rs index a6847b2..a8d4f08 100644 --- a/src/core/state.rs +++ b/src/core/state.rs @@ -35,15 +35,19 @@ pub struct State { queue: wgpu::Queue, config: wgpu::SurfaceConfiguration, geometry_pass: RenderPass, + fog_pass: RenderPass, camera: Camera, camera_uniform: CameraUniform, camera_buffer: wgpu::Buffer, camera_bind_group: wgpu::BindGroup, camera_controller: CameraController, - instances: Vec, - instance_buffer: wgpu::Buffer, + geom_instances: Vec, + geom_instance_buffer: wgpu::Buffer, + fog_instances: Vec, + fog_instance_buffer: wgpu::Buffer, depth_texture: Texture, - model: Model, + geom_model: Model, + fog_model: Model, light_model: Model, light_uniform: LightUniform, light_buffer: wgpu::Buffer, @@ -350,7 +354,7 @@ impl State { label: Some("texture_bind_group_layout"), }); - let model = resources::load_model_gltf( + let geom_model = resources::load_model_gltf( "models/Sponza.glb", &device, &queue, @@ -359,6 +363,15 @@ impl State { .await .unwrap(); + let fog_model = resources::load_model_gltf( + "models/Cube.glb", + &device, + &queue, + &texture_bind_group_layout, + ) + .await + .unwrap(); + let light_model = resources::load_model_gltf( "models/Cube.glb", &device, @@ -368,15 +381,28 @@ impl State { .await .unwrap(); - let instances = vec![Instance { - position: [0.0, 0.0, 0.0].into(), + let geom_instances = vec![Instance { + // this sponza model isn't quite centered + position: [60.0, 0.0, 35.0].into(), rotation: cgmath::Quaternion::one(), + scale: [1.0, 1.0, 1.0].into(), }]; + let geom_instance_data = geom_instances.iter().map(Instance::to_raw).collect::>(); + let geom_instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Geometry Instance Buffer"), + contents: bytemuck::cast_slice(&geom_instance_data), + usage: wgpu::BufferUsages::VERTEX, + }); - let instance_data = instances.iter().map(Instance::to_raw).collect::>(); - let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Instance Buffer"), - contents: bytemuck::cast_slice(&instance_data), + let fog_instances = vec![Instance { + position: [0.0, 20.0, 0.0].into(), + rotation: cgmath::Quaternion::one(), + scale: [1360.0, 20.0, 600.0].into(), + }]; + let fog_instance_data = fog_instances.iter().map(Instance::to_raw).collect::>(); + let fog_instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Fog Instance Buffer"), + contents: bytemuck::cast_slice(&fog_instance_data), usage: wgpu::BufferUsages::VERTEX, }); @@ -393,6 +419,7 @@ impl State { &[ModelVertex::desc(), InstanceRaw::desc()], "light depth pass", true, + false, ); let geometry_pass = RenderPass::new( @@ -410,6 +437,25 @@ impl State { &[ModelVertex::desc(), InstanceRaw::desc()], "geometry pass", false, + false, + ); + + let fog_pass = RenderPass::new( + &device, + &[ + &camera_bind_group_layout, + &light_bind_group_layout, + &light_depth_bind_group_layout, + &texture_bind_group_layout, + ], + &[], + "fog.wgsl", + Some(config.format), + Some(Texture::DEPTH_FORMAT), + &[ModelVertex::desc(), InstanceRaw::desc()], + "fog pass", + false, + true, ); let light_debug_pass = RenderPass::new( @@ -422,6 +468,7 @@ impl State { &[ModelVertex::desc()], "light debug pass", false, + false, ); Self { @@ -431,15 +478,19 @@ impl State { queue, config, geometry_pass, + fog_pass, camera, camera_uniform, camera_buffer, camera_bind_group, camera_controller, - instances, - instance_buffer, + geom_instances, + geom_instance_buffer, + fog_instances, + fog_instance_buffer, depth_texture, - model, + geom_model, + fog_model, light_model, light_uniform, light_buffer, @@ -546,11 +597,11 @@ impl State { occlusion_query_set: None, }); - light_depth_render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..)); + light_depth_render_pass.set_vertex_buffer(1, self.geom_instance_buffer.slice(..)); light_depth_render_pass.set_pipeline(&self.light_depth_pass.pipeline); light_depth_render_pass.draw_model_instanced( - &self.model, - 0..self.instances.len() as u32, + &self.geom_model, + 0..self.geom_instances.len() as u32, [&self.camera_bind_group, &self.light_bind_group].into(), ); } @@ -598,11 +649,45 @@ impl State { occlusion_query_set: None, }); - geom_render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..)); + geom_render_pass.set_vertex_buffer(1, self.geom_instance_buffer.slice(..)); geom_render_pass.set_pipeline(&self.geometry_pass.pipeline); geom_render_pass.draw_model_instanced( - &self.model, - 0..self.instances.len() as u32, + &self.geom_model, + 0..self.geom_instances.len() as u32, + [&self.camera_bind_group, &self.light_bind_group, &self.light_depth_bind_group].into(), + ); + } + encoder.pop_debug_group(); + + encoder.push_debug_group("fog pass"); + { + let mut fog_render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Fog Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &surface_view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: StoreOp::Store, + }, + })], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &self.depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: StoreOp::Store, + }), + stencil_ops: None, + }), + timestamp_writes: None, + occlusion_query_set: None, + }); + + fog_render_pass.set_vertex_buffer(1, self.fog_instance_buffer.slice(..)); + fog_render_pass.set_pipeline(&self.fog_pass.pipeline); + fog_render_pass.draw_model_instanced( + &self.fog_model, + 0..self.fog_instances.len() as u32, [&self.camera_bind_group, &self.light_bind_group, &self.light_depth_bind_group].into(), ); }