1use std::{f32::consts, iter, ops::Range};
2
3use bytemuck::{Pod, Zeroable};
4use wgpu::util::{align_to, DeviceExt};
5
6#[repr(C)]
7#[derive(Clone, Copy, Pod, Zeroable)]
8struct Vertex {
9 _pos: [i8; 4],
10 _normal: [i8; 4],
11}
12
13fn vertex(pos: [i8; 3], nor: [i8; 3]) -> Vertex {
14 Vertex {
15 _pos: [pos[0], pos[1], pos[2], 1],
16 _normal: [nor[0], nor[1], nor[2], 0],
17 }
18}
19
20fn create_cube() -> (Vec<Vertex>, Vec<u16>) {
21 let vertex_data = [
22 vertex([-1, -1, 1], [0, 0, 1]),
24 vertex([1, -1, 1], [0, 0, 1]),
25 vertex([1, 1, 1], [0, 0, 1]),
26 vertex([-1, 1, 1], [0, 0, 1]),
27 vertex([-1, 1, -1], [0, 0, -1]),
29 vertex([1, 1, -1], [0, 0, -1]),
30 vertex([1, -1, -1], [0, 0, -1]),
31 vertex([-1, -1, -1], [0, 0, -1]),
32 vertex([1, -1, -1], [1, 0, 0]),
34 vertex([1, 1, -1], [1, 0, 0]),
35 vertex([1, 1, 1], [1, 0, 0]),
36 vertex([1, -1, 1], [1, 0, 0]),
37 vertex([-1, -1, 1], [-1, 0, 0]),
39 vertex([-1, 1, 1], [-1, 0, 0]),
40 vertex([-1, 1, -1], [-1, 0, 0]),
41 vertex([-1, -1, -1], [-1, 0, 0]),
42 vertex([1, 1, -1], [0, 1, 0]),
44 vertex([-1, 1, -1], [0, 1, 0]),
45 vertex([-1, 1, 1], [0, 1, 0]),
46 vertex([1, 1, 1], [0, 1, 0]),
47 vertex([1, -1, 1], [0, -1, 0]),
49 vertex([-1, -1, 1], [0, -1, 0]),
50 vertex([-1, -1, -1], [0, -1, 0]),
51 vertex([1, -1, -1], [0, -1, 0]),
52 ];
53
54 let index_data: &[u16] = &[
55 0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 8, 9, 10, 10, 11, 8, 12, 13, 14, 14, 15, 12, 16, 17, 18, 18, 19, 16, 20, 21, 22, 22, 23, 20, ];
62
63 (vertex_data.to_vec(), index_data.to_vec())
64}
65
66fn create_plane(size: i8) -> (Vec<Vertex>, Vec<u16>) {
67 let vertex_data = [
68 vertex([size, -size, 0], [0, 0, 1]),
69 vertex([size, size, 0], [0, 0, 1]),
70 vertex([-size, -size, 0], [0, 0, 1]),
71 vertex([-size, size, 0], [0, 0, 1]),
72 ];
73
74 let index_data: &[u16] = &[0, 1, 2, 2, 1, 3];
75
76 (vertex_data.to_vec(), index_data.to_vec())
77}
78
79struct Entity {
80 mx_world: glam::Mat4,
81 rotation_speed: f32,
82 color: wgpu::Color,
83 vertex_buf: wgpu::Buffer,
84 index_buf: wgpu::Buffer,
85 index_format: wgpu::IndexFormat,
86 index_count: usize,
87 uniform_offset: wgpu::DynamicOffset,
88}
89
90struct Light {
91 pos: glam::Vec3,
92 color: wgpu::Color,
93 fov: f32,
94 depth: Range<f32>,
95 target_view: wgpu::TextureView,
96}
97
98#[repr(C)]
99#[derive(Clone, Copy, Pod, Zeroable)]
100struct LightRaw {
101 proj: [[f32; 4]; 4],
102 pos: [f32; 4],
103 color: [f32; 4],
104}
105
106impl Light {
107 fn to_raw(&self) -> LightRaw {
108 let view = glam::Mat4::look_at_rh(self.pos, glam::Vec3::ZERO, glam::Vec3::Z);
109 let projection = glam::Mat4::perspective_rh(
110 self.fov * consts::PI / 180.,
111 1.0,
112 self.depth.start,
113 self.depth.end,
114 );
115 let view_proj = projection * view;
116 LightRaw {
117 proj: view_proj.to_cols_array_2d(),
118 pos: [self.pos.x, self.pos.y, self.pos.z, 1.0],
119 color: [
120 self.color.r as f32,
121 self.color.g as f32,
122 self.color.b as f32,
123 1.0,
124 ],
125 }
126 }
127}
128
129#[repr(C)]
130#[derive(Clone, Copy, Pod, Zeroable)]
131struct GlobalUniforms {
132 proj: [[f32; 4]; 4],
133 num_lights: [u32; 4],
134}
135
136#[repr(C)]
137#[derive(Clone, Copy, Pod, Zeroable)]
138struct EntityUniforms {
139 model: [[f32; 4]; 4],
140 color: [f32; 4],
141}
142
143struct Pass {
144 pipeline: wgpu::RenderPipeline,
145 bind_group: wgpu::BindGroup,
146 uniform_buf: wgpu::Buffer,
147}
148
149struct Example {
150 entities: Vec<Entity>,
151 lights: Vec<Light>,
152 lights_are_dirty: bool,
153 shadow_pass: Pass,
154 forward_pass: Pass,
155 forward_depth: wgpu::TextureView,
156 entity_bind_group: wgpu::BindGroup,
157 light_storage_buf: wgpu::Buffer,
158 entity_uniform_buf: wgpu::Buffer,
159}
160
161impl Example {
162 const MAX_LIGHTS: usize = 10;
163 const SHADOW_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
164 const SHADOW_SIZE: wgpu::Extent3d = wgpu::Extent3d {
165 width: 512,
166 height: 512,
167 depth_or_array_layers: Self::MAX_LIGHTS as u32,
168 };
169 const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
170
171 fn generate_matrix(aspect_ratio: f32) -> glam::Mat4 {
172 let projection = glam::Mat4::perspective_rh(consts::FRAC_PI_4, aspect_ratio, 1.0, 20.0);
173 let view = glam::Mat4::look_at_rh(
174 glam::Vec3::new(3.0f32, -10.0, 6.0),
175 glam::Vec3::new(0f32, 0.0, 0.0),
176 glam::Vec3::Z,
177 );
178 projection * view
179 }
180
181 fn create_depth_texture(
182 config: &wgpu::SurfaceConfiguration,
183 device: &wgpu::Device,
184 ) -> wgpu::TextureView {
185 let depth_texture = device.create_texture(&wgpu::TextureDescriptor {
186 size: wgpu::Extent3d {
187 width: config.width,
188 height: config.height,
189 depth_or_array_layers: 1,
190 },
191 mip_level_count: 1,
192 sample_count: 1,
193 dimension: wgpu::TextureDimension::D2,
194 format: Self::DEPTH_FORMAT,
195 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
196 label: None,
197 view_formats: &[],
198 });
199
200 depth_texture.create_view(&wgpu::TextureViewDescriptor::default())
201 }
202}
203
204impl crate::framework::Example for Example {
205 fn optional_features() -> wgpu::Features {
206 wgpu::Features::DEPTH_CLIP_CONTROL
207 }
208
209 fn init(
210 config: &wgpu::SurfaceConfiguration,
211 adapter: &wgpu::Adapter,
212 device: &wgpu::Device,
213 _queue: &wgpu::Queue,
214 ) -> Self {
215 let supports_storage_resources = adapter
216 .get_downlevel_capabilities()
217 .flags
218 .contains(wgpu::DownlevelFlags::VERTEX_STORAGE)
219 && device.limits().max_storage_buffers_per_shader_stage > 0;
220
221 let vertex_size = size_of::<Vertex>();
223 let (cube_vertex_data, cube_index_data) = create_cube();
224 let cube_vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
225 label: Some("Cubes Vertex Buffer"),
226 contents: bytemuck::cast_slice(&cube_vertex_data),
227 usage: wgpu::BufferUsages::VERTEX,
228 });
229
230 let cube_index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
231 label: Some("Cubes Index Buffer"),
232 contents: bytemuck::cast_slice(&cube_index_data),
233 usage: wgpu::BufferUsages::INDEX,
234 });
235
236 let (plane_vertex_data, plane_index_data) = create_plane(7);
237 let plane_vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
238 label: Some("Plane Vertex Buffer"),
239 contents: bytemuck::cast_slice(&plane_vertex_data),
240 usage: wgpu::BufferUsages::VERTEX,
241 });
242
243 let plane_index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
244 label: Some("Plane Index Buffer"),
245 contents: bytemuck::cast_slice(&plane_index_data),
246 usage: wgpu::BufferUsages::INDEX,
247 });
248
249 struct CubeDesc {
250 offset: glam::Vec3,
251 angle: f32,
252 scale: f32,
253 rotation: f32,
254 }
255 let cube_descs = [
256 CubeDesc {
257 offset: glam::Vec3::new(-2.0, -2.0, 2.0),
258 angle: 10.0,
259 scale: 0.7,
260 rotation: 0.1,
261 },
262 CubeDesc {
263 offset: glam::Vec3::new(2.0, -2.0, 2.0),
264 angle: 50.0,
265 scale: 1.3,
266 rotation: 0.2,
267 },
268 CubeDesc {
269 offset: glam::Vec3::new(-2.0, 2.0, 2.0),
270 angle: 140.0,
271 scale: 1.1,
272 rotation: 0.3,
273 },
274 CubeDesc {
275 offset: glam::Vec3::new(2.0, 2.0, 2.0),
276 angle: 210.0,
277 scale: 0.9,
278 rotation: 0.4,
279 },
280 ];
281
282 let entity_uniform_size = size_of::<EntityUniforms>() as wgpu::BufferAddress;
283 let num_entities = 1 + cube_descs.len() as wgpu::BufferAddress;
284 let uniform_alignment = {
286 let alignment =
287 device.limits().min_uniform_buffer_offset_alignment as wgpu::BufferAddress;
288 align_to(entity_uniform_size, alignment)
289 };
290 let entity_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
292 label: None,
293 size: num_entities * uniform_alignment,
294 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
295 mapped_at_creation: false,
296 });
297
298 let index_format = wgpu::IndexFormat::Uint16;
299
300 let mut entities = vec![{
301 Entity {
302 mx_world: glam::Mat4::IDENTITY,
303 rotation_speed: 0.0,
304 color: wgpu::Color::WHITE,
305 vertex_buf: plane_vertex_buf,
306 index_buf: plane_index_buf,
307 index_format,
308 index_count: plane_index_data.len(),
309 uniform_offset: 0,
310 }
311 }];
312
313 for (i, cube) in cube_descs.iter().enumerate() {
314 let mx_world = glam::Mat4::from_scale_rotation_translation(
315 glam::Vec3::splat(cube.scale),
316 glam::Quat::from_axis_angle(
317 cube.offset.normalize(),
318 cube.angle * consts::PI / 180.,
319 ),
320 cube.offset,
321 );
322 entities.push(Entity {
323 mx_world,
324 rotation_speed: cube.rotation,
325 color: wgpu::Color::GREEN,
326 vertex_buf: cube_vertex_buf.clone(),
327 index_buf: cube_index_buf.clone(),
328 index_format,
329 index_count: cube_index_data.len(),
330 uniform_offset: ((i + 1) * uniform_alignment as usize) as _,
331 });
332 }
333
334 let local_bind_group_layout =
335 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
336 entries: &[wgpu::BindGroupLayoutEntry {
337 binding: 0,
338 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
339 ty: wgpu::BindingType::Buffer {
340 ty: wgpu::BufferBindingType::Uniform,
341 has_dynamic_offset: true,
342 min_binding_size: wgpu::BufferSize::new(entity_uniform_size),
343 },
344 count: None,
345 }],
346 label: None,
347 });
348 let entity_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
349 layout: &local_bind_group_layout,
350 entries: &[wgpu::BindGroupEntry {
351 binding: 0,
352 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
353 buffer: &entity_uniform_buf,
354 offset: 0,
355 size: wgpu::BufferSize::new(entity_uniform_size),
356 }),
357 }],
358 label: None,
359 });
360
361 let shadow_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
363 label: Some("shadow"),
364 address_mode_u: wgpu::AddressMode::ClampToEdge,
365 address_mode_v: wgpu::AddressMode::ClampToEdge,
366 address_mode_w: wgpu::AddressMode::ClampToEdge,
367 mag_filter: wgpu::FilterMode::Linear,
368 min_filter: wgpu::FilterMode::Linear,
369 mipmap_filter: wgpu::FilterMode::Nearest,
370 compare: Some(wgpu::CompareFunction::LessEqual),
371 ..Default::default()
372 });
373
374 let shadow_texture = device.create_texture(&wgpu::TextureDescriptor {
375 size: Self::SHADOW_SIZE,
376 mip_level_count: 1,
377 sample_count: 1,
378 dimension: wgpu::TextureDimension::D2,
379 format: Self::SHADOW_FORMAT,
380 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
381 label: None,
382 view_formats: &[],
383 });
384 let shadow_view = shadow_texture.create_view(&wgpu::TextureViewDescriptor::default());
385
386 let mut shadow_target_views = (0..2)
387 .map(|i| {
388 Some(shadow_texture.create_view(&wgpu::TextureViewDescriptor {
389 label: Some("shadow"),
390 format: None,
391 dimension: Some(wgpu::TextureViewDimension::D2),
392 usage: None,
393 aspect: wgpu::TextureAspect::All,
394 base_mip_level: 0,
395 mip_level_count: None,
396 base_array_layer: i as u32,
397 array_layer_count: Some(1),
398 }))
399 })
400 .collect::<Vec<_>>();
401 let lights = vec![
402 Light {
403 pos: glam::Vec3::new(7.0, -5.0, 10.0),
404 color: wgpu::Color {
405 r: 0.5,
406 g: 1.0,
407 b: 0.5,
408 a: 1.0,
409 },
410 fov: 60.0,
411 depth: 1.0..20.0,
412 target_view: shadow_target_views[0].take().unwrap(),
413 },
414 Light {
415 pos: glam::Vec3::new(-5.0, 7.0, 10.0),
416 color: wgpu::Color {
417 r: 1.0,
418 g: 0.5,
419 b: 0.5,
420 a: 1.0,
421 },
422 fov: 45.0,
423 depth: 1.0..20.0,
424 target_view: shadow_target_views[1].take().unwrap(),
425 },
426 ];
427 let light_uniform_size = (Self::MAX_LIGHTS * size_of::<LightRaw>()) as wgpu::BufferAddress;
428 let light_storage_buf = device.create_buffer(&wgpu::BufferDescriptor {
429 label: None,
430 size: light_uniform_size,
431 usage: if supports_storage_resources {
432 wgpu::BufferUsages::STORAGE
433 } else {
434 wgpu::BufferUsages::UNIFORM
435 } | wgpu::BufferUsages::COPY_SRC
436 | wgpu::BufferUsages::COPY_DST,
437 mapped_at_creation: false,
438 });
439
440 let vertex_attr = wgpu::vertex_attr_array![0 => Sint8x4, 1 => Sint8x4];
441 let vb_desc = wgpu::VertexBufferLayout {
442 array_stride: vertex_size as wgpu::BufferAddress,
443 step_mode: wgpu::VertexStepMode::Vertex,
444 attributes: &vertex_attr,
445 };
446
447 let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
448
449 let shadow_pass = {
450 let uniform_size = size_of::<GlobalUniforms>() as wgpu::BufferAddress;
451 let bind_group_layout =
453 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
454 label: None,
455 entries: &[wgpu::BindGroupLayoutEntry {
456 binding: 0, visibility: wgpu::ShaderStages::VERTEX,
458 ty: wgpu::BindingType::Buffer {
459 ty: wgpu::BufferBindingType::Uniform,
460 has_dynamic_offset: false,
461 min_binding_size: wgpu::BufferSize::new(uniform_size),
462 },
463 count: None,
464 }],
465 });
466 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
467 label: Some("shadow"),
468 bind_group_layouts: &[&bind_group_layout, &local_bind_group_layout],
469 push_constant_ranges: &[],
470 });
471
472 let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
473 label: None,
474 size: uniform_size,
475 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
476 mapped_at_creation: false,
477 });
478
479 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
481 layout: &bind_group_layout,
482 entries: &[wgpu::BindGroupEntry {
483 binding: 0,
484 resource: uniform_buf.as_entire_binding(),
485 }],
486 label: None,
487 });
488
489 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
491 label: Some("shadow"),
492 layout: Some(&pipeline_layout),
493 vertex: wgpu::VertexState {
494 module: &shader,
495 entry_point: Some("vs_bake"),
496 compilation_options: Default::default(),
497 buffers: &[vb_desc.clone()],
498 },
499 fragment: None,
500 primitive: wgpu::PrimitiveState {
501 topology: wgpu::PrimitiveTopology::TriangleList,
502 front_face: wgpu::FrontFace::Ccw,
503 cull_mode: Some(wgpu::Face::Back),
504 unclipped_depth: device
505 .features()
506 .contains(wgpu::Features::DEPTH_CLIP_CONTROL),
507 ..Default::default()
508 },
509 depth_stencil: Some(wgpu::DepthStencilState {
510 format: Self::SHADOW_FORMAT,
511 depth_write_enabled: true,
512 depth_compare: wgpu::CompareFunction::LessEqual,
513 stencil: wgpu::StencilState::default(),
514 bias: wgpu::DepthBiasState {
515 constant: 2, slope_scale: 2.0,
517 clamp: 0.0,
518 },
519 }),
520 multisample: wgpu::MultisampleState::default(),
521 multiview: None,
522 cache: None,
523 });
524
525 Pass {
526 pipeline,
527 bind_group,
528 uniform_buf,
529 }
530 };
531
532 let forward_pass = {
533 let bind_group_layout =
535 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
536 entries: &[
537 wgpu::BindGroupLayoutEntry {
538 binding: 0, visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
540 ty: wgpu::BindingType::Buffer {
541 ty: wgpu::BufferBindingType::Uniform,
542 has_dynamic_offset: false,
543 min_binding_size: wgpu::BufferSize::new(
544 size_of::<GlobalUniforms>() as _,
545 ),
546 },
547 count: None,
548 },
549 wgpu::BindGroupLayoutEntry {
550 binding: 1, visibility: wgpu::ShaderStages::FRAGMENT,
552 ty: wgpu::BindingType::Buffer {
553 ty: if supports_storage_resources {
554 wgpu::BufferBindingType::Storage { read_only: true }
555 } else {
556 wgpu::BufferBindingType::Uniform
557 },
558 has_dynamic_offset: false,
559 min_binding_size: wgpu::BufferSize::new(light_uniform_size),
560 },
561 count: None,
562 },
563 wgpu::BindGroupLayoutEntry {
564 binding: 2,
565 visibility: wgpu::ShaderStages::FRAGMENT,
566 ty: wgpu::BindingType::Texture {
567 multisampled: false,
568 sample_type: wgpu::TextureSampleType::Depth,
569 view_dimension: wgpu::TextureViewDimension::D2Array,
570 },
571 count: None,
572 },
573 wgpu::BindGroupLayoutEntry {
574 binding: 3,
575 visibility: wgpu::ShaderStages::FRAGMENT,
576 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
577 count: None,
578 },
579 ],
580 label: None,
581 });
582 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
583 label: Some("main"),
584 bind_group_layouts: &[&bind_group_layout, &local_bind_group_layout],
585 push_constant_ranges: &[],
586 });
587
588 let mx_total = Self::generate_matrix(config.width as f32 / config.height as f32);
589 let forward_uniforms = GlobalUniforms {
590 proj: mx_total.to_cols_array_2d(),
591 num_lights: [lights.len() as u32, 0, 0, 0],
592 };
593 let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
594 label: Some("Uniform Buffer"),
595 contents: bytemuck::bytes_of(&forward_uniforms),
596 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
597 });
598
599 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
601 layout: &bind_group_layout,
602 entries: &[
603 wgpu::BindGroupEntry {
604 binding: 0,
605 resource: uniform_buf.as_entire_binding(),
606 },
607 wgpu::BindGroupEntry {
608 binding: 1,
609 resource: light_storage_buf.as_entire_binding(),
610 },
611 wgpu::BindGroupEntry {
612 binding: 2,
613 resource: wgpu::BindingResource::TextureView(&shadow_view),
614 },
615 wgpu::BindGroupEntry {
616 binding: 3,
617 resource: wgpu::BindingResource::Sampler(&shadow_sampler),
618 },
619 ],
620 label: None,
621 });
622
623 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
625 label: Some("main"),
626 layout: Some(&pipeline_layout),
627 vertex: wgpu::VertexState {
628 module: &shader,
629 entry_point: Some("vs_main"),
630 compilation_options: Default::default(),
631 buffers: &[vb_desc],
632 },
633 fragment: Some(wgpu::FragmentState {
634 module: &shader,
635 entry_point: Some(if supports_storage_resources {
636 "fs_main"
637 } else {
638 "fs_main_without_storage"
639 }),
640 compilation_options: Default::default(),
641 targets: &[Some(config.view_formats[0].into())],
642 }),
643 primitive: wgpu::PrimitiveState {
644 front_face: wgpu::FrontFace::Ccw,
645 cull_mode: Some(wgpu::Face::Back),
646 ..Default::default()
647 },
648 depth_stencil: Some(wgpu::DepthStencilState {
649 format: Self::DEPTH_FORMAT,
650 depth_write_enabled: true,
651 depth_compare: wgpu::CompareFunction::Less,
652 stencil: wgpu::StencilState::default(),
653 bias: wgpu::DepthBiasState::default(),
654 }),
655 multisample: wgpu::MultisampleState::default(),
656 multiview: None,
657 cache: None,
658 });
659
660 Pass {
661 pipeline,
662 bind_group,
663 uniform_buf,
664 }
665 };
666
667 let forward_depth = Self::create_depth_texture(config, device);
668
669 Example {
670 entities,
671 lights,
672 lights_are_dirty: true,
673 shadow_pass,
674 forward_pass,
675 forward_depth,
676 light_storage_buf,
677 entity_uniform_buf,
678 entity_bind_group,
679 }
680 }
681
682 fn update(&mut self, _event: winit::event::WindowEvent) {
683 }
685
686 fn resize(
687 &mut self,
688 config: &wgpu::SurfaceConfiguration,
689 device: &wgpu::Device,
690 queue: &wgpu::Queue,
691 ) {
692 let mx_total = Self::generate_matrix(config.width as f32 / config.height as f32);
694 let mx_ref: &[f32; 16] = mx_total.as_ref();
695 queue.write_buffer(
696 &self.forward_pass.uniform_buf,
697 0,
698 bytemuck::cast_slice(mx_ref),
699 );
700
701 self.forward_depth = Self::create_depth_texture(config, device);
702 }
703
704 fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
705 for entity in self.entities.iter_mut() {
707 if entity.rotation_speed != 0.0 {
708 let rotation =
709 glam::Mat4::from_rotation_x(entity.rotation_speed * consts::PI / 180.);
710 entity.mx_world *= rotation;
711 }
712 let data = EntityUniforms {
713 model: entity.mx_world.to_cols_array_2d(),
714 color: [
715 entity.color.r as f32,
716 entity.color.g as f32,
717 entity.color.b as f32,
718 entity.color.a as f32,
719 ],
720 };
721 queue.write_buffer(
722 &self.entity_uniform_buf,
723 entity.uniform_offset as wgpu::BufferAddress,
724 bytemuck::bytes_of(&data),
725 );
726 }
727
728 if self.lights_are_dirty {
729 self.lights_are_dirty = false;
730 for (i, light) in self.lights.iter().enumerate() {
731 queue.write_buffer(
732 &self.light_storage_buf,
733 (i * size_of::<LightRaw>()) as wgpu::BufferAddress,
734 bytemuck::bytes_of(&light.to_raw()),
735 );
736 }
737 }
738
739 let mut encoder =
740 device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
741
742 encoder.push_debug_group("shadow passes");
743 for (i, light) in self.lights.iter().enumerate() {
744 encoder.push_debug_group(&format!(
745 "shadow pass {} (light at position {:?})",
746 i, light.pos
747 ));
748
749 encoder.copy_buffer_to_buffer(
752 &self.light_storage_buf,
753 (i * size_of::<LightRaw>()) as wgpu::BufferAddress,
754 &self.shadow_pass.uniform_buf,
755 0,
756 64,
757 );
758
759 encoder.insert_debug_marker("render entities");
760 {
761 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
762 label: None,
763 color_attachments: &[],
764 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
765 view: &light.target_view,
766 depth_ops: Some(wgpu::Operations {
767 load: wgpu::LoadOp::Clear(1.0),
768 store: wgpu::StoreOp::Store,
769 }),
770 stencil_ops: None,
771 }),
772 timestamp_writes: None,
773 occlusion_query_set: None,
774 });
775 pass.set_pipeline(&self.shadow_pass.pipeline);
776 pass.set_bind_group(0, &self.shadow_pass.bind_group, &[]);
777
778 for entity in &self.entities {
779 pass.set_bind_group(1, &self.entity_bind_group, &[entity.uniform_offset]);
780 pass.set_index_buffer(entity.index_buf.slice(..), entity.index_format);
781 pass.set_vertex_buffer(0, entity.vertex_buf.slice(..));
782 pass.draw_indexed(0..entity.index_count as u32, 0, 0..1);
783 }
784 }
785
786 encoder.pop_debug_group();
787 }
788 encoder.pop_debug_group();
789
790 encoder.push_debug_group("forward rendering pass");
792 {
793 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
794 label: None,
795 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
796 view,
797 depth_slice: None,
798 resolve_target: None,
799 ops: wgpu::Operations {
800 load: wgpu::LoadOp::Clear(wgpu::Color {
801 r: 0.1,
802 g: 0.2,
803 b: 0.3,
804 a: 1.0,
805 }),
806 store: wgpu::StoreOp::Store,
807 },
808 })],
809 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
810 view: &self.forward_depth,
811 depth_ops: Some(wgpu::Operations {
812 load: wgpu::LoadOp::Clear(1.0),
813 store: wgpu::StoreOp::Discard,
814 }),
815 stencil_ops: None,
816 }),
817 timestamp_writes: None,
818 occlusion_query_set: None,
819 });
820 pass.set_pipeline(&self.forward_pass.pipeline);
821 pass.set_bind_group(0, &self.forward_pass.bind_group, &[]);
822
823 for entity in &self.entities {
824 pass.set_bind_group(1, &self.entity_bind_group, &[entity.uniform_offset]);
825 pass.set_index_buffer(entity.index_buf.slice(..), entity.index_format);
826 pass.set_vertex_buffer(0, entity.vertex_buf.slice(..));
827 pass.draw_indexed(0..entity.index_count as u32, 0, 0..1);
828 }
829 }
830 encoder.pop_debug_group();
831
832 queue.submit(iter::once(encoder.finish()));
833 }
834}
835
836pub fn main() {
837 crate::framework::run::<Example>("shadow");
838}
839
840#[cfg(test)]
841#[wgpu_test::gpu_test]
842pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
843 name: "shadow",
844 image_path: "/examples/features/src/shadow/screenshot.png",
845 width: 1024,
846 height: 768,
847 optional_features: wgpu::Features::default(),
848 base_test_parameters: wgpu_test::TestParameters::default()
849 .downlevel_flags(wgpu::DownlevelFlags::COMPARISON_SAMPLERS)
850 .expect_fail(wgpu_test::FailureCase::backend_adapter(
852 wgpu::Backends::VULKAN,
853 "V3D",
854 )),
855 comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],
856 _phantom: std::marker::PhantomData::<Example>,
857};