1mod point_gen;
2
3use bytemuck::{Pod, Zeroable};
4use glam::Vec3;
5use nanorand::{Rng, WyRand};
6use std::{f32::consts, iter};
7use wgpu::util::DeviceExt;
8
9const SIZE: f32 = 29.0;
18
19const CAMERA: Vec3 = glam::Vec3::new(-200.0, 70.0, 200.0);
24
25struct Matrices {
26 view: glam::Mat4,
27 flipped_view: glam::Mat4,
28 projection: glam::Mat4,
29}
30
31#[repr(C)]
32#[derive(Copy, Clone, Debug, PartialEq, Pod, Zeroable)]
33struct TerrainUniforms {
34 view_projection: [f32; 16],
35 clipping_plane: [f32; 4],
36}
37
38#[repr(C)]
39#[derive(Copy, Clone, Debug, PartialEq, Pod, Zeroable)]
40struct WaterUniforms {
41 view: [f32; 16],
42 projection: [f32; 16],
43 time_size_width: [f32; 4],
44 height: [f32; 4],
45}
46
47struct Uniforms {
48 terrain_normal: TerrainUniforms,
49 terrain_flipped: TerrainUniforms,
50 water: WaterUniforms,
51}
52
53struct Example {
54 water_vertex_buf: wgpu::Buffer,
55 water_vertex_count: usize,
56 water_bind_group_layout: wgpu::BindGroupLayout,
57 water_bind_group: wgpu::BindGroup,
58 water_uniform_buf: wgpu::Buffer,
59 water_pipeline: wgpu::RenderPipeline,
60
61 terrain_vertex_buf: wgpu::Buffer,
62 terrain_vertex_count: usize,
63 terrain_normal_bind_group: wgpu::BindGroup,
64 terrain_normal_uniform_buf: wgpu::Buffer,
65 terrain_flipped_uniform_buf: wgpu::Buffer,
70 terrain_pipeline: wgpu::RenderPipeline,
71
72 terrain_bundle: wgpu::RenderBundle,
78
79 reflect_view: wgpu::TextureView,
80
81 depth_buffer: wgpu::TextureView,
82
83 current_frame: usize,
84
85 active: Option<usize>,
90}
91
92impl Example {
93 fn generate_matrices(aspect_ratio: f32) -> Matrices {
97 let projection = glam::Mat4::perspective_rh(consts::FRAC_PI_4, aspect_ratio, 10.0, 400.0);
98 let reg_view = glam::Mat4::look_at_rh(
99 CAMERA,
100 glam::Vec3::new(0f32, 0.0, 0.0),
101 glam::Vec3::Y, );
103
104 let scale = glam::Mat4::from_scale(glam::Vec3::new(8.0, 1.5, 8.0));
105
106 let reg_view = reg_view * scale;
107
108 let flipped_view = glam::Mat4::look_at_rh(
109 glam::Vec3::new(CAMERA.x, -CAMERA.y, CAMERA.z),
110 glam::Vec3::ZERO,
111 glam::Vec3::Y,
112 );
113
114 let flipped_view = flipped_view * scale;
115
116 Matrices {
117 view: reg_view,
118 flipped_view,
119 projection,
120 }
121 }
122
123 fn generate_uniforms(width: u32, height: u32) -> Uniforms {
124 let Matrices {
125 view,
126 flipped_view,
127 projection,
128 } = Self::generate_matrices(width as f32 / height as f32);
129
130 Uniforms {
131 terrain_normal: TerrainUniforms {
132 view_projection: *(projection * view).as_ref(),
133 clipping_plane: [0.0; 4],
134 },
135 terrain_flipped: TerrainUniforms {
136 view_projection: *(projection * flipped_view).as_ref(),
137 clipping_plane: [0., 1., 0., 0.],
138 },
139 water: WaterUniforms {
140 view: *view.as_ref(),
141 projection: *projection.as_ref(),
142 time_size_width: [0.0, 1.0, SIZE * 2.0, width as f32],
143 height: [height as f32, 0.0, 0.0, 0.0],
144 },
145 }
146 }
147
148 fn initialize_resources(
152 config: &wgpu::SurfaceConfiguration,
153 device: &wgpu::Device,
154 queue: &wgpu::Queue,
155 water_uniforms: &wgpu::Buffer,
156 terrain_normal_uniforms: &wgpu::Buffer,
157 terrain_flipped_uniforms: &wgpu::Buffer,
158 water_bind_group_layout: &wgpu::BindGroupLayout,
159 ) -> (wgpu::TextureView, wgpu::TextureView, wgpu::BindGroup) {
160 let Uniforms {
163 terrain_normal,
164 terrain_flipped,
165 water,
166 } = Self::generate_uniforms(config.width, config.height);
167
168 queue.write_buffer(
170 terrain_normal_uniforms,
171 0,
172 bytemuck::cast_slice(&[terrain_normal]),
173 );
174 queue.write_buffer(
175 terrain_flipped_uniforms,
176 0,
177 bytemuck::cast_slice(&[terrain_flipped]),
178 );
179 queue.write_buffer(water_uniforms, 0, bytemuck::cast_slice(&[water]));
180
181 let texture_extent = wgpu::Extent3d {
182 width: config.width,
183 height: config.height,
184 depth_or_array_layers: 1,
185 };
186
187 let reflection_texture = device.create_texture(&wgpu::TextureDescriptor {
188 label: Some("Reflection Render Texture"),
189 size: texture_extent,
190 mip_level_count: 1,
191 sample_count: 1,
192 dimension: wgpu::TextureDimension::D2,
193 format: config.view_formats[0],
194 usage: wgpu::TextureUsages::TEXTURE_BINDING
195 | wgpu::TextureUsages::COPY_DST
196 | wgpu::TextureUsages::RENDER_ATTACHMENT,
197 view_formats: &[],
198 });
199
200 let draw_depth_buffer = device.create_texture(&wgpu::TextureDescriptor {
201 label: Some("Depth Buffer"),
202 size: texture_extent,
203 mip_level_count: 1,
204 sample_count: 1,
205 dimension: wgpu::TextureDimension::D2,
206 format: wgpu::TextureFormat::Depth32Float,
207 usage: wgpu::TextureUsages::TEXTURE_BINDING
208 | wgpu::TextureUsages::COPY_DST
209 | wgpu::TextureUsages::RENDER_ATTACHMENT,
210 view_formats: &[],
211 });
212
213 let color_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
214 label: Some("Color Sampler"),
215 address_mode_u: wgpu::AddressMode::ClampToEdge,
216 address_mode_v: wgpu::AddressMode::ClampToEdge,
217 address_mode_w: wgpu::AddressMode::ClampToEdge,
218 mag_filter: wgpu::FilterMode::Nearest,
219 min_filter: wgpu::FilterMode::Linear,
220 mipmap_filter: wgpu::MipmapFilterMode::Nearest,
221 ..Default::default()
222 });
223
224 let depth_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
225 label: Some("Depth Sampler"),
226 ..Default::default()
227 });
228
229 let depth_view = draw_depth_buffer.create_view(&wgpu::TextureViewDescriptor::default());
230
231 let water_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
232 layout: water_bind_group_layout,
233 entries: &[
234 wgpu::BindGroupEntry {
235 binding: 0,
236 resource: water_uniforms.as_entire_binding(),
237 },
238 wgpu::BindGroupEntry {
239 binding: 1,
240 resource: wgpu::BindingResource::TextureView(
241 &reflection_texture.create_view(&wgpu::TextureViewDescriptor::default()),
242 ),
243 },
244 wgpu::BindGroupEntry {
245 binding: 2,
246 resource: wgpu::BindingResource::TextureView(&depth_view),
247 },
248 wgpu::BindGroupEntry {
249 binding: 3,
250 resource: wgpu::BindingResource::Sampler(&color_sampler),
251 },
252 wgpu::BindGroupEntry {
253 binding: 4,
254 resource: wgpu::BindingResource::Sampler(&depth_sampler),
255 },
256 ],
257 label: Some("Water Bind Group"),
258 });
259
260 (
261 reflection_texture.create_view(&wgpu::TextureViewDescriptor::default()),
262 depth_view,
263 water_bind_group,
264 )
265 }
266}
267
268impl crate::framework::Example for Example {
269 fn init(
270 config: &wgpu::SurfaceConfiguration,
271 _adapter: &wgpu::Adapter,
272 device: &wgpu::Device,
273 queue: &wgpu::Queue,
274 ) -> Self {
275 let water_vertex_size = size_of::<point_gen::WaterVertexAttributes>();
277
278 let water_vertices = point_gen::HexWaterMesh::generate(SIZE).generate_points();
279
280 let terrain_vertex_size = size_of::<point_gen::TerrainVertexAttributes>();
282
283 let terrain_noise = noise::OpenSimplex::default();
285
286 let mut terrain_random = WyRand::new_seed(42);
288
289 let terrain =
291 point_gen::HexTerrainMesh::generate(SIZE, |point| -> point_gen::TerrainVertex {
292 use noise::NoiseFn;
293 let noise = terrain_noise.get([point[0] as f64 / 5.0, point[1] as f64 / 5.0]) + 0.1;
294
295 let y = noise as f32 * 22.0;
296
297 fn mul_arr(mut arr: [u8; 4], by: f32) -> [u8; 4] {
299 arr[0] = (arr[0] as f32 * by).min(255.0) as u8;
300 arr[1] = (arr[1] as f32 * by).min(255.0) as u8;
301 arr[2] = (arr[2] as f32 * by).min(255.0) as u8;
302 arr
303 }
304
305 const DARK_SAND: [u8; 4] = [235, 175, 71, 255];
307 const SAND: [u8; 4] = [217, 191, 76, 255];
309 const GRASS: [u8; 4] = [122, 170, 19, 255];
311 const SNOW: [u8; 4] = [175, 224, 237, 255];
313
314 let random = terrain_random.generate::<f32>() * 0.2 + 0.9;
316
317 let colour = if y <= 0.0 {
319 DARK_SAND
320 } else if y <= 0.8 {
321 SAND
322 } else if y <= 10.0 {
323 GRASS
324 } else {
325 SNOW
326 };
327 point_gen::TerrainVertex {
328 position: Vec3::new(point[0], y, point[1]),
329 colour: mul_arr(colour, random),
330 }
331 });
332
333 let terrain_vertices = terrain.make_buffer_data();
335
336 let water_vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
338 label: Some("Water vertices"),
339 contents: bytemuck::cast_slice(&water_vertices),
340 usage: wgpu::BufferUsages::VERTEX,
341 });
342
343 let terrain_vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
344 label: Some("Terrain vertices"),
345 contents: bytemuck::cast_slice(&terrain_vertices),
346 usage: wgpu::BufferUsages::VERTEX,
347 });
348
349 let water_bind_group_layout =
351 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
352 label: Some("Water Bind Group Layout"),
353 entries: &[
354 wgpu::BindGroupLayoutEntry {
356 binding: 0,
357 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
358 ty: wgpu::BindingType::Buffer {
359 ty: wgpu::BufferBindingType::Uniform,
360 has_dynamic_offset: false,
361 min_binding_size: wgpu::BufferSize::new(
362 size_of::<WaterUniforms>() as _,
363 ),
364 },
365 count: None,
366 },
367 wgpu::BindGroupLayoutEntry {
369 binding: 1,
370 visibility: wgpu::ShaderStages::FRAGMENT,
371 ty: wgpu::BindingType::Texture {
372 multisampled: false,
373 sample_type: wgpu::TextureSampleType::Float { filterable: true },
374 view_dimension: wgpu::TextureViewDimension::D2,
375 },
376 count: None,
377 },
378 wgpu::BindGroupLayoutEntry {
380 binding: 2,
381 visibility: wgpu::ShaderStages::FRAGMENT,
382 ty: wgpu::BindingType::Texture {
383 multisampled: false,
384 sample_type: wgpu::TextureSampleType::Float { filterable: false },
385 view_dimension: wgpu::TextureViewDimension::D2,
386 },
387 count: None,
388 },
389 wgpu::BindGroupLayoutEntry {
391 binding: 3,
392 visibility: wgpu::ShaderStages::FRAGMENT,
393 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
394 count: None,
395 },
396 wgpu::BindGroupLayoutEntry {
398 binding: 4,
399 visibility: wgpu::ShaderStages::FRAGMENT,
400 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
401 count: None,
402 },
403 ],
404 });
405
406 let terrain_bind_group_layout =
407 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
408 label: Some("Terrain Bind Group Layout"),
409 entries: &[
410 wgpu::BindGroupLayoutEntry {
412 binding: 0,
413 visibility: wgpu::ShaderStages::VERTEX,
414 ty: wgpu::BindingType::Buffer {
415 ty: wgpu::BufferBindingType::Uniform,
416 has_dynamic_offset: false,
417 min_binding_size: wgpu::BufferSize::new(
418 size_of::<TerrainUniforms>() as _
419 ),
420 },
421 count: None,
422 },
423 ],
424 });
425
426 let water_pipeline_layout =
428 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
429 label: Some("water"),
430 bind_group_layouts: &[Some(&water_bind_group_layout)],
431 immediate_size: 0,
432 });
433
434 let terrain_pipeline_layout =
435 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
436 label: Some("terrain"),
437 bind_group_layouts: &[Some(&terrain_bind_group_layout)],
438 immediate_size: 0,
439 });
440
441 let water_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
442 label: Some("Water Uniforms"),
443 size: size_of::<WaterUniforms>() as _,
444 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
445 mapped_at_creation: false,
446 });
447
448 let terrain_normal_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
449 label: Some("Normal Terrain Uniforms"),
450 size: size_of::<TerrainUniforms>() as _,
451 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
452 mapped_at_creation: false,
453 });
454
455 let terrain_flipped_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
456 label: Some("Flipped Terrain Uniforms"),
457 size: size_of::<TerrainUniforms>() as _,
458 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
459 mapped_at_creation: false,
460 });
461
462 let (reflect_view, depth_buffer, water_bind_group) = Self::initialize_resources(
466 config,
467 device,
468 queue,
469 &water_uniform_buf,
470 &terrain_normal_uniform_buf,
471 &terrain_flipped_uniform_buf,
472 &water_bind_group_layout,
473 );
474
475 let terrain_normal_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
476 layout: &terrain_bind_group_layout,
477 entries: &[wgpu::BindGroupEntry {
478 binding: 0,
479 resource: terrain_normal_uniform_buf.as_entire_binding(),
480 }],
481 label: Some("Terrain Normal Bind Group"),
482 });
483
484 let terrain_flipped_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
487 layout: &terrain_bind_group_layout,
488 entries: &[wgpu::BindGroupEntry {
489 binding: 0,
490 resource: terrain_flipped_uniform_buf.as_entire_binding(),
491 }],
492 label: Some("Terrain Flipped Bind Group"),
493 });
494
495 let terrain_module = device.create_shader_module(wgpu::include_wgsl!("terrain.wgsl"));
497 let water_module = device.create_shader_module(wgpu::include_wgsl!("water.wgsl"));
498
499 let water_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
502 label: Some("water"),
503 layout: Some(&water_pipeline_layout),
505 vertex: wgpu::VertexState {
507 module: &water_module,
508 entry_point: Some("vs_main"),
509 compilation_options: Default::default(),
510 buffers: &[wgpu::VertexBufferLayout {
516 array_stride: water_vertex_size as wgpu::BufferAddress,
517 step_mode: wgpu::VertexStepMode::Vertex,
518 attributes: &wgpu::vertex_attr_array![0 => Sint16x2, 1 => Sint8x4],
519 }],
520 },
521 fragment: Some(wgpu::FragmentState {
523 module: &water_module,
524 entry_point: Some("fs_main"),
525 compilation_options: Default::default(),
526 targets: &[Some(wgpu::ColorTargetState {
529 format: config.view_formats[0],
530 blend: Some(wgpu::BlendState {
531 color: wgpu::BlendComponent {
532 src_factor: wgpu::BlendFactor::SrcAlpha,
533 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
534 operation: wgpu::BlendOperation::Add,
535 },
536 alpha: wgpu::BlendComponent {
537 src_factor: wgpu::BlendFactor::One,
538 dst_factor: wgpu::BlendFactor::One,
539 operation: wgpu::BlendOperation::Max,
540 },
541 }),
542 write_mask: wgpu::ColorWrites::ALL,
543 })],
544 }),
545 primitive: wgpu::PrimitiveState {
549 topology: wgpu::PrimitiveTopology::TriangleList,
551 front_face: wgpu::FrontFace::Cw,
552 ..Default::default()
553 },
554 depth_stencil: Some(wgpu::DepthStencilState {
561 format: wgpu::TextureFormat::Depth32Float,
562 depth_write_enabled: Some(false),
563 depth_compare: Some(wgpu::CompareFunction::Less),
564 stencil: wgpu::StencilState::default(),
565 bias: wgpu::DepthBiasState::default(),
566 }),
567 multisample: wgpu::MultisampleState::default(),
569 multiview_mask: None,
570 cache: None,
572 });
573
574 let terrain_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
576 label: Some("terrain"),
577 layout: Some(&terrain_pipeline_layout),
578 vertex: wgpu::VertexState {
579 module: &terrain_module,
580 entry_point: Some("vs_main"),
581 compilation_options: Default::default(),
582 buffers: &[wgpu::VertexBufferLayout {
583 array_stride: terrain_vertex_size as wgpu::BufferAddress,
584 step_mode: wgpu::VertexStepMode::Vertex,
585 attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3, 2 => Unorm8x4],
586 }],
587 },
588 fragment: Some(wgpu::FragmentState {
589 module: &terrain_module,
590 entry_point: Some("fs_main"),
591 compilation_options: Default::default(),
592 targets: &[Some(config.view_formats[0].into())],
593 }),
594 primitive: wgpu::PrimitiveState {
595 front_face: wgpu::FrontFace::Ccw,
596 cull_mode: Some(wgpu::Face::Front),
597 ..Default::default()
598 },
599 depth_stencil: Some(wgpu::DepthStencilState {
600 format: wgpu::TextureFormat::Depth32Float,
601 depth_write_enabled: Some(true),
602 depth_compare: Some(wgpu::CompareFunction::Less),
603 stencil: wgpu::StencilState::default(),
604 bias: wgpu::DepthBiasState::default(),
605 }),
606 multisample: wgpu::MultisampleState::default(),
607 multiview_mask: None,
608 cache: None
609 });
610
611 let terrain_bundle = {
613 let mut encoder =
614 device.create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor {
615 label: None,
616 color_formats: &[Some(config.view_formats[0])],
617 depth_stencil: Some(wgpu::RenderBundleDepthStencil {
618 format: wgpu::TextureFormat::Depth32Float,
619 depth_read_only: false,
620 stencil_read_only: true,
621 }),
622 sample_count: 1,
623 multiview: None,
624 });
625 encoder.set_pipeline(&terrain_pipeline);
626 encoder.set_bind_group(0, &terrain_flipped_bind_group, &[]);
627 encoder.set_vertex_buffer(0, terrain_vertex_buf.slice(..));
628 encoder.draw(0..terrain_vertices.len() as u32, 0..1);
629 encoder.finish(&wgpu::RenderBundleDescriptor::default())
630 };
631
632 Example {
634 water_vertex_buf,
635 water_vertex_count: water_vertices.len(),
636 water_bind_group_layout,
637 water_bind_group,
638 water_uniform_buf,
639 water_pipeline,
640
641 terrain_vertex_buf,
642 terrain_vertex_count: terrain_vertices.len(),
643 terrain_normal_bind_group,
644 terrain_normal_uniform_buf,
645 terrain_flipped_uniform_buf,
646 terrain_pipeline,
647 terrain_bundle,
648
649 reflect_view,
650
651 depth_buffer,
652
653 current_frame: 0,
654
655 active: Some(0),
656 }
657 }
658
659 fn update(&mut self, _event: winit::event::WindowEvent) {
660 }
662
663 fn resize(
664 &mut self,
665 config: &wgpu::SurfaceConfiguration,
666 device: &wgpu::Device,
667 queue: &wgpu::Queue,
668 ) {
669 if config.width == 0 && config.height == 0 {
670 self.active = None;
672 return;
673 }
674 self.active = Some(self.current_frame);
675
676 let (reflect_view, depth_buffer, water_bind_group) = Self::initialize_resources(
679 config,
680 device,
681 queue,
682 &self.water_uniform_buf,
683 &self.terrain_normal_uniform_buf,
684 &self.terrain_flipped_uniform_buf,
685 &self.water_bind_group_layout,
686 );
687 self.water_bind_group = water_bind_group;
688
689 self.depth_buffer = depth_buffer;
690 self.reflect_view = reflect_view;
691 }
692
693 fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
694 self.current_frame += 1;
696 #[expect(clippy::eq_op, reason = "keeping common divisor on all elements")]
697 let back_color = wgpu::Color {
698 r: 161.0 / 255.0,
699 g: 246.0 / 255.0,
700 b: 255.0 / 255.0,
701 a: 1.0,
702 };
703
704 let (water_sin, water_cos) = ((self.current_frame as f32) / 600.0).sin_cos();
706 queue.write_buffer(
707 &self.water_uniform_buf,
708 size_of::<[f32; 16]>() as wgpu::BufferAddress * 2,
709 bytemuck::cast_slice(&[water_sin, water_cos]),
710 );
711
712 if let Some(active) = self.active {
714 if active >= self.current_frame {
715 return;
716 }
717 } else {
718 return;
719 }
720
721 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
724 label: Some("Main Command Encoder"),
725 });
726
727 {
729 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
730 label: None,
731 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
732 view: &self.reflect_view,
733 depth_slice: None,
734 resolve_target: None,
735 ops: wgpu::Operations {
736 load: wgpu::LoadOp::Clear(back_color),
737 store: wgpu::StoreOp::Store,
738 },
739 })],
740 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
743 view: &self.depth_buffer,
744 depth_ops: Some(wgpu::Operations {
745 load: wgpu::LoadOp::Clear(1.0),
746 store: wgpu::StoreOp::Store,
747 }),
748 stencil_ops: None,
749 }),
750 timestamp_writes: None,
751 occlusion_query_set: None,
752 multiview_mask: None,
753 });
754
755 rpass.execute_bundles([&self.terrain_bundle]);
756 }
757 {
760 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
761 label: None,
762 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
763 view,
764 depth_slice: None,
765 resolve_target: None,
766 ops: wgpu::Operations {
767 load: wgpu::LoadOp::Clear(back_color),
768 store: wgpu::StoreOp::Store,
769 },
770 })],
771 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
772 view: &self.depth_buffer,
773 depth_ops: Some(wgpu::Operations {
774 load: wgpu::LoadOp::Clear(1.0),
775 store: wgpu::StoreOp::Store,
776 }),
777 stencil_ops: None,
778 }),
779 timestamp_writes: None,
780 occlusion_query_set: None,
781 multiview_mask: None,
782 });
783 rpass.set_pipeline(&self.terrain_pipeline);
784 rpass.set_bind_group(0, &self.terrain_normal_bind_group, &[]);
785 rpass.set_vertex_buffer(0, self.terrain_vertex_buf.slice(..));
786 rpass.draw(0..self.terrain_vertex_count as u32, 0..1);
787 }
788 {
791 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
792 label: None,
793 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
794 view,
795 depth_slice: None,
796 resolve_target: None,
797 ops: wgpu::Operations {
798 load: wgpu::LoadOp::Load,
799 store: wgpu::StoreOp::Store,
800 },
801 })],
802 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
803 view: &self.depth_buffer,
804 depth_ops: None,
805 stencil_ops: None,
806 }),
807 timestamp_writes: None,
808 occlusion_query_set: None,
809 multiview_mask: None,
810 });
811
812 rpass.set_pipeline(&self.water_pipeline);
813 rpass.set_bind_group(0, &self.water_bind_group, &[]);
814 rpass.set_vertex_buffer(0, self.water_vertex_buf.slice(..));
815 rpass.draw(0..self.water_vertex_count as u32, 0..1);
816 }
817
818 queue.submit(iter::once(encoder.finish()));
819 }
820}
821
822pub fn main() {
823 crate::framework::run::<Example>("water");
824}
825
826#[cfg(test)]
827#[wgpu_test::gpu_test]
828pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
829 name: "water",
830 image_path: "/examples/features/src/water/screenshot.png",
831 width: 1024,
832 height: 768,
833 optional_features: wgpu::Features::default(),
834 base_test_parameters: wgpu_test::TestParameters::default()
835 .downlevel_flags(wgpu::DownlevelFlags::READ_ONLY_DEPTH_STENCIL)
836 .expect_fail(wgpu_test::FailureCase {
838 backends: Some(wgpu::Backends::VULKAN),
839 reasons: vec![wgpu_test::FailureReason::validation_error()
840 .with_message("WRITE_AFTER_WRITE hazard detected.")],
841 behavior: wgpu_test::FailureBehavior::AssertFailure,
842 ..Default::default()
843 }),
844 comparisons: &[wgpu_test::ComparisonType::Mean(0.018)], _phantom: std::marker::PhantomData::<Example>,
846};