1use crate::utils;
2use bytemuck::{Pod, Zeroable};
3use glam::{Mat4, Quat, Vec3};
4use std::f32::consts::PI;
5use std::ops::IndexMut;
6use std::{borrow::Cow, iter, mem, ops::Range};
7use wgpu::util::DeviceExt;
8
9#[repr(C)]
11#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
12struct Vertex {
13 pos: [f32; 3],
14 _p0: [u32; 1],
15 normal: [f32; 3],
16 _p1: [u32; 1],
17 uv: [f32; 2],
18 _p2: [u32; 2],
19}
20
21#[repr(C)]
22#[derive(Clone, Copy, Pod, Zeroable)]
23struct Uniforms {
24 view_inverse: Mat4,
25 proj_inverse: Mat4,
26}
27
28#[derive(Debug, Clone, Default)]
29struct RawSceneComponents {
30 vertices: Vec<Vertex>,
31 indices: Vec<u32>,
32 geometries: Vec<(Range<usize>, Material)>, instances: Vec<(Range<usize>, Range<usize>)>, }
35
36struct SceneComponents {
37 vertices: wgpu::Buffer,
38 indices: wgpu::Buffer,
39 geometries: wgpu::Buffer,
40 instances: wgpu::Buffer,
41 bottom_level_acceleration_structures: Vec<wgpu::Blas>,
42}
43
44#[repr(C)]
45#[derive(Clone, Copy, Pod, Zeroable)]
46struct InstanceEntry {
47 first_vertex: u32,
48 first_geometry: u32,
49 last_geometry: u32,
50 _pad: u32,
51}
52
53#[repr(C)]
54#[derive(Clone, Copy, Pod, Zeroable, Default)]
55struct GeometryEntry {
56 first_index: u32,
57 _p0: [u32; 3],
58 material: Material,
59}
60
61#[repr(C)]
62#[derive(Clone, Copy, Pod, Zeroable, Default, Debug)]
63struct Material {
64 roughness_exponent: f32,
65 metalness: f32,
66 specularity: f32,
67 _p0: [u32; 1],
68 albedo: [f32; 3],
69 _p1: [u32; 1],
70}
71
72fn load_model(scene: &mut RawSceneComponents, path: &str) {
73 let path = env!("CARGO_MANIFEST_DIR").to_string() + "/src" + path;
74 println!("{path}");
75 let mut object = obj::Obj::load(path).unwrap();
76 object.load_mtls().unwrap();
77
78 let data = object.data;
79
80 let start_vertex_index = scene.vertices.len();
81 let start_geometry_index = scene.geometries.len();
82
83 let mut mapping = std::collections::HashMap::<(usize, Option<usize>, usize), usize>::new();
84
85 let mut next_index = 0;
86
87 for object in data.objects {
88 for group in object.groups {
89 let start_index_index = scene.indices.len();
90 for poly in group.polys {
91 for end_index in 2..poly.0.len() {
92 for &index in &[0, end_index - 1, end_index] {
93 let obj::IndexTuple(position_id, texture_id, normal_id) = poly.0[index];
94 let uv = texture_id
95 .map(|texture_id| data.texture[texture_id])
96 .unwrap_or_default();
97 let normal_id = normal_id.expect("normals required");
98
99 let index = *mapping
100 .entry((position_id, texture_id, normal_id))
101 .or_insert(next_index);
102 if index == next_index {
103 next_index += 1;
104
105 scene.vertices.push(Vertex {
106 pos: data.position[position_id],
107 uv,
108 normal: data.normal[normal_id],
109 ..Default::default()
110 })
111 }
112
113 scene.indices.push(index as u32);
114 }
115 }
116 }
117
118 let mut material: Material = Default::default();
119
120 if let Some(obj::ObjMaterial::Mtl(mat)) = group.material {
121 if let Some(kd) = mat.kd {
122 material.albedo = kd;
123 }
124 if let Some(ns) = mat.ns {
125 material.roughness_exponent = ns;
126 }
127 if let Some(ka) = mat.ka {
128 material.metalness = ka[0];
129 }
130 if let Some(ks) = mat.ks {
131 material.specularity = ks[0];
132 }
133 }
134
135 scene
136 .geometries
137 .push((start_index_index..scene.indices.len(), material));
138 }
139 }
140 scene.instances.push((
141 start_vertex_index..scene.vertices.len(),
142 start_geometry_index..scene.geometries.len(),
143 ));
144
145 }
150
151fn upload_scene_components(
152 device: &wgpu::Device,
153 queue: &wgpu::Queue,
154 scene: &RawSceneComponents,
155) -> SceneComponents {
156 let geometry_buffer_content = scene
157 .geometries
158 .iter()
159 .map(|(index_range, material)| GeometryEntry {
160 first_index: index_range.start as u32,
161 material: *material,
162 ..Default::default()
163 })
164 .collect::<Vec<_>>();
165
166 let instance_buffer_content = scene
167 .instances
168 .iter()
169 .map(|geometry| InstanceEntry {
170 first_vertex: geometry.0.start as u32,
171 first_geometry: geometry.1.start as u32,
172 last_geometry: geometry.1.end as u32,
173 _pad: 1,
174 })
175 .collect::<Vec<_>>();
176
177 let vertices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
178 label: Some("Vertices"),
179 contents: bytemuck::cast_slice(&scene.vertices),
180 usage: wgpu::BufferUsages::VERTEX
181 | wgpu::BufferUsages::STORAGE
182 | wgpu::BufferUsages::BLAS_INPUT,
183 });
184 let indices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
185 label: Some("Indices"),
186 contents: bytemuck::cast_slice(&scene.indices),
187 usage: wgpu::BufferUsages::INDEX
188 | wgpu::BufferUsages::STORAGE
189 | wgpu::BufferUsages::BLAS_INPUT,
190 });
191 let geometries = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
192 label: Some("Geometries"),
193 contents: bytemuck::cast_slice(&geometry_buffer_content),
194 usage: wgpu::BufferUsages::STORAGE,
195 });
196 let instances = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
197 label: Some("Instances"),
198 contents: bytemuck::cast_slice(&instance_buffer_content),
199 usage: wgpu::BufferUsages::STORAGE,
200 });
201
202 let (size_descriptors, bottom_level_acceleration_structures): (Vec<_>, Vec<_>) = scene
203 .instances
204 .iter()
205 .map(|(vertex_range, geometry_range)| {
206 let size_desc: Vec<wgpu::BlasTriangleGeometrySizeDescriptor> = (*geometry_range)
207 .clone()
208 .map(|i| wgpu::BlasTriangleGeometrySizeDescriptor {
209 vertex_format: wgpu::VertexFormat::Float32x3,
210 vertex_count: vertex_range.end as u32 - vertex_range.start as u32,
211 index_format: Some(wgpu::IndexFormat::Uint32),
212 index_count: Some(
213 scene.geometries[i].0.end as u32 - scene.geometries[i].0.start as u32,
214 ),
215 flags: wgpu::AccelerationStructureGeometryFlags::OPAQUE,
216 })
217 .collect();
218
219 let blas = device.create_blas(
220 &wgpu::CreateBlasDescriptor {
221 label: None,
222 flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,
223 update_mode: wgpu::AccelerationStructureUpdateMode::Build,
224 },
225 wgpu::BlasGeometrySizeDescriptors::Triangles {
226 descriptors: size_desc.clone(),
227 },
228 );
229 (size_desc, blas)
230 })
231 .unzip();
232
233 let build_entries: Vec<_> = scene
234 .instances
235 .iter()
236 .zip(size_descriptors.iter())
237 .zip(bottom_level_acceleration_structures.iter())
238 .map(|(((vertex_range, geometry_range), size_desc), blas)| {
239 let triangle_geometries: Vec<_> = size_desc
240 .iter()
241 .zip(geometry_range.clone())
242 .map(|(size, i)| wgpu::BlasTriangleGeometry {
243 size,
244 vertex_buffer: &vertices,
245 first_vertex: vertex_range.start as u32,
246 vertex_stride: mem::size_of::<Vertex>() as u64,
247 index_buffer: Some(&indices),
248 first_index: Some(scene.geometries[i].0.start as u32),
249 transform_buffer: None,
250 transform_buffer_offset: None,
251 })
252 .collect();
253
254 wgpu::BlasBuildEntry {
255 blas,
256 geometry: wgpu::BlasGeometries::TriangleGeometries(triangle_geometries),
257 }
258 })
259 .collect();
260
261 let mut encoder =
262 device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
263
264 encoder.build_acceleration_structures(build_entries.iter(), iter::empty());
265
266 queue.submit(Some(encoder.finish()));
267
268 SceneComponents {
269 vertices,
270 indices,
271 geometries,
272 instances,
273 bottom_level_acceleration_structures,
274 }
275}
276
277fn load_scene(device: &wgpu::Device, queue: &wgpu::Queue) -> SceneComponents {
278 let mut scene = RawSceneComponents::default();
279
280 load_model(&mut scene, "/skybox/models/rustacean-3d.obj");
281 load_model(&mut scene, "/ray_scene/cube.obj");
282
283 upload_scene_components(device, queue, &scene)
284}
285
286struct Example {
287 uniforms: Uniforms,
288 uniform_buf: wgpu::Buffer,
289 tlas: wgpu::Tlas,
290 pipeline: wgpu::RenderPipeline,
291 bind_group: wgpu::BindGroup,
292 scene_components: SceneComponents,
293 animation_timer: utils::AnimationTimer,
294}
295
296impl crate::framework::Example for Example {
297 fn required_features() -> wgpu::Features {
298 wgpu::Features::EXPERIMENTAL_RAY_QUERY
299 }
300
301 fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {
302 wgpu::DownlevelCapabilities {
303 flags: wgpu::DownlevelFlags::COMPUTE_SHADERS,
304 ..Default::default()
305 }
306 }
307
308 fn required_limits() -> wgpu::Limits {
309 wgpu::Limits::default().using_minimum_supported_acceleration_structure_values()
310 }
311
312 fn init(
313 config: &wgpu::SurfaceConfiguration,
314 _adapter: &wgpu::Adapter,
315 device: &wgpu::Device,
316 queue: &wgpu::Queue,
317 ) -> Self {
318 let side_count = 8;
319
320 let scene_components = load_scene(device, queue);
321
322 let uniforms = {
323 let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 2.5), Vec3::ZERO, Vec3::Y);
324 let proj = Mat4::perspective_rh(
325 59.0_f32.to_radians(),
326 config.width as f32 / config.height as f32,
327 0.001,
328 1000.0,
329 );
330
331 Uniforms {
332 view_inverse: view.inverse(),
333 proj_inverse: proj.inverse(),
334 }
335 };
336
337 let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
338 label: Some("Uniform Buffer"),
339 contents: bytemuck::cast_slice(&[uniforms]),
340 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
341 });
342
343 let tlas = device.create_tlas(&wgpu::CreateTlasDescriptor {
344 label: None,
345 flags: wgpu::AccelerationStructureFlags::PREFER_FAST_TRACE,
346 update_mode: wgpu::AccelerationStructureUpdateMode::Build,
347 max_instances: side_count * side_count,
348 });
349
350 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
351 label: None,
352 source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
353 });
354
355 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
356 label: None,
357 layout: None,
358 vertex: wgpu::VertexState {
359 module: &shader,
360 entry_point: Some("vs_main"),
361 compilation_options: Default::default(),
362 buffers: &[],
363 },
364 fragment: Some(wgpu::FragmentState {
365 module: &shader,
366 entry_point: Some("fs_main"),
367 compilation_options: Default::default(),
368 targets: &[Some(config.format.into())],
369 }),
370 primitive: wgpu::PrimitiveState {
371 topology: wgpu::PrimitiveTopology::TriangleList,
372 ..Default::default()
373 },
374 depth_stencil: None,
375 multisample: wgpu::MultisampleState::default(),
376 multiview_mask: None,
377 cache: None,
378 });
379
380 let bind_group_layout = pipeline.get_bind_group_layout(0);
381
382 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
383 label: None,
384 layout: &bind_group_layout,
385 entries: &[
386 wgpu::BindGroupEntry {
387 binding: 0,
388 resource: uniform_buf.as_entire_binding(),
389 },
390 wgpu::BindGroupEntry {
391 binding: 5,
392 resource: tlas.as_binding(),
393 },
394 wgpu::BindGroupEntry {
395 binding: 1,
396 resource: scene_components.vertices.as_entire_binding(),
397 },
398 wgpu::BindGroupEntry {
399 binding: 2,
400 resource: scene_components.indices.as_entire_binding(),
401 },
402 wgpu::BindGroupEntry {
403 binding: 3,
404 resource: scene_components.geometries.as_entire_binding(),
405 },
406 wgpu::BindGroupEntry {
407 binding: 4,
408 resource: scene_components.instances.as_entire_binding(),
409 },
410 ],
411 });
412
413 Example {
414 uniforms,
415 uniform_buf,
416 tlas,
417 pipeline,
418 bind_group,
419 scene_components,
420 animation_timer: utils::AnimationTimer::default(),
421 }
422 }
423
424 fn update(&mut self, _event: winit::event::WindowEvent) {}
425
426 fn resize(
427 &mut self,
428 config: &wgpu::SurfaceConfiguration,
429 _device: &wgpu::Device,
430 queue: &wgpu::Queue,
431 ) {
432 let proj = Mat4::perspective_rh(
433 59.0_f32.to_radians(),
434 config.width as f32 / config.height as f32,
435 0.001,
436 1000.0,
437 );
438
439 self.uniforms.proj_inverse = proj.inverse();
440
441 queue.write_buffer(&self.uniform_buf, 0, bytemuck::cast_slice(&[self.uniforms]));
442 }
443
444 fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
445 {
447 let dist = 3.5;
448
449 let side_count = 2;
450
451 let anim_time = self.animation_timer.time();
452
453 for x in 0..side_count {
454 for y in 0..side_count {
455 let instance = self.tlas.index_mut(x + y * side_count);
456
457 let blas_index = (x + y)
458 % self
459 .scene_components
460 .bottom_level_acceleration_structures
461 .len();
462
463 let x = x as f32 / (side_count - 1) as f32;
464 let y = y as f32 / (side_count - 1) as f32;
465 let x = x * 2.0 - 1.0;
466 let y = y * 2.0 - 1.0;
467
468 let transform = Mat4::from_rotation_translation(
469 Quat::from_euler(
470 glam::EulerRot::XYZ,
471 anim_time * 0.5 * 0.342,
472 anim_time * 0.5 * 0.254,
473 anim_time * 0.5 * 0.832 + PI,
474 ),
475 Vec3 {
476 x: x * dist,
477 y: y * dist,
478 z: -14.0,
479 },
480 );
481 let transform = transform.transpose().to_cols_array()[..12]
482 .try_into()
483 .unwrap();
484 *instance = Some(wgpu::TlasInstance::new(
485 &self.scene_components.bottom_level_acceleration_structures[blas_index],
486 transform,
487 blas_index as u32,
488 0xff,
489 ));
490 }
491 }
492 }
493
494 let mut encoder =
495 device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
496
497 encoder.build_acceleration_structures(iter::empty(), iter::once(&self.tlas));
498
499 {
500 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
501 label: None,
502 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
503 view,
504 depth_slice: None,
505 resolve_target: None,
506 ops: wgpu::Operations {
507 load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
508 store: wgpu::StoreOp::Store,
509 },
510 })],
511 depth_stencil_attachment: None,
512 timestamp_writes: None,
513 occlusion_query_set: None,
514 multiview_mask: None,
515 });
516
517 rpass.set_pipeline(&self.pipeline);
518 rpass.set_bind_group(0, Some(&self.bind_group), &[]);
519 rpass.draw(0..3, 0..1);
520 }
521
522 queue.submit(Some(encoder.finish()));
523 }
524}
525
526pub fn main() {
527 crate::framework::run::<Example>("ray_scene");
528}
529
530#[cfg(test)]
531#[wgpu_test::gpu_test]
532pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
533 name: "ray_scene",
534 image_path: "/examples/features/src/ray_scene/screenshot.png",
535 width: 1024,
536 height: 768,
537 optional_features: wgpu::Features::default(),
538 base_test_parameters: wgpu_test::TestParameters::default()
539 .expect_fail(wgpu_test::FailureCase::backend(wgpu::Backends::METAL)),
541 comparisons: &[wgpu_test::ComparisonType::Mean(0.02)],
542 _phantom: std::marker::PhantomData::<Example>,
543};