wgpu_examples/water/
mod.rs

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
9///
10/// Radius of the terrain.
11///
12/// Changing this value will change the size of the
13/// water and terrain. Note however, that changes to
14/// this value will require modification of the time
15/// scale in the `render` method below.
16///
17const SIZE: f32 = 29.0;
18
19///
20/// Location of the camera.
21/// Location of light is in terrain/water shaders.
22///
23const 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    ///
66    /// Contains uniform variables where the camera
67    /// has been placed underwater.
68    ///
69    terrain_flipped_uniform_buf: wgpu::Buffer,
70    terrain_pipeline: wgpu::RenderPipeline,
71
72    /// A render bundle for drawing the terrain.
73    ///
74    /// This isn't really necessary, but it does make sure we have at
75    /// least one use of `RenderBundleEncoder::set_bind_group` among
76    /// the examples.
77    terrain_bundle: wgpu::RenderBundle,
78
79    reflect_view: wgpu::TextureView,
80
81    depth_buffer: wgpu::TextureView,
82
83    current_frame: usize,
84
85    ///
86    /// Used to prevent issues when rendering after
87    /// minimizing the window.
88    ///
89    active: Option<usize>,
90}
91
92impl Example {
93    ///
94    /// Creates the view matrices, and the corrected projection matrix.
95    ///
96    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, //Note that y is up. Differs from other examples.
102        );
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    ///
149    /// Initializes Uniforms and textures.
150    ///
151    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        // Matrices for our projection and view.
161        // flipped_view is the view from under the water.
162        let Uniforms {
163            terrain_normal,
164            terrain_flipped,
165            water,
166        } = Self::generate_uniforms(config.width, config.height);
167
168        // Put the uniforms into buffers on the GPU
169        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::FilterMode::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        // Size of one water vertex
276        let water_vertex_size = size_of::<point_gen::WaterVertexAttributes>();
277
278        let water_vertices = point_gen::HexWaterMesh::generate(SIZE).generate_points();
279
280        // Size of one terrain vertex
281        let terrain_vertex_size = size_of::<point_gen::TerrainVertexAttributes>();
282
283        // Noise generation
284        let terrain_noise = noise::OpenSimplex::default();
285
286        // Random colouration
287        let mut terrain_random = WyRand::new_seed(42);
288
289        // Generate terrain. The closure determines what each hexagon will look like.
290        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                // Multiplies a colour by some random amount.
298                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                // Under water
306                const DARK_SAND: [u8; 4] = [235, 175, 71, 255];
307                // Coast
308                const SAND: [u8; 4] = [217, 191, 76, 255];
309                // Normal
310                const GRASS: [u8; 4] = [122, 170, 19, 255];
311                // Mountain
312                const SNOW: [u8; 4] = [175, 224, 237, 255];
313
314                // Random colouration.
315                let random = terrain_random.generate::<f32>() * 0.2 + 0.9;
316
317                // Choose colour.
318                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        // Generate the buffer data.
334        let terrain_vertices = terrain.make_buffer_data();
335
336        // Create the buffers on the GPU to hold the data.
337        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        // Create the bind group layout. This is what our uniforms will look like.
350        let water_bind_group_layout =
351            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
352                label: Some("Water Bind Group Layout"),
353                entries: &[
354                    // Uniform variables such as projection/view.
355                    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                    // Reflection texture.
368                    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                    // Depth texture for terrain.
379                    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                    // Sampler to be able to sample the textures.
390                    wgpu::BindGroupLayoutEntry {
391                        binding: 3,
392                        visibility: wgpu::ShaderStages::FRAGMENT,
393                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
394                        count: None,
395                    },
396                    // Sampler to be able to sample the textures.
397                    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                    // Regular uniform variables like view/projection.
411                    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        // Create our pipeline layouts.
427        let water_pipeline_layout =
428            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
429                label: Some("water"),
430                bind_group_layouts: &[&water_bind_group_layout],
431                push_constant_ranges: &[],
432            });
433
434        let terrain_pipeline_layout =
435            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
436                label: Some("terrain"),
437                bind_group_layouts: &[&terrain_bind_group_layout],
438                push_constant_ranges: &[],
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        // Create bind group.
463        // This puts values behind what was laid out in the bind group layout.
464
465        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        // Binds to the uniform buffer where the
485        // camera has been placed underwater.
486        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        // Upload/compile them to GPU code.
496        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        // Create the render pipelines. These describe how the data will flow through the GPU, and what
500        // constraints and modifiers it will have.
501        let water_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
502            label: Some("water"),
503            // The "layout" is what uniforms will be needed.
504            layout: Some(&water_pipeline_layout),
505            // Vertex shader and input buffers
506            vertex: wgpu::VertexState {
507                module: &water_module,
508                entry_point: Some("vs_main"),
509                compilation_options: Default::default(),
510                // Layout of our vertices. This should match the structs
511                // which are uploaded to the GPU. This should also be
512                // ensured by tagging on either a `#[repr(C)]` onto a
513                // struct, or a `#[repr(transparent)]` if it only contains
514                // one item, which is itself `repr(C)`.
515                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 shader and output targets
522            fragment: Some(wgpu::FragmentState {
523                module: &water_module,
524                entry_point: Some("fs_main"),
525                compilation_options: Default::default(),
526                // Describes how the colour will be interpolated
527                // and assigned to the output attachment.
528                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            // How the triangles will be rasterized. This is more important
546            // for the terrain because of the beneath-the water shot.
547            // This is also dependent on how the triangles are being generated.
548            primitive: wgpu::PrimitiveState {
549                // What kind of data are we passing in?
550                topology: wgpu::PrimitiveTopology::TriangleList,
551                front_face: wgpu::FrontFace::Cw,
552                ..Default::default()
553            },
554            // Describes how us writing to the depth/stencil buffer
555            // will work. Since this is water, we need to read from the
556            // depth buffer both as a texture in the shader, and as an
557            // input attachment to do depth-testing. We don't write, so
558            // depth_write_enabled is set to false. This is called
559            // RODS or read-only depth stencil.
560            depth_stencil: Some(wgpu::DepthStencilState {
561                // We don't use stencil.
562                format: wgpu::TextureFormat::Depth32Float,
563                depth_write_enabled: false,
564                depth_compare: wgpu::CompareFunction::Less,
565                stencil: wgpu::StencilState::default(),
566                bias: wgpu::DepthBiasState::default(),
567            }),
568            // No multisampling is used.
569            multisample: wgpu::MultisampleState::default(),
570            multiview: None,
571            // No pipeline caching is used
572            cache: None,
573        });
574
575        // Same idea as the water pipeline.
576        let terrain_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
577            label: Some("terrain"),
578            layout: Some(&terrain_pipeline_layout),
579            vertex: wgpu::VertexState {
580                module: &terrain_module,
581                entry_point: Some("vs_main"),
582                compilation_options: Default::default(),
583                buffers: &[wgpu::VertexBufferLayout {
584                    array_stride: terrain_vertex_size as wgpu::BufferAddress,
585                    step_mode: wgpu::VertexStepMode::Vertex,
586                    attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3, 2 => Unorm8x4],
587                }],
588            },
589            fragment: Some(wgpu::FragmentState {
590                module: &terrain_module,
591                entry_point: Some("fs_main"),
592                compilation_options: Default::default(),
593                targets: &[Some(config.view_formats[0].into())],
594            }),
595            primitive: wgpu::PrimitiveState {
596                front_face: wgpu::FrontFace::Ccw,
597                cull_mode: Some(wgpu::Face::Front),
598                ..Default::default()
599            },
600            depth_stencil: Some(wgpu::DepthStencilState {
601                format: wgpu::TextureFormat::Depth32Float,
602                depth_write_enabled: true,
603                depth_compare: wgpu::CompareFunction::Less,
604                stencil: wgpu::StencilState::default(),
605                bias: wgpu::DepthBiasState::default(),
606            }),
607            multisample: wgpu::MultisampleState::default(),
608            multiview: None,
609            cache: None
610        });
611
612        // A render bundle to draw the terrain.
613        let terrain_bundle = {
614            let mut encoder =
615                device.create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor {
616                    label: None,
617                    color_formats: &[Some(config.view_formats[0])],
618                    depth_stencil: Some(wgpu::RenderBundleDepthStencil {
619                        format: wgpu::TextureFormat::Depth32Float,
620                        depth_read_only: false,
621                        stencil_read_only: true,
622                    }),
623                    sample_count: 1,
624                    multiview: None,
625                });
626            encoder.set_pipeline(&terrain_pipeline);
627            encoder.set_bind_group(0, &terrain_flipped_bind_group, &[]);
628            encoder.set_vertex_buffer(0, terrain_vertex_buf.slice(..));
629            encoder.draw(0..terrain_vertices.len() as u32, 0..1);
630            encoder.finish(&wgpu::RenderBundleDescriptor::default())
631        };
632
633        // Done
634        Example {
635            water_vertex_buf,
636            water_vertex_count: water_vertices.len(),
637            water_bind_group_layout,
638            water_bind_group,
639            water_uniform_buf,
640            water_pipeline,
641
642            terrain_vertex_buf,
643            terrain_vertex_count: terrain_vertices.len(),
644            terrain_normal_bind_group,
645            terrain_normal_uniform_buf,
646            terrain_flipped_uniform_buf,
647            terrain_pipeline,
648            terrain_bundle,
649
650            reflect_view,
651
652            depth_buffer,
653
654            current_frame: 0,
655
656            active: Some(0),
657        }
658    }
659
660    fn update(&mut self, _event: winit::event::WindowEvent) {
661        //empty
662    }
663
664    fn resize(
665        &mut self,
666        config: &wgpu::SurfaceConfiguration,
667        device: &wgpu::Device,
668        queue: &wgpu::Queue,
669    ) {
670        if config.width == 0 && config.height == 0 {
671            // Stop rendering altogether.
672            self.active = None;
673            return;
674        }
675        self.active = Some(self.current_frame);
676
677        // Regenerate all of the buffers and textures.
678
679        let (reflect_view, depth_buffer, water_bind_group) = Self::initialize_resources(
680            config,
681            device,
682            queue,
683            &self.water_uniform_buf,
684            &self.terrain_normal_uniform_buf,
685            &self.terrain_flipped_uniform_buf,
686            &self.water_bind_group_layout,
687        );
688        self.water_bind_group = water_bind_group;
689
690        self.depth_buffer = depth_buffer;
691        self.reflect_view = reflect_view;
692    }
693
694    fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
695        // Increment frame count regardless of if we draw.
696        self.current_frame += 1;
697        #[expect(clippy::eq_op, reason = "keeping common divisor on all elements")]
698        let back_color = wgpu::Color {
699            r: 161.0 / 255.0,
700            g: 246.0 / 255.0,
701            b: 255.0 / 255.0,
702            a: 1.0,
703        };
704
705        // Write the sin/cos values to the uniform buffer for the water.
706        let (water_sin, water_cos) = ((self.current_frame as f32) / 600.0).sin_cos();
707        queue.write_buffer(
708            &self.water_uniform_buf,
709            size_of::<[f32; 16]>() as wgpu::BufferAddress * 2,
710            bytemuck::cast_slice(&[water_sin, water_cos]),
711        );
712
713        // Only render valid frames. See resize method.
714        if let Some(active) = self.active {
715            if active >= self.current_frame {
716                return;
717            }
718        } else {
719            return;
720        }
721
722        // The encoder provides a way to turn our instructions here, into
723        // a command buffer the GPU can understand.
724        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
725            label: Some("Main Command Encoder"),
726        });
727
728        // First pass: render the reflection.
729        {
730            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
731                label: None,
732                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
733                    view: &self.reflect_view,
734                    depth_slice: None,
735                    resolve_target: None,
736                    ops: wgpu::Operations {
737                        load: wgpu::LoadOp::Clear(back_color),
738                        store: wgpu::StoreOp::Store,
739                    },
740                })],
741                // We still need to use the depth buffer here
742                // since the pipeline requires it.
743                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
744                    view: &self.depth_buffer,
745                    depth_ops: Some(wgpu::Operations {
746                        load: wgpu::LoadOp::Clear(1.0),
747                        store: wgpu::StoreOp::Store,
748                    }),
749                    stencil_ops: None,
750                }),
751                timestamp_writes: None,
752                occlusion_query_set: None,
753            });
754
755            rpass.execute_bundles([&self.terrain_bundle]);
756        }
757        // Terrain right side up. This time we need to use the
758        // depth values, so we must use StoreOp::Store.
759        {
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            });
782            rpass.set_pipeline(&self.terrain_pipeline);
783            rpass.set_bind_group(0, &self.terrain_normal_bind_group, &[]);
784            rpass.set_vertex_buffer(0, self.terrain_vertex_buf.slice(..));
785            rpass.draw(0..self.terrain_vertex_count as u32, 0..1);
786        }
787        // Render the water. This reads from the depth buffer, but does not write
788        // to it, so it cannot be in the same render pass.
789        {
790            let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
791                label: None,
792                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
793                    view,
794                    depth_slice: None,
795                    resolve_target: None,
796                    ops: wgpu::Operations {
797                        load: wgpu::LoadOp::Load,
798                        store: wgpu::StoreOp::Store,
799                    },
800                })],
801                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
802                    view: &self.depth_buffer,
803                    depth_ops: None,
804                    stencil_ops: None,
805                }),
806                timestamp_writes: None,
807                occlusion_query_set: None,
808            });
809
810            rpass.set_pipeline(&self.water_pipeline);
811            rpass.set_bind_group(0, &self.water_bind_group, &[]);
812            rpass.set_vertex_buffer(0, self.water_vertex_buf.slice(..));
813            rpass.draw(0..self.water_vertex_count as u32, 0..1);
814        }
815
816        queue.submit(iter::once(encoder.finish()));
817    }
818}
819
820pub fn main() {
821    crate::framework::run::<Example>("water");
822}
823
824#[cfg(test)]
825#[wgpu_test::gpu_test]
826pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
827    name: "water",
828    image_path: "/examples/features/src/water/screenshot.png",
829    width: 1024,
830    height: 768,
831    optional_features: wgpu::Features::default(),
832    base_test_parameters: wgpu_test::TestParameters::default()
833        .downlevel_flags(wgpu::DownlevelFlags::READ_ONLY_DEPTH_STENCIL)
834        // To be fixed in <https://github.com/gfx-rs/wgpu/issues/5231>.
835        .expect_fail(wgpu_test::FailureCase {
836            backends: Some(wgpu::Backends::VULKAN),
837            reasons: vec![wgpu_test::FailureReason::validation_error()
838                .with_message("WRITE_AFTER_WRITE hazard detected.")],
839            behavior: wgpu_test::FailureBehavior::AssertFailure,
840            ..Default::default()
841        }),
842    comparisons: &[wgpu_test::ComparisonType::Mean(0.01)],
843    _phantom: std::marker::PhantomData::<Example>,
844};