1use crate::gpu_types::Vertex;
2use crate::pipeline::{load_shader, SceneState};
3
4pub struct DeferredState {
6 pub albedo_metallic_tex: wgpu::Texture,
8 pub albedo_metallic_view: wgpu::TextureView,
9 pub normal_roughness_tex: wgpu::Texture,
10 pub normal_roughness_view: wgpu::TextureView,
11 pub world_position_tex: wgpu::Texture,
12 pub world_position_view: wgpu::TextureView,
13 pub world_tangent_tex: wgpu::Texture,
14 pub world_tangent_view: wgpu::TextureView,
15
16 pub gbuffer_pipeline: wgpu::RenderPipeline,
18
19 pub z_prepass_pipeline: wgpu::RenderPipeline,
21
22 pub lighting_pipeline: wgpu::RenderPipeline,
24
25 pub gbuffer_bind_group_layout: wgpu::BindGroupLayout,
27 pub gbuffer_bind_group: wgpu::BindGroup,
28 pub gbuf_sampler: wgpu::Sampler,
29
30 pub width: u32,
31 pub height: u32,
32}
33
34impl DeferredState {
35 pub fn new(device: &wgpu::Device, scene: &SceneState, width: u32, height: u32) -> Self {
36 let (
37 albedo_metallic_tex,
38 albedo_metallic_view,
39 normal_roughness_tex,
40 normal_roughness_view,
41 world_position_tex,
42 world_position_view,
43 world_tangent_tex,
44 world_tangent_view,
45 gbuf_sampler,
46 ) = Self::create_gbuffer_textures(device, width, height);
47
48 let gbuffer_bind_group_layout = Self::create_gbuffer_layout(device);
49
50 let gbuffer_bind_group = Self::create_gbuffer_bind_group(
51 device,
52 &gbuffer_bind_group_layout,
53 &albedo_metallic_view,
54 &normal_roughness_view,
55 &world_position_view,
56 &world_tangent_view,
57 &gbuf_sampler,
58 );
59
60 let z_prepass_pipeline = Self::create_z_prepass_pipeline(device, scene);
61 let gbuffer_pipeline = Self::create_gbuffer_pipeline(device, scene);
62 let lighting_pipeline =
63 Self::create_lighting_pipeline(device, scene, &gbuffer_bind_group_layout);
64
65 Self {
66 albedo_metallic_tex,
67 albedo_metallic_view,
68 normal_roughness_tex,
69 normal_roughness_view,
70 world_position_tex,
71 world_position_view,
72 world_tangent_tex,
73 world_tangent_view,
74 gbuffer_pipeline,
75 z_prepass_pipeline,
76 lighting_pipeline,
77 gbuffer_bind_group_layout,
78 gbuffer_bind_group,
79 gbuf_sampler,
80 width,
81 height,
82 }
83 }
84
85 pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
87 if self.width == width && self.height == height {
88 return;
89 }
90 let (
91 albedo_metallic_tex,
92 albedo_metallic_view,
93 normal_roughness_tex,
94 normal_roughness_view,
95 world_position_tex,
96 world_position_view,
97 world_tangent_tex,
98 world_tangent_view,
99 gbuf_sampler,
100 ) = Self::create_gbuffer_textures(device, width, height);
101
102 self.gbuffer_bind_group = Self::create_gbuffer_bind_group(
103 device,
104 &self.gbuffer_bind_group_layout,
105 &albedo_metallic_view,
106 &normal_roughness_view,
107 &world_position_view,
108 &world_tangent_view,
109 &gbuf_sampler,
110 );
111
112 self.albedo_metallic_tex = albedo_metallic_tex;
113 self.albedo_metallic_view = albedo_metallic_view;
114 self.normal_roughness_tex = normal_roughness_tex;
115 self.normal_roughness_view = normal_roughness_view;
116 self.world_position_tex = world_position_tex;
117 self.world_position_view = world_position_view;
118 self.world_tangent_tex = world_tangent_tex;
119 self.world_tangent_view = world_tangent_view;
120 self.gbuf_sampler = gbuf_sampler;
121 self.width = width;
122 self.height = height;
123 }
124
125 fn create_gbuffer_textures(
128 device: &wgpu::Device,
129 w: u32,
130 h: u32,
131 ) -> (
132 wgpu::Texture,
133 wgpu::TextureView,
134 wgpu::Texture,
135 wgpu::TextureView,
136 wgpu::Texture,
137 wgpu::TextureView,
138 wgpu::Texture,
139 wgpu::TextureView,
140 wgpu::Sampler,
141 ) {
142 let mk = |label: &str, fmt: wgpu::TextureFormat| {
143 let t = device.create_texture(&wgpu::TextureDescriptor {
144 label: Some(label),
145 size: wgpu::Extent3d {
146 width: w,
147 height: h,
148 depth_or_array_layers: 1,
149 },
150 mip_level_count: 1,
151 sample_count: 1,
152 dimension: wgpu::TextureDimension::D2,
153 format: fmt,
154 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
155 | wgpu::TextureUsages::TEXTURE_BINDING,
156 view_formats: &[],
157 });
158 let v = t.create_view(&wgpu::TextureViewDescriptor::default());
159 (t, v)
160 };
161
162 let (a, av) = mk("gbuf_albedo_metallic", wgpu::TextureFormat::Rgba8Unorm);
163 let (n, nv) = mk("gbuf_normal_roughness", wgpu::TextureFormat::Rgba16Float);
164 let (p, pv) = mk("gbuf_world_position", wgpu::TextureFormat::Rgba16Float);
165 let (t, tv) = mk("gbuf_world_tangent", wgpu::TextureFormat::Rgba16Float);
166
167 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
168 address_mode_u: wgpu::AddressMode::ClampToEdge,
169 address_mode_v: wgpu::AddressMode::ClampToEdge,
170 mag_filter: wgpu::FilterMode::Nearest,
171 min_filter: wgpu::FilterMode::Nearest,
172 ..Default::default()
173 });
174
175 (a, av, n, nv, p, pv, t, tv, sampler)
176 }
177
178 fn create_gbuffer_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
179 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
180 label: Some("gbuffer_bind_group_layout"),
181 entries: &[
182 wgpu::BindGroupLayoutEntry {
184 binding: 0,
185 visibility: wgpu::ShaderStages::FRAGMENT,
186 ty: wgpu::BindingType::Texture {
187 multisampled: false,
188 view_dimension: wgpu::TextureViewDimension::D2,
189 sample_type: wgpu::TextureSampleType::Float { filterable: false },
190 },
191 count: None,
192 },
193 wgpu::BindGroupLayoutEntry {
195 binding: 1,
196 visibility: wgpu::ShaderStages::FRAGMENT,
197 ty: wgpu::BindingType::Texture {
198 multisampled: false,
199 view_dimension: wgpu::TextureViewDimension::D2,
200 sample_type: wgpu::TextureSampleType::Float { filterable: false },
201 },
202 count: None,
203 },
204 wgpu::BindGroupLayoutEntry {
206 binding: 2,
207 visibility: wgpu::ShaderStages::FRAGMENT,
208 ty: wgpu::BindingType::Texture {
209 multisampled: false,
210 view_dimension: wgpu::TextureViewDimension::D2,
211 sample_type: wgpu::TextureSampleType::Float { filterable: false },
212 },
213 count: None,
214 },
215 wgpu::BindGroupLayoutEntry {
217 binding: 3,
218 visibility: wgpu::ShaderStages::FRAGMENT,
219 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
220 count: None,
221 },
222 wgpu::BindGroupLayoutEntry {
224 binding: 4,
225 visibility: wgpu::ShaderStages::FRAGMENT,
226 ty: wgpu::BindingType::Texture {
227 multisampled: false,
228 view_dimension: wgpu::TextureViewDimension::D2,
229 sample_type: wgpu::TextureSampleType::Float { filterable: false },
230 },
231 count: None,
232 },
233 ],
234 })
235 }
236
237 fn create_gbuffer_bind_group(
238 device: &wgpu::Device,
239 layout: &wgpu::BindGroupLayout,
240 albedo_v: &wgpu::TextureView,
241 normal_v: &wgpu::TextureView,
242 pos_v: &wgpu::TextureView,
243 tangent_v: &wgpu::TextureView,
244 sampler: &wgpu::Sampler,
245 ) -> wgpu::BindGroup {
246 device.create_bind_group(&wgpu::BindGroupDescriptor {
247 label: Some("gbuffer_bind_group"),
248 layout,
249 entries: &[
250 wgpu::BindGroupEntry {
251 binding: 0,
252 resource: wgpu::BindingResource::TextureView(albedo_v),
253 },
254 wgpu::BindGroupEntry {
255 binding: 1,
256 resource: wgpu::BindingResource::TextureView(normal_v),
257 },
258 wgpu::BindGroupEntry {
259 binding: 2,
260 resource: wgpu::BindingResource::TextureView(pos_v),
261 },
262 wgpu::BindGroupEntry {
263 binding: 3,
264 resource: wgpu::BindingResource::Sampler(sampler),
265 },
266 wgpu::BindGroupEntry {
267 binding: 4,
268 resource: wgpu::BindingResource::TextureView(tangent_v),
269 },
270 ],
271 })
272 }
273
274 fn create_gbuffer_pipeline(device: &wgpu::Device, scene: &SceneState) -> wgpu::RenderPipeline {
275 let shader = load_shader(
276 device,
277 "demo/assets/shaders/gbuffer.wgsl",
278 include_str!("shaders/gbuffer.wgsl"),
279 "GBuffer Shader",
280 );
281
282 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
283 label: Some("GBuffer Pipeline Layout"),
284 bind_group_layouts: &[
285 &scene.global_bind_group_layout, &scene.texture_bind_group_layout, &scene.shadow_bind_group_layout, &scene.skeleton_bind_group_layout, &scene.instance_bind_group_layout, ],
291 push_constant_ranges: &[],
292 });
293
294 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
295 label: Some("GBuffer Pipeline"),
296 layout: Some(&layout),
297 vertex: wgpu::VertexState {
298 module: &shader,
299 entry_point: "vs_main",
300 compilation_options: Default::default(),
301 buffers: &[Vertex::desc()],
302 },
303 fragment: Some(wgpu::FragmentState {
304 module: &shader,
305 entry_point: "fs_main",
306 compilation_options: Default::default(),
307 targets: &[
308 Some(wgpu::ColorTargetState {
310 format: wgpu::TextureFormat::Rgba8Unorm,
311 blend: None,
312 write_mask: wgpu::ColorWrites::ALL,
313 }),
314 Some(wgpu::ColorTargetState {
316 format: wgpu::TextureFormat::Rgba16Float,
317 blend: None,
318 write_mask: wgpu::ColorWrites::ALL,
319 }),
320 Some(wgpu::ColorTargetState {
322 format: wgpu::TextureFormat::Rgba16Float,
323 blend: None,
324 write_mask: wgpu::ColorWrites::ALL,
325 }),
326 Some(wgpu::ColorTargetState {
328 format: wgpu::TextureFormat::Rgba16Float,
329 blend: None,
330 write_mask: wgpu::ColorWrites::ALL,
331 }),
332 ],
333 }),
334 primitive: wgpu::PrimitiveState {
335 topology: wgpu::PrimitiveTopology::TriangleList,
336 front_face: wgpu::FrontFace::Ccw,
337 cull_mode: Some(wgpu::Face::Back),
338 ..Default::default()
339 },
340 depth_stencil: Some(wgpu::DepthStencilState {
341 format: wgpu::TextureFormat::Depth32Float,
342 depth_write_enabled: false,
343 depth_compare: wgpu::CompareFunction::LessEqual,
344 stencil: wgpu::StencilState::default(),
345 bias: wgpu::DepthBiasState::default(),
346 }),
347 multisample: wgpu::MultisampleState::default(),
348 multiview: None,
349 })
350 }
351
352 fn create_z_prepass_pipeline(
353 device: &wgpu::Device,
354 scene: &SceneState,
355 ) -> wgpu::RenderPipeline {
356 let shader = load_shader(
357 device,
358 "demo/assets/shaders/gbuffer.wgsl",
359 include_str!("shaders/gbuffer.wgsl"),
360 "Z-Prepass Shader",
361 );
362
363 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
364 label: Some("Z-Prepass Pipeline Layout"),
365 bind_group_layouts: &[
366 &scene.global_bind_group_layout, &scene.texture_bind_group_layout, &scene.shadow_bind_group_layout, &scene.skeleton_bind_group_layout, &scene.instance_bind_group_layout, ],
372 push_constant_ranges: &[],
373 });
374
375 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
376 label: Some("Z-Prepass Pipeline"),
377 layout: Some(&layout),
378 vertex: wgpu::VertexState {
379 module: &shader,
380 entry_point: "vs_main",
381 compilation_options: Default::default(),
382 buffers: &[Vertex::desc()],
383 },
384 fragment: None, primitive: wgpu::PrimitiveState {
386 topology: wgpu::PrimitiveTopology::TriangleList,
387 front_face: wgpu::FrontFace::Ccw,
388 cull_mode: Some(wgpu::Face::Back),
389 ..Default::default()
390 },
391 depth_stencil: Some(wgpu::DepthStencilState {
392 format: wgpu::TextureFormat::Depth32Float,
393 depth_write_enabled: true,
394 depth_compare: wgpu::CompareFunction::Less,
395 stencil: wgpu::StencilState::default(),
396 bias: wgpu::DepthBiasState::default(),
397 }),
398 multisample: wgpu::MultisampleState::default(),
399 multiview: None,
400 })
401 }
402
403 fn create_lighting_pipeline(
404 device: &wgpu::Device,
405 scene: &SceneState,
406 gbuffer_layout: &wgpu::BindGroupLayout,
407 ) -> wgpu::RenderPipeline {
408 let shader = load_shader(
409 device,
410 "demo/assets/shaders/deferred_lighting.wgsl",
411 include_str!("shaders/deferred_lighting.wgsl"),
412 "Deferred Lighting Shader",
413 );
414
415 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
416 label: Some("Deferred Lighting Layout"),
417 bind_group_layouts: &[
418 &scene.global_bind_group_layout, &scene.shadow_bind_group_layout, gbuffer_layout, ],
422 push_constant_ranges: &[],
423 });
424
425 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
426 label: Some("Deferred Lighting Pipeline"),
427 layout: Some(&layout),
428 vertex: wgpu::VertexState {
429 module: &shader,
430 entry_point: "vs_main",
431 compilation_options: Default::default(),
432 buffers: &[], },
434 fragment: Some(wgpu::FragmentState {
435 module: &shader,
436 entry_point: "fs_main",
437 compilation_options: Default::default(),
438 targets: &[Some(wgpu::ColorTargetState {
439 format: wgpu::TextureFormat::Rgba16Float,
440 blend: None,
441 write_mask: wgpu::ColorWrites::ALL,
442 })],
443 }),
444 primitive: wgpu::PrimitiveState {
445 topology: wgpu::PrimitiveTopology::TriangleList,
446 cull_mode: None,
447 ..Default::default()
448 },
449 depth_stencil: None, multisample: wgpu::MultisampleState::default(),
451 multiview: None,
452 })
453 }
454}