wgpu_examples/shadow/
mod.rs

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        // top (0, 0, 1)
23        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        // bottom (0, 0, -1)
28        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        // right (1, 0, 0)
33        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        // left (-1, 0, 0)
38        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        // front (0, 1, 0)
43        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        // back (0, -1, 0)
48        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, // top
56        4, 5, 6, 6, 7, 4, // bottom
57        8, 9, 10, 10, 11, 8, // right
58        12, 13, 14, 14, 15, 12, // left
59        16, 17, 18, 18, 19, 16, // front
60        20, 21, 22, 22, 23, 20, // back
61    ];
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        // Create the vertex and index buffers
222        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        // Make the `uniform_alignment` >= `entity_uniform_size` and aligned to `min_uniform_buffer_offset_alignment`.
285        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        // Note: dynamic uniform offsets also have to be aligned to `Limits::min_uniform_buffer_offset_alignment`.
291        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        // Create other resources
362        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            // Create pipeline layout
452            let bind_group_layout =
453                device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
454                    label: None,
455                    entries: &[wgpu::BindGroupLayoutEntry {
456                        binding: 0, // global
457                        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            // Create bind group
480            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            // Create the render pipeline
490            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, // corresponds to bilinear filtering
516                        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            // Create pipeline layout
534            let bind_group_layout =
535                device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
536                    entries: &[
537                        wgpu::BindGroupLayoutEntry {
538                            binding: 0, // global
539                            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, // lights
551                            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            // Create bind group
600            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            // Create the render pipeline
624            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        //empty
684    }
685
686    fn resize(
687        &mut self,
688        config: &wgpu::SurfaceConfiguration,
689        device: &wgpu::Device,
690        queue: &wgpu::Queue,
691    ) {
692        // update view-projection matrix
693        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        // update uniforms
706        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            // The light uniform buffer already has the projection,
750            // let's just copy it over to the shadow uniform buffer.
751            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        // forward pass
791        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        // rpi4 on VK doesn't work: https://gitlab.freedesktop.org/mesa/mesa/-/issues/3916
851        .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};