use std::{iter, mem, num::NonZeroU32, ops::Range, rc::Rc}; #[path = "../framework.rs"] mod framework; use bytemuck::{Pod, Zeroable}; use wgpu::util::DeviceExt; #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct Vertex { _pos: [i8; 4], _normal: [i8; 4], } fn vertex(pos: [i8; 3], nor: [i8; 3]) -> Vertex { Vertex { _pos: [pos[0], pos[1], pos[2], 1], _normal: [nor[0], nor[1], nor[2], 0], } } fn create_cube() -> (Vec, Vec) { let vertex_data = [ // top (0, 0, 1) vertex([-1, -1, 1], [0, 0, 1]), vertex([1, -1, 1], [0, 0, 1]), vertex([1, 1, 1], [0, 0, 1]), vertex([-1, 1, 1], [0, 0, 1]), // bottom (0, 0, -1) vertex([-1, 1, -1], [0, 0, -1]), vertex([1, 1, -1], [0, 0, -1]), vertex([1, -1, -1], [0, 0, -1]), vertex([-1, -1, -1], [0, 0, -1]), // right (1, 0, 0) vertex([1, -1, -1], [1, 0, 0]), vertex([1, 1, -1], [1, 0, 0]), vertex([1, 1, 1], [1, 0, 0]), vertex([1, -1, 1], [1, 0, 0]), // left (-1, 0, 0) vertex([-1, -1, 1], [-1, 0, 0]), vertex([-1, 1, 1], [-1, 0, 0]), vertex([-1, 1, -1], [-1, 0, 0]), vertex([-1, -1, -1], [-1, 0, 0]), // front (0, 1, 0) vertex([1, 1, -1], [0, 1, 0]), vertex([-1, 1, -1], [0, 1, 0]), vertex([-1, 1, 1], [0, 1, 0]), vertex([1, 1, 1], [0, 1, 0]), // back (0, -1, 0) vertex([1, -1, 1], [0, -1, 0]), vertex([-1, -1, 1], [0, -1, 0]), vertex([-1, -1, -1], [0, -1, 0]), vertex([1, -1, -1], [0, -1, 0]), ]; let index_data: &[u16] = &[ 0, 1, 2, 2, 3, 0, // top 4, 5, 6, 6, 7, 4, // bottom 8, 9, 10, 10, 11, 8, // right 12, 13, 14, 14, 15, 12, // left 16, 17, 18, 18, 19, 16, // front 20, 21, 22, 22, 23, 20, // back ]; (vertex_data.to_vec(), index_data.to_vec()) } fn create_plane(size: i8) -> (Vec, Vec) { let vertex_data = [ vertex([size, -size, 0], [0, 0, 1]), vertex([size, size, 0], [0, 0, 1]), vertex([-size, -size, 0], [0, 0, 1]), vertex([-size, size, 0], [0, 0, 1]), ]; let index_data: &[u16] = &[0, 1, 2, 2, 1, 3]; (vertex_data.to_vec(), index_data.to_vec()) } struct Entity { mx_world: cgmath::Matrix4, rotation_speed: f32, color: wgpu::Color, vertex_buf: Rc, index_buf: Rc, index_format: wgpu::IndexFormat, index_count: usize, uniform_offset: wgpu::DynamicOffset, } struct Light { pos: cgmath::Point3, color: wgpu::Color, fov: f32, depth: Range, target_view: wgpu::TextureView, } #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct LightRaw { proj: [[f32; 4]; 4], pos: [f32; 4], color: [f32; 4], } impl Light { fn to_raw(&self) -> LightRaw { use cgmath::{Deg, EuclideanSpace, Matrix4, PerspectiveFov, Point3, Vector3}; let mx_view = Matrix4::look_at(self.pos, Point3::origin(), Vector3::unit_z()); let projection = PerspectiveFov { fovy: Deg(self.fov).into(), aspect: 1.0, near: self.depth.start, far: self.depth.end, }; let mx_correction = framework::OPENGL_TO_WGPU_MATRIX; let mx_view_proj = mx_correction * cgmath::Matrix4::from(projection.to_perspective()) * mx_view; LightRaw { proj: *mx_view_proj.as_ref(), pos: [self.pos.x, self.pos.y, self.pos.z, 1.0], color: [ self.color.r as f32, self.color.g as f32, self.color.b as f32, 1.0, ], } } } #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct ForwardUniforms { proj: [[f32; 4]; 4], num_lights: [u32; 4], } #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct EntityUniforms { model: [[f32; 4]; 4], color: [f32; 4], } #[repr(C)] struct ShadowUniforms { proj: [[f32; 4]; 4], } struct Pass { pipeline: wgpu::RenderPipeline, bind_group: wgpu::BindGroup, uniform_buf: wgpu::Buffer, } struct Example { entities: Vec, lights: Vec, lights_are_dirty: bool, shadow_pass: Pass, forward_pass: Pass, forward_depth: wgpu::TextureView, entity_bind_group: wgpu::BindGroup, light_uniform_buf: wgpu::Buffer, entity_uniform_buf: wgpu::Buffer, } impl Example { const MAX_LIGHTS: usize = 10; const SHADOW_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; const SHADOW_SIZE: wgpu::Extent3d = wgpu::Extent3d { width: 512, height: 512, depth: Self::MAX_LIGHTS as u32, }; const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; fn generate_matrix(aspect_ratio: f32) -> cgmath::Matrix4 { let mx_projection = cgmath::perspective(cgmath::Deg(45f32), aspect_ratio, 1.0, 20.0); let mx_view = cgmath::Matrix4::look_at( cgmath::Point3::new(3.0f32, -10.0, 6.0), cgmath::Point3::new(0f32, 0.0, 0.0), cgmath::Vector3::unit_z(), ); let mx_correction = framework::OPENGL_TO_WGPU_MATRIX; mx_correction * mx_projection * mx_view } } impl framework::Example for Example { fn optional_features() -> wgpu::Features { wgpu::Features::DEPTH_CLAMPING } fn init( sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, _queue: &wgpu::Queue, ) -> Self { // Create the vertex and index buffers let vertex_size = mem::size_of::(); let (cube_vertex_data, cube_index_data) = create_cube(); let cube_vertex_buf = Rc::new(device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Cubes Vertex Buffer"), contents: bytemuck::cast_slice(&cube_vertex_data), usage: wgpu::BufferUsage::VERTEX, }, )); let cube_index_buf = Rc::new(device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Cubes Index Buffer"), contents: bytemuck::cast_slice(&cube_index_data), usage: wgpu::BufferUsage::INDEX, }, )); let (plane_vertex_data, plane_index_data) = create_plane(7); let plane_vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Plane Vertex Buffer"), contents: bytemuck::cast_slice(&plane_vertex_data), usage: wgpu::BufferUsage::VERTEX, }); let plane_index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Plane Index Buffer"), contents: bytemuck::cast_slice(&plane_index_data), usage: wgpu::BufferUsage::INDEX, }); struct CubeDesc { offset: cgmath::Vector3, angle: f32, scale: f32, rotation: f32, } let cube_descs = [ CubeDesc { offset: cgmath::vec3(-2.0, -2.0, 2.0), angle: 10.0, scale: 0.7, rotation: 0.1, }, CubeDesc { offset: cgmath::vec3(2.0, -2.0, 2.0), angle: 50.0, scale: 1.3, rotation: 0.2, }, CubeDesc { offset: cgmath::vec3(-2.0, 2.0, 2.0), angle: 140.0, scale: 1.1, rotation: 0.3, }, CubeDesc { offset: cgmath::vec3(2.0, 2.0, 2.0), angle: 210.0, scale: 0.9, rotation: 0.4, }, ]; let entity_uniform_size = mem::size_of::() as wgpu::BufferAddress; let num_entities = 1 + cube_descs.len() as wgpu::BufferAddress; assert!(entity_uniform_size <= wgpu::BIND_BUFFER_ALIGNMENT); //Note: dynamic offsets also have to be aligned to `BIND_BUFFER_ALIGNMENT`. let entity_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { label: None, size: num_entities * wgpu::BIND_BUFFER_ALIGNMENT, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, mapped_at_creation: false, }); let index_format = wgpu::IndexFormat::Uint16; let mut entities = vec![{ use cgmath::SquareMatrix; Entity { mx_world: cgmath::Matrix4::identity(), rotation_speed: 0.0, color: wgpu::Color::WHITE, vertex_buf: Rc::new(plane_vertex_buf), index_buf: Rc::new(plane_index_buf), index_format, index_count: plane_index_data.len(), uniform_offset: 0, } }]; for (i, cube) in cube_descs.iter().enumerate() { use cgmath::{Decomposed, Deg, InnerSpace, Quaternion, Rotation3}; let transform = Decomposed { disp: cube.offset.clone(), rot: Quaternion::from_axis_angle(cube.offset.normalize(), Deg(cube.angle)), scale: cube.scale, }; entities.push(Entity { mx_world: cgmath::Matrix4::from(transform), rotation_speed: cube.rotation, color: wgpu::Color::GREEN, vertex_buf: Rc::clone(&cube_vertex_buf), index_buf: Rc::clone(&cube_index_buf), index_format, index_count: cube_index_data.len(), uniform_offset: ((i + 1) * wgpu::BIND_BUFFER_ALIGNMENT as usize) as _, }); } let local_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: true, min_binding_size: wgpu::BufferSize::new(entity_uniform_size), }, count: None, }], label: None, }); let entity_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &local_bind_group_layout, entries: &[wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::Buffer { buffer: &entity_uniform_buf, offset: 0, size: wgpu::BufferSize::new(entity_uniform_size), }, }], label: None, }); // Create other resources let shadow_sampler = device.create_sampler(&wgpu::SamplerDescriptor { label: Some("shadow"), address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Nearest, compare: Some(wgpu::CompareFunction::LessEqual), ..Default::default() }); let shadow_texture = device.create_texture(&wgpu::TextureDescriptor { size: Self::SHADOW_SIZE, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: Self::SHADOW_FORMAT, usage: wgpu::TextureUsage::RENDER_ATTACHMENT | wgpu::TextureUsage::SAMPLED, label: None, }); let shadow_view = shadow_texture.create_view(&wgpu::TextureViewDescriptor::default()); let mut shadow_target_views = (0..2) .map(|i| { Some(shadow_texture.create_view(&wgpu::TextureViewDescriptor { label: Some("shadow"), format: None, dimension: Some(wgpu::TextureViewDimension::D2), aspect: wgpu::TextureAspect::All, base_mip_level: 0, level_count: None, base_array_layer: i as u32, array_layer_count: NonZeroU32::new(1), })) }) .collect::>(); let lights = vec![ Light { pos: cgmath::Point3::new(7.0, -5.0, 10.0), color: wgpu::Color { r: 0.5, g: 1.0, b: 0.5, a: 1.0, }, fov: 60.0, depth: 1.0..20.0, target_view: shadow_target_views[0].take().unwrap(), }, Light { pos: cgmath::Point3::new(-5.0, 7.0, 10.0), color: wgpu::Color { r: 1.0, g: 0.5, b: 0.5, a: 1.0, }, fov: 45.0, depth: 1.0..20.0, target_view: shadow_target_views[1].take().unwrap(), }, ]; let light_uniform_size = (Self::MAX_LIGHTS * mem::size_of::()) as wgpu::BufferAddress; let light_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { label: None, size: light_uniform_size, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_SRC | wgpu::BufferUsage::COPY_DST, mapped_at_creation: false, }); let vertex_attr = wgpu::vertex_attr_array![0 => Char4, 1 => Char4]; let vb_desc = wgpu::VertexBufferDescriptor { stride: vertex_size as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Vertex, attributes: &vertex_attr, }; let shadow_pass = { let uniform_size = mem::size_of::() as wgpu::BufferAddress; // Create pipeline layout let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, entries: &[wgpu::BindGroupLayoutEntry { binding: 0, // global visibility: wgpu::ShaderStage::VERTEX, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: wgpu::BufferSize::new(uniform_size), }, count: None, }], }); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("shadow"), bind_group_layouts: &[&bind_group_layout, &local_bind_group_layout], push_constant_ranges: &[], }); let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { label: None, size: uniform_size, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, mapped_at_creation: false, }); // Create bind group let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, entries: &[wgpu::BindGroupEntry { binding: 0, resource: uniform_buf.as_entire_binding(), }], label: None, }); // Create the render pipeline let vs_module = device.create_shader_module(&wgpu::include_spirv!("bake.vert.spv")); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("shadow"), layout: Some(&pipeline_layout), vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: None, rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Ccw, cull_mode: wgpu::CullMode::Back, polygon_mode: wgpu::PolygonMode::Fill, depth_bias: 2, // corresponds to bilinear filtering depth_bias_slope_scale: 2.0, depth_bias_clamp: 0.0, clamp_depth: device.features().contains(wgpu::Features::DEPTH_CLAMPING), }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[], depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { format: Self::SHADOW_FORMAT, depth_write_enabled: true, depth_compare: wgpu::CompareFunction::LessEqual, stencil: wgpu::StencilStateDescriptor::default(), }), vertex_state: wgpu::VertexStateDescriptor { index_format: Some(index_format), vertex_buffers: &[vb_desc.clone()], }, sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Pass { pipeline, bind_group, uniform_buf, } }; let forward_pass = { // Create pipeline layout let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[ wgpu::BindGroupLayoutEntry { binding: 0, // global visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: wgpu::BufferSize::new(mem::size_of::< ForwardUniforms, >( ) as _), }, count: None, }, wgpu::BindGroupLayoutEntry { binding: 1, // lights visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: wgpu::BufferSize::new(light_uniform_size), }, count: None, }, wgpu::BindGroupLayoutEntry { binding: 2, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Texture { multisampled: false, sample_type: wgpu::TextureSampleType::Depth, view_dimension: wgpu::TextureViewDimension::D2Array, }, count: None, }, wgpu::BindGroupLayoutEntry { binding: 3, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler { comparison: true, filtering: false, }, count: None, }, ], label: None, }); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("main"), bind_group_layouts: &[&bind_group_layout, &local_bind_group_layout], push_constant_ranges: &[], }); let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); let forward_uniforms = ForwardUniforms { proj: *mx_total.as_ref(), num_lights: [lights.len() as u32, 0, 0, 0], }; let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Uniform Buffer"), contents: bytemuck::bytes_of(&forward_uniforms), usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, }); // Create bind group let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, entries: &[ wgpu::BindGroupEntry { binding: 0, resource: uniform_buf.as_entire_binding(), }, wgpu::BindGroupEntry { binding: 1, resource: light_uniform_buf.as_entire_binding(), }, wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(&shadow_view), }, wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::Sampler(&shadow_sampler), }, ], label: None, }); // Create the render pipeline let vs_module = device.create_shader_module(&wgpu::include_spirv!("forward.vert.spv")); let fs_module = device.create_shader_module(&wgpu::include_spirv!("forward.frag.spv")); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("main"), layout: Some(&pipeline_layout), vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Ccw, cull_mode: wgpu::CullMode::Back, ..Default::default() }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[sc_desc.format.into()], depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { format: Self::DEPTH_FORMAT, depth_write_enabled: true, depth_compare: wgpu::CompareFunction::Less, stencil: wgpu::StencilStateDescriptor::default(), }), vertex_state: wgpu::VertexStateDescriptor { index_format: Some(index_format), vertex_buffers: &[vb_desc], }, sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Pass { pipeline, bind_group, uniform_buf, } }; let depth_texture = device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: sc_desc.width, height: sc_desc.height, depth: 1, }, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: Self::DEPTH_FORMAT, usage: wgpu::TextureUsage::RENDER_ATTACHMENT, label: None, }); Example { entities, lights, lights_are_dirty: true, shadow_pass, forward_pass, forward_depth: depth_texture.create_view(&wgpu::TextureViewDescriptor::default()), light_uniform_buf, entity_uniform_buf, entity_bind_group, } } fn update(&mut self, _event: winit::event::WindowEvent) { //empty } fn resize( &mut self, sc_desc: &wgpu::SwapChainDescriptor, device: &wgpu::Device, queue: &wgpu::Queue, ) { // update view-projection matrix let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); let mx_ref: &[f32; 16] = mx_total.as_ref(); queue.write_buffer( &self.forward_pass.uniform_buf, 0, bytemuck::cast_slice(mx_ref), ); let depth_texture = device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: sc_desc.width, height: sc_desc.height, depth: 1, }, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: Self::DEPTH_FORMAT, usage: wgpu::TextureUsage::RENDER_ATTACHMENT, label: None, }); self.forward_depth = depth_texture.create_view(&wgpu::TextureViewDescriptor::default()); } fn render( &mut self, frame: &wgpu::SwapChainTexture, device: &wgpu::Device, queue: &wgpu::Queue, _spawner: &impl futures::task::LocalSpawn, ) { // update uniforms for entity in self.entities.iter_mut() { if entity.rotation_speed != 0.0 { let rotation = cgmath::Matrix4::from_angle_x(cgmath::Deg(entity.rotation_speed)); entity.mx_world = entity.mx_world * rotation; } let data = EntityUniforms { model: entity.mx_world.into(), color: [ entity.color.r as f32, entity.color.g as f32, entity.color.b as f32, entity.color.a as f32, ], }; queue.write_buffer( &self.entity_uniform_buf, entity.uniform_offset as wgpu::BufferAddress, bytemuck::bytes_of(&data), ); } if self.lights_are_dirty { self.lights_are_dirty = false; for (i, light) in self.lights.iter().enumerate() { queue.write_buffer( &self.light_uniform_buf, (i * mem::size_of::()) as wgpu::BufferAddress, bytemuck::bytes_of(&light.to_raw()), ); } } let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); encoder.push_debug_group("shadow passes"); for (i, light) in self.lights.iter().enumerate() { encoder.push_debug_group(&format!( "shadow pass {} (light at position {:?})", i, light.pos )); // The light uniform buffer already has the projection, // let's just copy it over to the shadow uniform buffer. encoder.copy_buffer_to_buffer( &self.light_uniform_buf, (i * mem::size_of::()) as wgpu::BufferAddress, &self.shadow_pass.uniform_buf, 0, 64, ); encoder.insert_debug_marker("render entities"); { let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[], depth_stencil_attachment: Some( wgpu::RenderPassDepthStencilAttachmentDescriptor { attachment: &light.target_view, depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(1.0), store: true, }), stencil_ops: None, }, ), }); pass.set_pipeline(&self.shadow_pass.pipeline); pass.set_bind_group(0, &self.shadow_pass.bind_group, &[]); for entity in &self.entities { pass.set_bind_group(1, &self.entity_bind_group, &[entity.uniform_offset]); pass.set_index_buffer(entity.index_buf.slice(..), entity.index_format); pass.set_vertex_buffer(0, entity.vertex_buf.slice(..)); pass.draw_indexed(0..entity.index_count as u32, 0, 0..1); } } encoder.pop_debug_group(); } encoder.pop_debug_group(); // forward pass encoder.push_debug_group("forward rendering pass"); { let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { attachment: &frame.view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0, }), store: true, }, }], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { attachment: &self.forward_depth, depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(1.0), store: false, }), stencil_ops: None, }), }); pass.set_pipeline(&self.forward_pass.pipeline); pass.set_bind_group(0, &self.forward_pass.bind_group, &[]); for entity in &self.entities { pass.set_bind_group(1, &self.entity_bind_group, &[entity.uniform_offset]); pass.set_index_buffer(entity.index_buf.slice(..), entity.index_format); pass.set_vertex_buffer(0, entity.vertex_buf.slice(..)); pass.draw_indexed(0..entity.index_count as u32, 0, 0..1); } } encoder.pop_debug_group(); queue.submit(iter::once(encoder.finish())); } } fn main() { framework::run::("shadow"); }