use crate::gpu_types::Vertex;
use crate::pipeline::{load_shader, SceneState};
pub struct DeferredState {
pub albedo_metallic_tex: wgpu::Texture,
pub albedo_metallic_view: wgpu::TextureView,
pub normal_roughness_tex: wgpu::Texture,
pub normal_roughness_view: wgpu::TextureView,
pub world_position_tex: wgpu::Texture,
pub world_position_view: wgpu::TextureView,
pub world_tangent_tex: wgpu::Texture,
pub world_tangent_view: wgpu::TextureView,
pub gbuffer_pipeline: wgpu::RenderPipeline,
pub z_prepass_pipeline: wgpu::RenderPipeline,
pub lighting_pipeline: wgpu::RenderPipeline,
pub gbuffer_bind_group_layout: wgpu::BindGroupLayout,
pub gbuffer_bind_group: wgpu::BindGroup,
pub gbuf_sampler: wgpu::Sampler,
pub width: u32,
pub height: u32,
}
impl DeferredState {
pub fn new(device: &wgpu::Device, scene: &SceneState, width: u32, height: u32) -> Self {
let (
albedo_metallic_tex,
albedo_metallic_view,
normal_roughness_tex,
normal_roughness_view,
world_position_tex,
world_position_view,
world_tangent_tex,
world_tangent_view,
gbuf_sampler,
) = Self::create_gbuffer_textures(device, width, height);
let gbuffer_bind_group_layout = Self::create_gbuffer_layout(device);
let gbuffer_bind_group = Self::create_gbuffer_bind_group(
device,
&gbuffer_bind_group_layout,
&albedo_metallic_view,
&normal_roughness_view,
&world_position_view,
&world_tangent_view,
&gbuf_sampler,
);
let z_prepass_pipeline = Self::create_z_prepass_pipeline(device, scene);
let gbuffer_pipeline = Self::create_gbuffer_pipeline(device, scene);
let lighting_pipeline =
Self::create_lighting_pipeline(device, scene, &gbuffer_bind_group_layout);
Self {
albedo_metallic_tex,
albedo_metallic_view,
normal_roughness_tex,
normal_roughness_view,
world_position_tex,
world_position_view,
world_tangent_tex,
world_tangent_view,
gbuffer_pipeline,
z_prepass_pipeline,
lighting_pipeline,
gbuffer_bind_group_layout,
gbuffer_bind_group,
gbuf_sampler,
width,
height,
}
}
pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
if self.width == width && self.height == height {
return;
}
let (
albedo_metallic_tex,
albedo_metallic_view,
normal_roughness_tex,
normal_roughness_view,
world_position_tex,
world_position_view,
world_tangent_tex,
world_tangent_view,
gbuf_sampler,
) = Self::create_gbuffer_textures(device, width, height);
self.gbuffer_bind_group = Self::create_gbuffer_bind_group(
device,
&self.gbuffer_bind_group_layout,
&albedo_metallic_view,
&normal_roughness_view,
&world_position_view,
&world_tangent_view,
&gbuf_sampler,
);
self.albedo_metallic_tex = albedo_metallic_tex;
self.albedo_metallic_view = albedo_metallic_view;
self.normal_roughness_tex = normal_roughness_tex;
self.normal_roughness_view = normal_roughness_view;
self.world_position_tex = world_position_tex;
self.world_position_view = world_position_view;
self.world_tangent_tex = world_tangent_tex;
self.world_tangent_view = world_tangent_view;
self.gbuf_sampler = gbuf_sampler;
self.width = width;
self.height = height;
}
fn create_gbuffer_textures(
device: &wgpu::Device,
w: u32,
h: u32,
) -> (
wgpu::Texture,
wgpu::TextureView,
wgpu::Texture,
wgpu::TextureView,
wgpu::Texture,
wgpu::TextureView,
wgpu::Texture,
wgpu::TextureView,
wgpu::Sampler,
) {
let mk = |label: &str, fmt: wgpu::TextureFormat| {
let t = device.create_texture(&wgpu::TextureDescriptor {
label: Some(label),
size: wgpu::Extent3d {
width: w,
height: h,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: fmt,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let v = t.create_view(&wgpu::TextureViewDescriptor::default());
(t, v)
};
let (a, av) = mk("gbuf_albedo_metallic", wgpu::TextureFormat::Rgba8Unorm);
let (n, nv) = mk("gbuf_normal_roughness", wgpu::TextureFormat::Rgba16Float);
let (p, pv) = mk("gbuf_world_position", wgpu::TextureFormat::Rgba16Float);
let (t, tv) = mk("gbuf_world_tangent", wgpu::TextureFormat::Rgba16Float);
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Nearest,
min_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
(a, av, n, nv, p, pv, t, tv, sampler)
}
fn create_gbuffer_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("gbuffer_bind_group_layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: false },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: false },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: false },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 4,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: false },
},
count: None,
},
],
})
}
fn create_gbuffer_bind_group(
device: &wgpu::Device,
layout: &wgpu::BindGroupLayout,
albedo_v: &wgpu::TextureView,
normal_v: &wgpu::TextureView,
pos_v: &wgpu::TextureView,
tangent_v: &wgpu::TextureView,
sampler: &wgpu::Sampler,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("gbuffer_bind_group"),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(albedo_v),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(normal_v),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::TextureView(pos_v),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::Sampler(sampler),
},
wgpu::BindGroupEntry {
binding: 4,
resource: wgpu::BindingResource::TextureView(tangent_v),
},
],
})
}
fn create_gbuffer_pipeline(device: &wgpu::Device, scene: &SceneState) -> wgpu::RenderPipeline {
let shader = load_shader(
device,
"demo/assets/shaders/gbuffer.wgsl",
include_str!("shaders/gbuffer.wgsl"),
"GBuffer Shader",
);
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("GBuffer Pipeline Layout"),
bind_group_layouts: &[
&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, ],
push_constant_ranges: &[],
});
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("GBuffer Pipeline"),
layout: Some(&layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
compilation_options: Default::default(),
buffers: &[Vertex::desc()],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
compilation_options: Default::default(),
targets: &[
Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba8Unorm,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
}),
Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
}),
Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
}),
Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
}),
],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
..Default::default()
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: false,
depth_compare: wgpu::CompareFunction::LessEqual,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
})
}
fn create_z_prepass_pipeline(
device: &wgpu::Device,
scene: &SceneState,
) -> wgpu::RenderPipeline {
let shader = load_shader(
device,
"demo/assets/shaders/gbuffer.wgsl",
include_str!("shaders/gbuffer.wgsl"),
"Z-Prepass Shader",
);
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Z-Prepass Pipeline Layout"),
bind_group_layouts: &[
&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, ],
push_constant_ranges: &[],
});
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Z-Prepass Pipeline"),
layout: Some(&layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
compilation_options: Default::default(),
buffers: &[Vertex::desc()],
},
fragment: None, primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
..Default::default()
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
})
}
fn create_lighting_pipeline(
device: &wgpu::Device,
scene: &SceneState,
gbuffer_layout: &wgpu::BindGroupLayout,
) -> wgpu::RenderPipeline {
let shader = load_shader(
device,
"demo/assets/shaders/deferred_lighting.wgsl",
include_str!("shaders/deferred_lighting.wgsl"),
"Deferred Lighting Shader",
);
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Deferred Lighting Layout"),
bind_group_layouts: &[
&scene.global_bind_group_layout, &scene.shadow_bind_group_layout, gbuffer_layout, ],
push_constant_ranges: &[],
});
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Deferred Lighting Pipeline"),
layout: Some(&layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
compilation_options: Default::default(),
buffers: &[], },
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
cull_mode: None,
..Default::default()
},
depth_stencil: None, multisample: wgpu::MultisampleState::default(),
multiview: None,
})
}
}