mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Initial bloom impl
This commit is contained in:
parent
b8716e03a6
commit
2d83ef1c0e
31
assets/voxygen/shaders/duel-downsample-frag.glsl
Normal file
31
assets/voxygen/shaders/duel-downsample-frag.glsl
Normal file
@ -0,0 +1,31 @@
|
||||
#version 420 core
|
||||
|
||||
layout(set = 0, binding = 0)
|
||||
uniform texture2D t_src_color;
|
||||
layout(set = 0, binding = 1)
|
||||
uniform sampler s_src_color;
|
||||
layout(set = 0, binding = 2)
|
||||
uniform vec2 halfpixel;
|
||||
|
||||
layout(location = 0) in vec2 uv;
|
||||
|
||||
layout(location = 0) out vec4 tgt_color;
|
||||
|
||||
vec4 simplesample(vec2 uv) {
|
||||
return textureLod(sampler2D(t_src_color, s_src_color), uv, 0);
|
||||
}
|
||||
|
||||
// From: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
|
||||
vec4 downsample(vec2 uv, vec2 halfpixel) {
|
||||
vec4 sum = simplesample(uv) * 4.0;
|
||||
sum += simplesample(uv - halfpixel.xy);
|
||||
sum += simplesample(uv + halfpixel.xy);
|
||||
sum += simplesample(uv + vec2(halfpixel.x, -halfpixel.y));
|
||||
sum += simplesample(uv - vec2(halfpixel.x, -halfpixel.y));
|
||||
|
||||
return sum / 8.0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
tgt_color = downsample(uv, halfpixel);
|
||||
}
|
33
assets/voxygen/shaders/duel-upsample-frag.glsl
Normal file
33
assets/voxygen/shaders/duel-upsample-frag.glsl
Normal file
@ -0,0 +1,33 @@
|
||||
#version 420 core
|
||||
|
||||
layout(set = 0, binding = 0)
|
||||
uniform texture2D t_src_color;
|
||||
layout(set = 0, binding = 1)
|
||||
uniform sampler s_src_color;
|
||||
layout(set = 0, binding = 2)
|
||||
uniform vec2 halfpixel;
|
||||
|
||||
layout(location = 0) in vec2 uv;
|
||||
|
||||
layout(location = 0) out vec4 tgt_color;
|
||||
|
||||
vec4 simplesample(vec2 uv) {
|
||||
return textureLod(sampler2D(t_src_color, s_src_color), uv, 0);
|
||||
}
|
||||
|
||||
// From: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
|
||||
vec4 upsample(vec2 uv, vec2 halfpixel) {
|
||||
vec4 sum = simplesample(uv + vec2(-halfpixel.x * 2.0, 0.0));
|
||||
sum += simplesample(uv + vec2(-halfpixel.x, halfpixel.y)) * 2.0;
|
||||
sum += simplesample(uv + vec2(0.0, halfpixel.y * 2.0));
|
||||
sum += simplesample(uv + vec2(halfpixel.x, halfpixel.y)) * 2.0;
|
||||
sum += simplesample(uv + vec2(halfpixel.x * 2.0, 0.0));
|
||||
sum += simplesample(uv + vec2(halfpixel.x, -halfpixel.y)) * 2.0;
|
||||
sum += simplesample(uv + vec2(0.0, -halfpixel.y * 2.0));
|
||||
sum += simplesample(uv + vec2(-halfpixel.x, -halfpixel.y)) * 2.0;
|
||||
return sum / 12.0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
tgt_color = upsample(uv, halfpixel);
|
||||
}
|
@ -25,6 +25,8 @@
|
||||
layout(set = 1, binding = 0)
|
||||
uniform texture2D t_src_color;
|
||||
layout(set = 1, binding = 1)
|
||||
uniform texture2D t_src_bloom;
|
||||
layout(set = 1, binding = 2)
|
||||
uniform sampler s_src_color;
|
||||
|
||||
|
||||
@ -182,6 +184,10 @@ void main() {
|
||||
|
||||
vec4 aa_color = aa_apply(t_src_color, s_src_color, uv * screen_res.xy, screen_res.xy);
|
||||
|
||||
// Bloom
|
||||
vec4 bloom = textureLod(sampler2D(t_src_bloom, s_src_color), uv, 0) * 0.05;
|
||||
aa_color += bloom;
|
||||
|
||||
// Tonemapping
|
||||
float exposure_offset = 1.0;
|
||||
// Adding an in-code offset to gamma and exposure let us have more precise control over the game's look
|
||||
|
@ -4,6 +4,8 @@
|
||||
#![deny(clippy::clone_on_ref_ptr)]
|
||||
#![feature(
|
||||
array_map,
|
||||
array_methods,
|
||||
array_zip,
|
||||
bool_to_option,
|
||||
const_generics,
|
||||
drain_filter,
|
||||
|
197
voxygen/src/render/pipelines/bloom.rs
Normal file
197
voxygen/src/render/pipelines/bloom.rs
Normal file
@ -0,0 +1,197 @@
|
||||
use super::super::Consts;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use vek::*;
|
||||
|
||||
/// Each level is a multiple of 2 smaller in both dimensions.
|
||||
/// For a total of 8 passes from the largest to the smallest to the largest
|
||||
/// again.
|
||||
pub const NUM_SIZES: usize = 5;
|
||||
|
||||
pub struct BindGroup {
|
||||
pub(in super::super) bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct HalfPixel([f32; 2]);
|
||||
|
||||
impl HalfPixel {
|
||||
pub fn new(source_texture_resolution: Vec2<f32>) -> Self {
|
||||
Self(source_texture_resolution.map(|e| 0.5 / e).into_array())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BloomLayout {
|
||||
pub layout: wgpu::BindGroupLayout,
|
||||
}
|
||||
|
||||
impl BloomLayout {
|
||||
pub fn new(device: &wgpu::Device) -> Self {
|
||||
Self {
|
||||
layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: None,
|
||||
entries: &[
|
||||
// Color source
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler {
|
||||
filtering: true,
|
||||
comparison: false,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
// halfpixel
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind(
|
||||
&self,
|
||||
device: &wgpu::Device,
|
||||
src_color: &wgpu::TextureView,
|
||||
sampler: &wgpu::Sampler,
|
||||
half_pixel: Consts<HalfPixel>,
|
||||
) -> BindGroup {
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &self.layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(src_color),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: half_pixel.buf().as_entire_binding(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
BindGroup { bind_group }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BloomPipelines {
|
||||
//pub downsample_filtered: wgpu::RenderPipeline,
|
||||
pub downsample: wgpu::RenderPipeline,
|
||||
pub upsample: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
impl BloomPipelines {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
vs_module: &wgpu::ShaderModule,
|
||||
//downsample_filtered_fs_module: &wgpu::ShaderModule,
|
||||
downsample_fs_module: &wgpu::ShaderModule,
|
||||
upsample_fs_module: &wgpu::ShaderModule,
|
||||
target_format: wgpu::TextureFormat,
|
||||
layout: &BloomLayout,
|
||||
) -> Self {
|
||||
common_base::span!(_guard, "BloomPipelines::new");
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Bloom pipelines layout"),
|
||||
push_constant_ranges: &[],
|
||||
bind_group_layouts: &[&layout.layout],
|
||||
});
|
||||
|
||||
let downsample_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Bloom downsample pipeline"),
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: vs_module,
|
||||
entry_point: "main",
|
||||
buffers: &[],
|
||||
},
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: None,
|
||||
clamp_depth: false,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: downsample_fs_module,
|
||||
entry_point: "main",
|
||||
targets: &[wgpu::ColorTargetState {
|
||||
format: target_format,
|
||||
blend: None,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
}],
|
||||
}),
|
||||
});
|
||||
|
||||
let upsample_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Bloom upsample pipeline"),
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: vs_module,
|
||||
entry_point: "main",
|
||||
buffers: &[],
|
||||
},
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: None,
|
||||
clamp_depth: false,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: upsample_fs_module,
|
||||
entry_point: "main",
|
||||
targets: &[wgpu::ColorTargetState {
|
||||
format: target_format,
|
||||
blend: None,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
}],
|
||||
}),
|
||||
});
|
||||
|
||||
Self {
|
||||
downsample: downsample_pipeline,
|
||||
upsample: upsample_pipeline,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
pub mod blit;
|
||||
pub mod bloom;
|
||||
pub mod clouds;
|
||||
pub mod debug;
|
||||
pub mod figure;
|
||||
|
@ -39,7 +39,19 @@ impl PostProcessLayout {
|
||||
// src color
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
// TODO: make optional
|
||||
// src bloom
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
@ -49,7 +61,7 @@ impl PostProcessLayout {
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler {
|
||||
filtering: true,
|
||||
comparison: false,
|
||||
@ -76,6 +88,7 @@ impl PostProcessLayout {
|
||||
&self,
|
||||
device: &wgpu::Device,
|
||||
src_color: &wgpu::TextureView,
|
||||
src_bloom: &wgpu::TextureView,
|
||||
sampler: &wgpu::Sampler,
|
||||
locals: &Consts<Locals>,
|
||||
) -> BindGroup {
|
||||
@ -87,6 +100,14 @@ impl PostProcessLayout {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(src_color),
|
||||
},
|
||||
// TODO: might be cheaper to premix bloom at lower resolution if we are doing
|
||||
// extensive upscaling
|
||||
// TODO: if there is no upscaling we can do the last bloom upsampling in post
|
||||
// process to save a pass and the need for the final full size bloom render target
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(src_bloom),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(sampler),
|
||||
|
@ -21,8 +21,8 @@ use super::{
|
||||
mesh::Mesh,
|
||||
model::{DynamicModel, Model},
|
||||
pipelines::{
|
||||
blit, clouds, debug, figure, postprocess, shadow, sprite, terrain, ui, GlobalsBindGroup,
|
||||
GlobalsLayouts, ShadowTexturesBindGroup,
|
||||
blit, bloom, clouds, debug, figure, postprocess, shadow, sprite, terrain, ui,
|
||||
GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup,
|
||||
},
|
||||
texture::Texture,
|
||||
AaMode, AddressMode, FilterMode, RenderError, RenderMode, ShadowMapMode, ShadowMode, Vertex,
|
||||
@ -49,13 +49,14 @@ const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000;
|
||||
struct Layouts {
|
||||
global: GlobalsLayouts,
|
||||
|
||||
clouds: clouds::CloudsLayout,
|
||||
debug: debug::DebugLayout,
|
||||
figure: figure::FigureLayout,
|
||||
postprocess: postprocess::PostProcessLayout,
|
||||
shadow: shadow::ShadowLayout,
|
||||
sprite: sprite::SpriteLayout,
|
||||
terrain: terrain::TerrainLayout,
|
||||
clouds: clouds::CloudsLayout,
|
||||
bloom: bloom::BloomLayout,
|
||||
postprocess: postprocess::PostProcessLayout,
|
||||
ui: ui::UiLayout,
|
||||
blit: blit::BlitLayout,
|
||||
}
|
||||
@ -67,6 +68,8 @@ struct Views {
|
||||
|
||||
tgt_color: wgpu::TextureView,
|
||||
tgt_depth: wgpu::TextureView,
|
||||
|
||||
bloom_tgts: [wgpu::TextureView; bloom::NUM_SIZES],
|
||||
// TODO: rename
|
||||
tgt_color_pp: wgpu::TextureView,
|
||||
}
|
||||
@ -305,26 +308,28 @@ impl Renderer {
|
||||
let layouts = {
|
||||
let global = GlobalsLayouts::new(&device);
|
||||
|
||||
let clouds = clouds::CloudsLayout::new(&device);
|
||||
let debug = debug::DebugLayout::new(&device);
|
||||
let figure = figure::FigureLayout::new(&device);
|
||||
let postprocess = postprocess::PostProcessLayout::new(&device);
|
||||
let shadow = shadow::ShadowLayout::new(&device);
|
||||
let sprite = sprite::SpriteLayout::new(&device);
|
||||
let terrain = terrain::TerrainLayout::new(&device);
|
||||
let clouds = clouds::CloudsLayout::new(&device);
|
||||
let bloom = bloom::BloomLayout::new(&device);
|
||||
let postprocess = postprocess::PostProcessLayout::new(&device);
|
||||
let ui = ui::UiLayout::new(&device);
|
||||
let blit = blit::BlitLayout::new(&device);
|
||||
|
||||
Layouts {
|
||||
global,
|
||||
|
||||
clouds,
|
||||
debug,
|
||||
figure,
|
||||
postprocess,
|
||||
shadow,
|
||||
sprite,
|
||||
terrain,
|
||||
clouds,
|
||||
bloom,
|
||||
postprocess,
|
||||
ui,
|
||||
blit,
|
||||
}
|
||||
@ -350,7 +355,8 @@ impl Renderer {
|
||||
creating,
|
||||
};
|
||||
|
||||
let views = Self::create_rt_views(&device, (dims.width, dims.height), &mode)?;
|
||||
let (views, bloom_sizes) =
|
||||
Self::create_rt_views(&device, (dims.width, dims.height), &mode)?;
|
||||
|
||||
let create_sampler = |filter| {
|
||||
device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
@ -379,6 +385,8 @@ impl Renderer {
|
||||
|
||||
let clouds_locals =
|
||||
Self::create_consts_inner(&device, &queue, &[clouds::Locals::default()]);
|
||||
let bloom_locals = bloom_sizes
|
||||
.map(|size| Self::create_consts_inner(&device, &queue, &[bloom::HalfPixel::new(size)]));
|
||||
let postprocess_locals =
|
||||
Self::create_consts_inner(&device, &queue, &[postprocess::Locals::default()]);
|
||||
|
||||
@ -386,9 +394,18 @@ impl Renderer {
|
||||
&device,
|
||||
&layouts,
|
||||
clouds_locals,
|
||||
bloom_locals,
|
||||
postprocess_locals,
|
||||
&views.tgt_color,
|
||||
&views.tgt_depth,
|
||||
[
|
||||
&views.tgt_color,
|
||||
&views.bloom_tgts[1],
|
||||
&views.bloom_tgts[2],
|
||||
&views.bloom_tgts[3],
|
||||
&views.bloom_tgts[4],
|
||||
],
|
||||
&views.bloom_tgts[0],
|
||||
&views.tgt_color_pp,
|
||||
&sampler,
|
||||
&depth_sampler,
|
||||
@ -546,13 +563,26 @@ impl Renderer {
|
||||
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
|
||||
|
||||
// Resize other render targets
|
||||
self.views = Self::create_rt_views(&self.device, (dims.x, dims.y), &self.mode)?;
|
||||
let (views, bloom_sizes) =
|
||||
Self::create_rt_views(&self.device, (dims.x, dims.y), &self.mode)?;
|
||||
self.views = views;
|
||||
let bloom_locals =
|
||||
bloom_sizes.map(|size| self.create_consts(&[bloom::HalfPixel::new(size)]));
|
||||
// Rebind views to clouds/postprocess bind groups
|
||||
self.locals.rebind(
|
||||
&self.device,
|
||||
&self.layouts,
|
||||
bloom_locals,
|
||||
&self.views.tgt_color,
|
||||
&self.views.tgt_depth,
|
||||
[
|
||||
&self.views.tgt_color,
|
||||
&self.views.bloom_tgts[1],
|
||||
&self.views.bloom_tgts[2],
|
||||
&self.views.bloom_tgts[3],
|
||||
&self.views.bloom_tgts[4],
|
||||
],
|
||||
&self.views.bloom_tgts[0],
|
||||
&self.views.tgt_color_pp,
|
||||
&self.sampler,
|
||||
&self.depth_sampler,
|
||||
@ -625,7 +655,7 @@ impl Renderer {
|
||||
device: &wgpu::Device,
|
||||
size: (u32, u32),
|
||||
mode: &RenderMode,
|
||||
) -> Result<Views, RenderError> {
|
||||
) -> Result<(Views, [Vec2<f32>; bloom::NUM_SIZES]), RenderError> {
|
||||
let upscaled = Vec2::<u32>::from(size)
|
||||
.map(|e| (e as f32 * mode.upscale_mode.factor) as u32)
|
||||
.into_tuple();
|
||||
@ -637,7 +667,7 @@ impl Renderer {
|
||||
};
|
||||
let levels = 1;
|
||||
|
||||
let color_view = || {
|
||||
let color_view = |width, height| {
|
||||
let tex = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
size: wgpu::Extent3d {
|
||||
@ -665,8 +695,19 @@ impl Renderer {
|
||||
})
|
||||
};
|
||||
|
||||
let tgt_color_view = color_view();
|
||||
let tgt_color_pp_view = color_view();
|
||||
let tgt_color_view = color_view(width, height);
|
||||
let tgt_color_pp_view = color_view(width, height);
|
||||
|
||||
let mut size_shift = 0;
|
||||
// TODO: skip creating bloom stuff when it is disabled
|
||||
let bloom_sizes = [(); bloom::NUM_SIZES].map(|()| {
|
||||
// .max(1) to ensure we don't create zero sized textures
|
||||
let size = Vec2::new(width, height).map(|e| (e >> size_shift).max(1));
|
||||
size_shift += 1;
|
||||
size
|
||||
});
|
||||
|
||||
let bloom_tgt_views = bloom_sizes.map(|size| color_view(size.x, size.y));
|
||||
|
||||
let tgt_depth_tex = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
@ -717,12 +758,16 @@ impl Renderer {
|
||||
array_layer_count: None,
|
||||
});
|
||||
|
||||
Ok(Views {
|
||||
tgt_color: tgt_color_view,
|
||||
tgt_depth: tgt_depth_view,
|
||||
tgt_color_pp: tgt_color_pp_view,
|
||||
_win_depth: win_depth_view,
|
||||
})
|
||||
Ok((
|
||||
Views {
|
||||
tgt_color: tgt_color_view,
|
||||
tgt_depth: tgt_depth_view,
|
||||
bloom_tgts: bloom_tgt_views,
|
||||
tgt_color_pp: tgt_color_pp_view,
|
||||
_win_depth: win_depth_view,
|
||||
},
|
||||
bloom_sizes.map(|s| s.map(|e| e as f32)),
|
||||
))
|
||||
}
|
||||
|
||||
/// Get the resolution of the render target.
|
||||
|
@ -4,8 +4,8 @@ use super::{
|
||||
instances::Instances,
|
||||
model::{DynamicModel, Model, SubModel},
|
||||
pipelines::{
|
||||
blit, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox, sprite,
|
||||
terrain, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup,
|
||||
blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox,
|
||||
sprite, terrain, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup,
|
||||
},
|
||||
},
|
||||
Renderer, ShadowMap, ShadowMapRenderer,
|
||||
@ -241,6 +241,57 @@ impl<'frame> Drawer<'frame> {
|
||||
})
|
||||
}
|
||||
|
||||
/// To be ran between the second pass and the third pass
|
||||
/// does nothing if the ingame pipelines are not yet ready
|
||||
pub fn run_bloom_passes(&mut self) {
|
||||
let pipelines = match self.borrow.pipelines.all() {
|
||||
Some(p) => p,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let locals = &self.borrow.locals;
|
||||
let views = &self.borrow.views;
|
||||
|
||||
let encoder = self.encoder.as_mut().unwrap();
|
||||
let device = self.borrow.device;
|
||||
|
||||
let mut run_bloom_pass = |bind, view, label: String, pipeline| {
|
||||
let mut render_pass =
|
||||
encoder.scoped_render_pass(&label, device, &wgpu::RenderPassDescriptor {
|
||||
label: Some(&label),
|
||||
color_attachments: &[wgpu::RenderPassColorAttachment {
|
||||
resolve_target: None,
|
||||
view,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
||||
store: true,
|
||||
},
|
||||
}],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
render_pass.set_bind_group(0, bind, &[]);
|
||||
render_pass.set_pipeline(pipeline);
|
||||
render_pass.draw(0..3, 0..1);
|
||||
};
|
||||
|
||||
// Downsample filter passes
|
||||
(0..bloom::NUM_SIZES - 1).for_each(|index| {
|
||||
let bind = &locals.bloom_binds[index].bind_group;
|
||||
let view = &views.bloom_tgts[index + 1];
|
||||
let label = format!("bloom downsample pass {}", index + 1);
|
||||
run_bloom_pass(bind, view, label, &pipelines.bloom.downsample);
|
||||
});
|
||||
|
||||
// Upsample filter passes
|
||||
(0..bloom::NUM_SIZES - 1).for_each(|index| {
|
||||
let bind = &locals.bloom_binds[bloom::NUM_SIZES - 1 - index].bind_group;
|
||||
let view = &views.bloom_tgts[bloom::NUM_SIZES - 2 - index];
|
||||
let label = format!("bloom upsample pass {}", index + 1);
|
||||
run_bloom_pass(bind, view, label, &pipelines.bloom.upsample);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn third_pass(&mut self) -> ThirdPassDrawer {
|
||||
let encoder = self.encoder.as_mut().unwrap();
|
||||
let device = self.borrow.device;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::{
|
||||
super::{
|
||||
consts::Consts,
|
||||
pipelines::{clouds, postprocess},
|
||||
pipelines::{bloom, clouds, postprocess},
|
||||
},
|
||||
Layouts,
|
||||
};
|
||||
@ -10,6 +10,8 @@ pub struct Locals {
|
||||
pub clouds: Consts<clouds::Locals>,
|
||||
pub clouds_bind: clouds::BindGroup,
|
||||
|
||||
pub bloom_binds: [bloom::BindGroup; bloom::NUM_SIZES],
|
||||
|
||||
pub postprocess: Consts<postprocess::Locals>,
|
||||
pub postprocess_bind: postprocess::BindGroup,
|
||||
}
|
||||
@ -19,9 +21,12 @@ impl Locals {
|
||||
device: &wgpu::Device,
|
||||
layouts: &Layouts,
|
||||
clouds_locals: Consts<clouds::Locals>,
|
||||
bloom_locals: [Consts<bloom::HalfPixel>; bloom::NUM_SIZES],
|
||||
postprocess_locals: Consts<postprocess::Locals>,
|
||||
tgt_color_view: &wgpu::TextureView,
|
||||
tgt_depth_view: &wgpu::TextureView,
|
||||
bloom_src_views: [&wgpu::TextureView; bloom::NUM_SIZES],
|
||||
bloom_final_tgt_view: &wgpu::TextureView,
|
||||
tgt_color_pp_view: &wgpu::TextureView,
|
||||
sampler: &wgpu::Sampler,
|
||||
depth_sampler: &wgpu::Sampler,
|
||||
@ -34,14 +39,22 @@ impl Locals {
|
||||
depth_sampler,
|
||||
&clouds_locals,
|
||||
);
|
||||
let postprocess_bind =
|
||||
layouts
|
||||
.postprocess
|
||||
.bind(device, tgt_color_pp_view, sampler, &postprocess_locals);
|
||||
let bloom_binds = bloom_src_views
|
||||
.zip(bloom_locals)
|
||||
.map(|(view, locals)| layouts.bloom.bind(device, view, sampler, locals));
|
||||
|
||||
let postprocess_bind = layouts.postprocess.bind(
|
||||
device,
|
||||
tgt_color_pp_view,
|
||||
bloom_final_tgt_view,
|
||||
sampler,
|
||||
&postprocess_locals,
|
||||
);
|
||||
|
||||
Self {
|
||||
clouds: clouds_locals,
|
||||
clouds_bind,
|
||||
bloom_binds,
|
||||
postprocess: postprocess_locals,
|
||||
postprocess_bind,
|
||||
}
|
||||
@ -53,8 +66,11 @@ impl Locals {
|
||||
layouts: &Layouts,
|
||||
// Call when these are recreated and need to be rebound
|
||||
// e.g. resizing
|
||||
bloom_locals: [Consts<bloom::HalfPixel>; bloom::NUM_SIZES],
|
||||
tgt_color_view: &wgpu::TextureView,
|
||||
tgt_depth_view: &wgpu::TextureView,
|
||||
bloom_src_views: [&wgpu::TextureView; bloom::NUM_SIZES],
|
||||
bloom_final_tgt_view: &wgpu::TextureView,
|
||||
tgt_color_pp_view: &wgpu::TextureView,
|
||||
sampler: &wgpu::Sampler,
|
||||
depth_sampler: &wgpu::Sampler,
|
||||
@ -67,9 +83,15 @@ impl Locals {
|
||||
depth_sampler,
|
||||
&self.clouds,
|
||||
);
|
||||
self.postprocess_bind =
|
||||
layouts
|
||||
.postprocess
|
||||
.bind(device, tgt_color_pp_view, sampler, &self.postprocess);
|
||||
self.bloom_binds = bloom_src_views
|
||||
.zip(bloom_locals)
|
||||
.map(|(view, locals)| layouts.bloom.bind(device, view, sampler, locals));
|
||||
self.postprocess_bind = layouts.postprocess.bind(
|
||||
device,
|
||||
tgt_color_pp_view,
|
||||
bloom_final_tgt_view,
|
||||
sampler,
|
||||
&self.postprocess,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use super::{
|
||||
super::{
|
||||
pipelines::{
|
||||
blit, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox,
|
||||
sprite, terrain, ui,
|
||||
blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow,
|
||||
skybox, sprite, terrain, ui,
|
||||
},
|
||||
AaMode, CloudMode, FluidMode, LightingMode, RenderError, RenderMode, ShadowMode,
|
||||
},
|
||||
@ -20,6 +20,7 @@ pub struct Pipelines {
|
||||
pub lod_terrain: lod_terrain::LodTerrainPipeline,
|
||||
pub particle: particle::ParticlePipeline,
|
||||
pub clouds: clouds::CloudsPipeline,
|
||||
pub bloom: bloom::BloomPipelines,
|
||||
pub postprocess: postprocess::PostProcessPipeline,
|
||||
// Consider reenabling at some time
|
||||
// player_shadow: figure::FigurePipeline,
|
||||
@ -39,6 +40,7 @@ pub struct IngamePipelines {
|
||||
lod_terrain: lod_terrain::LodTerrainPipeline,
|
||||
particle: particle::ParticlePipeline,
|
||||
clouds: clouds::CloudsPipeline,
|
||||
bloom: bloom::BloomPipelines,
|
||||
postprocess: postprocess::PostProcessPipeline,
|
||||
// Consider reenabling at some time
|
||||
// player_shadow: figure::FigurePipeline,
|
||||
@ -74,6 +76,7 @@ impl Pipelines {
|
||||
lod_terrain: ingame.lod_terrain,
|
||||
particle: ingame.particle,
|
||||
clouds: ingame.clouds,
|
||||
bloom: ingame.bloom,
|
||||
postprocess: ingame.postprocess,
|
||||
//player_shadow: ingame.player_shadow,
|
||||
skybox: ingame.skybox,
|
||||
@ -107,6 +110,8 @@ struct ShaderModules {
|
||||
lod_terrain_frag: wgpu::ShaderModule,
|
||||
clouds_vert: wgpu::ShaderModule,
|
||||
clouds_frag: wgpu::ShaderModule,
|
||||
duel_downsample_frag: wgpu::ShaderModule,
|
||||
duel_upsample_frag: wgpu::ShaderModule,
|
||||
postprocess_vert: wgpu::ShaderModule,
|
||||
postprocess_frag: wgpu::ShaderModule,
|
||||
blit_vert: wgpu::ShaderModule,
|
||||
@ -255,6 +260,8 @@ impl ShaderModules {
|
||||
lod_terrain_frag: create_shader("lod-terrain-frag", ShaderKind::Fragment)?,
|
||||
clouds_vert: create_shader("clouds-vert", ShaderKind::Vertex)?,
|
||||
clouds_frag: create_shader("clouds-frag", ShaderKind::Fragment)?,
|
||||
duel_downsample_frag: create_shader("duel-downsample-frag", ShaderKind::Fragment)?,
|
||||
duel_upsample_frag: create_shader("duel-upsample-frag", ShaderKind::Fragment)?,
|
||||
postprocess_vert: create_shader("postprocess-vert", ShaderKind::Vertex)?,
|
||||
postprocess_frag: create_shader("postprocess-frag", ShaderKind::Fragment)?,
|
||||
blit_vert: create_shader("blit-vert", ShaderKind::Vertex)?,
|
||||
@ -360,7 +367,7 @@ fn create_interface_pipelines(
|
||||
fn create_ingame_and_shadow_pipelines(
|
||||
needs: PipelineNeeds,
|
||||
pool: &rayon::ThreadPool,
|
||||
tasks: [Task; 13],
|
||||
tasks: [Task; 14],
|
||||
) -> IngameAndShadowPipelines {
|
||||
prof_span!(_guard, "create_ingame_and_shadow_pipelines");
|
||||
|
||||
@ -382,6 +389,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
particle_task,
|
||||
lod_terrain_task,
|
||||
clouds_task,
|
||||
bloom_task,
|
||||
postprocess_task,
|
||||
// TODO: if these are ever actually optionally done, counting them
|
||||
// as tasks to do beforehand seems kind of iffy since they will just
|
||||
@ -535,6 +543,22 @@ fn create_ingame_and_shadow_pipelines(
|
||||
"clouds pipeline creation",
|
||||
)
|
||||
};
|
||||
// Pipelines for rendering our bloom
|
||||
let create_bloom = || {
|
||||
bloom_task.run(
|
||||
|| {
|
||||
bloom::BloomPipelines::new(
|
||||
device,
|
||||
&shaders.blit_vert,
|
||||
&shaders.duel_downsample_frag,
|
||||
&shaders.duel_upsample_frag,
|
||||
wgpu::TextureFormat::Rgba16Float,
|
||||
&layouts.bloom,
|
||||
)
|
||||
},
|
||||
"bloom pipelines creation",
|
||||
)
|
||||
};
|
||||
// Pipeline for rendering our post-processing
|
||||
let create_postprocess = || {
|
||||
postprocess_task.run(
|
||||
@ -622,7 +646,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
};
|
||||
|
||||
let j1 = || pool.join(create_debug, || pool.join(create_skybox, create_figure));
|
||||
let j2 = || pool.join(create_terrain, create_fluid);
|
||||
let j2 = || pool.join(create_terrain, || pool.join(create_fluid, create_bloom));
|
||||
let j3 = || pool.join(create_sprite, create_particle);
|
||||
let j4 = || pool.join(create_lod_terrain, create_clouds);
|
||||
let j5 = || pool.join(create_postprocess, create_point_shadow);
|
||||
@ -636,7 +660,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
// Ignore this
|
||||
let (
|
||||
(
|
||||
((debug, (skybox, figure)), (terrain, fluid)),
|
||||
((debug, (skybox, figure)), (terrain, (fluid, bloom))),
|
||||
((sprite, particle), (lod_terrain, clouds)),
|
||||
),
|
||||
((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)),
|
||||
@ -653,6 +677,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
lod_terrain,
|
||||
particle,
|
||||
clouds,
|
||||
bloom,
|
||||
postprocess,
|
||||
skybox,
|
||||
sprite,
|
||||
|
@ -1483,6 +1483,12 @@ impl PlayState for SessionState {
|
||||
second_pass.draw_clouds();
|
||||
}
|
||||
}
|
||||
// Bloom
|
||||
// TODO: make optional
|
||||
{
|
||||
prof_span!("bloom");
|
||||
drawer.run_bloom_passes()
|
||||
}
|
||||
// PostProcess and UI
|
||||
{
|
||||
prof_span!("post-process and ui");
|
||||
|
Loading…
Reference in New Issue
Block a user