wgpu_examples/ray_shadows/
mod.rs

1use std::{borrow::Cow, iter, mem};
2
3use bytemuck::{Pod, Zeroable};
4use glam::{Mat4, Vec3};
5use wgpu::util::DeviceExt;
6use wgpu::{vertex_attr_array, IndexFormat, VertexBufferLayout};
7
8use crate::utils;
9
10// from cube
11#[repr(C)]
12#[derive(Clone, Copy, Pod, Zeroable)]
13struct Vertex {
14    _pos: [f32; 3],
15    _normal: [f32; 3],
16}
17
18fn vertex(pos: [f32; 3], normal: [f32; 3]) -> Vertex {
19    Vertex {
20        _pos: pos,
21        _normal: normal,
22    }
23}
24
25fn create_vertices() -> (Vec<Vertex>, Vec<u16>) {
26    let vertex_data = [
27        // base
28        vertex([-1.0, 0.0, -1.0], [0.0, 1.0, 0.0]),
29        vertex([-1.0, 0.0, 1.0], [0.0, 1.0, 0.0]),
30        vertex([1.0, 0.0, -1.0], [0.0, 1.0, 0.0]),
31        vertex([1.0, 0.0, 1.0], [0.0, 1.0, 0.0]),
32        //shadow caster
33        vertex([-(1.0 / 3.0), 0.0, 1.0], [0.0, 0.0, 1.0]),
34        vertex([-(1.0 / 3.0), 2.0 / 3.0, 1.0], [0.0, 0.0, 1.0]),
35        vertex([1.0 / 3.0, 0.0, 1.0], [0.0, 0.0, 1.0]),
36        vertex([1.0 / 3.0, 2.0 / 3.0, 1.0], [0.0, 0.0, 1.0]),
37    ];
38
39    let index_data: &[u16] = &[
40        0, 1, 2, 2, 3, 1, //base
41        4, 5, 6, 6, 7, 5,
42    ];
43
44    (vertex_data.to_vec(), index_data.to_vec())
45}
46
47#[repr(C)]
48#[derive(Clone, Copy, Pod, Zeroable)]
49struct Uniforms {
50    view_inverse: Mat4,
51    proj_inverse: Mat4,
52    vertex: Mat4,
53}
54
55struct Example {
56    uniforms: Uniforms,
57    uniform_buf: wgpu::Buffer,
58    vertex_buf: wgpu::Buffer,
59    index_buf: wgpu::Buffer,
60    pipeline: wgpu::RenderPipeline,
61    bind_group: wgpu::BindGroup,
62    animation_timer: utils::AnimationTimer,
63}
64
65const CAM_LOOK_AT: Vec3 = Vec3::new(0.0, 1.0, -1.5);
66
67fn create_matrix(config: &wgpu::SurfaceConfiguration) -> Uniforms {
68    let view = Mat4::look_at_rh(CAM_LOOK_AT, Vec3::ZERO, Vec3::Y);
69    let proj = Mat4::perspective_rh(
70        59.0_f32.to_radians(),
71        config.width as f32 / config.height as f32,
72        0.1,
73        1000.0,
74    );
75
76    Uniforms {
77        view_inverse: view.inverse(),
78        proj_inverse: proj.inverse(),
79        vertex: (proj * view),
80    }
81}
82
83impl crate::framework::Example for Example {
84    fn required_features() -> wgpu::Features {
85        wgpu::Features::EXPERIMENTAL_RAY_QUERY | wgpu::Features::IMMEDIATES
86    }
87
88    fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {
89        wgpu::DownlevelCapabilities {
90            flags: wgpu::DownlevelFlags::COMPUTE_SHADERS,
91            ..Default::default()
92        }
93    }
94
95    fn required_limits() -> wgpu::Limits {
96        wgpu::Limits {
97            max_immediate_size: 16,
98            ..wgpu::Limits::default()
99        }
100        .using_minimum_supported_acceleration_structure_values()
101    }
102
103    fn init(
104        config: &wgpu::SurfaceConfiguration,
105        _adapter: &wgpu::Adapter,
106        device: &wgpu::Device,
107        queue: &wgpu::Queue,
108    ) -> Self {
109        let uniforms = create_matrix(config);
110
111        let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
112            label: Some("Uniform Buffer"),
113            contents: bytemuck::cast_slice(&[uniforms]),
114            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
115        });
116
117        let (vertex_data, index_data) = create_vertices();
118
119        let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
120            label: Some("Vertex Buffer"),
121            contents: bytemuck::cast_slice(&vertex_data),
122            usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT,
123        });
124
125        let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
126            label: Some("Index Buffer"),
127            contents: bytemuck::cast_slice(&index_data),
128            usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT,
129        });
130
131        let blas_geo_size_desc = wgpu::BlasTriangleGeometrySizeDescriptor {
132            vertex_format: wgpu::VertexFormat::Float32x3,
133            vertex_count: vertex_data.len() as u32,
134            index_format: Some(wgpu::IndexFormat::Uint16),
135            index_count: Some(index_data.len() as u32),
136            flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,
137        };
138
139        let blas = device.create_blas(
140            &wgpu::CreateBlasDescriptor {
141                label: None,
142                flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,
143                update_mode: wgpu::AccelerationStructureUpdateMode::Build,
144            },
145            wgpu::BlasGeometrySizeDescriptors::Triangles {
146                descriptors: vec![blas_geo_size_desc.clone()],
147            },
148        );
149
150        let mut tlas = device.create_tlas(&wgpu::CreateTlasDescriptor {
151            label: None,
152            flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,
153            update_mode: wgpu::AccelerationStructureUpdateMode::Build,
154            max_instances: 1,
155        });
156
157        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
158            label: None,
159            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
160        });
161
162        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
163            label: None,
164            entries: &[
165                wgpu::BindGroupLayoutEntry {
166                    binding: 0,
167                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
168                    ty: wgpu::BindingType::Buffer {
169                        ty: wgpu::BufferBindingType::Uniform,
170                        has_dynamic_offset: false,
171                        min_binding_size: None,
172                    },
173                    count: None,
174                },
175                wgpu::BindGroupLayoutEntry {
176                    binding: 1,
177                    visibility: wgpu::ShaderStages::FRAGMENT,
178                    ty: wgpu::BindingType::AccelerationStructure {
179                        vertex_return: false,
180                    },
181                    count: None,
182                },
183            ],
184        });
185
186        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
187            label: None,
188            bind_group_layouts: &[Some(&bind_group_layout)],
189            immediate_size: 16,
190        });
191
192        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
193            label: None,
194            layout: Some(&pipeline_layout),
195            vertex: wgpu::VertexState {
196                module: &shader,
197                entry_point: Some("vs_main"),
198                compilation_options: Default::default(),
199                buffers: &[VertexBufferLayout {
200                    array_stride: mem::size_of::<Vertex>() as wgpu::BufferAddress,
201                    step_mode: Default::default(),
202                    attributes: &vertex_attr_array![0 => Float32x3, 1 => Float32x3],
203                }],
204            },
205            fragment: Some(wgpu::FragmentState {
206                module: &shader,
207                entry_point: Some("fs_main"),
208                compilation_options: Default::default(),
209                targets: &[Some(config.format.into())],
210            }),
211            primitive: wgpu::PrimitiveState {
212                topology: wgpu::PrimitiveTopology::TriangleList,
213                ..Default::default()
214            },
215            depth_stencil: None,
216            multisample: wgpu::MultisampleState::default(),
217            multiview_mask: None,
218            cache: None,
219        });
220
221        tlas[0] = Some(wgpu::TlasInstance::new(
222            &blas,
223            [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0],
224            0,
225            0xFF,
226        ));
227
228        let mut encoder =
229            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
230
231        encoder.build_acceleration_structures(
232            iter::once(&wgpu::BlasBuildEntry {
233                blas: &blas,
234                geometry: wgpu::BlasGeometries::TriangleGeometries(vec![
235                    wgpu::BlasTriangleGeometry {
236                        size: &blas_geo_size_desc,
237                        vertex_buffer: &vertex_buf,
238                        first_vertex: 0,
239                        vertex_stride: mem::size_of::<Vertex>() as u64,
240                        index_buffer: Some(&index_buf),
241                        first_index: Some(0),
242                        transform_buffer: None,
243                        transform_buffer_offset: None,
244                    },
245                ]),
246            }),
247            iter::once(&tlas),
248        );
249
250        queue.submit(Some(encoder.finish()));
251
252        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
253            label: None,
254            layout: &bind_group_layout,
255            entries: &[
256                wgpu::BindGroupEntry {
257                    binding: 0,
258                    resource: uniform_buf.as_entire_binding(),
259                },
260                wgpu::BindGroupEntry {
261                    binding: 1,
262                    resource: tlas.as_binding(),
263                },
264            ],
265        });
266
267        let animation_timer = utils::AnimationTimer::default();
268
269        Example {
270            uniforms,
271            uniform_buf,
272            vertex_buf,
273            index_buf,
274            pipeline,
275            bind_group,
276            animation_timer,
277        }
278    }
279
280    fn update(&mut self, _event: winit::event::WindowEvent) {}
281
282    fn resize(
283        &mut self,
284        config: &wgpu::SurfaceConfiguration,
285        _device: &wgpu::Device,
286        queue: &wgpu::Queue,
287    ) {
288        self.uniforms = create_matrix(config);
289
290        queue.write_buffer(&self.uniform_buf, 0, bytemuck::cast_slice(&[self.uniforms]));
291        queue.submit(None);
292    }
293
294    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
295        //device.push_error_scope(wgpu::ErrorFilter::Validation);
296        const LIGHT_DISTANCE: f32 = 5.0;
297        const TIME_SCALE: f32 = -0.2;
298        const INITIAL_TIME: f32 = 1.0;
299        let time = self.animation_timer.time();
300        let cos = (time * TIME_SCALE + INITIAL_TIME).cos() * LIGHT_DISTANCE;
301        let sin = (time * TIME_SCALE + INITIAL_TIME).sin() * LIGHT_DISTANCE;
302
303        let mut encoder =
304            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
305
306        {
307            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
308                label: None,
309                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
310                    view,
311                    depth_slice: None,
312                    resolve_target: None,
313                    ops: wgpu::Operations {
314                        load: wgpu::LoadOp::Clear(wgpu::Color {
315                            r: 0.1,
316                            g: 0.1,
317                            b: 0.1,
318                            a: 1.0,
319                        }),
320                        store: wgpu::StoreOp::Store,
321                    },
322                })],
323                depth_stencil_attachment: None,
324                timestamp_writes: None,
325                occlusion_query_set: None,
326                multiview_mask: None,
327            });
328
329            rpass.set_pipeline(&self.pipeline);
330            rpass.set_bind_group(0, Some(&self.bind_group), &[]);
331            rpass.set_immediates(0, &0.0_f32.to_ne_bytes());
332            rpass.set_immediates(4, &cos.to_ne_bytes());
333            rpass.set_immediates(8, &sin.to_ne_bytes());
334            rpass.set_vertex_buffer(0, self.vertex_buf.slice(..));
335            rpass.set_index_buffer(self.index_buf.slice(..), IndexFormat::Uint16);
336            rpass.draw_indexed(0..12, 0, 0..1);
337        }
338        queue.submit(Some(encoder.finish()));
339        device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
340    }
341}
342
343pub fn main() {
344    crate::framework::run::<Example>("ray-shadows");
345}
346
347#[cfg(test)]
348#[wgpu_test::gpu_test]
349pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
350    name: "ray_shadows",
351    image_path: "/examples/features/src/ray_shadows/screenshot.png",
352    width: 1024,
353    height: 768,
354    optional_features: wgpu::Features::default(),
355    base_test_parameters: wgpu_test::TestParameters::default()
356        // https://github.com/gfx-rs/wgpu/issues/9100
357        .expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::METAL)),
358    comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],
359    _phantom: std::marker::PhantomData::<Example>,
360};