Add bloom passes in character select screen, use proper source image for bloom after clouds are applied, add blurred and less blurred stages of bloom together (experimental could remove), add filtering downsample pass that is not yet used

This commit is contained in:
Imbris 2021-07-24 00:48:26 -04:00
parent 74e01f10e3
commit 29bfee26c0
10 changed files with 193 additions and 90 deletions

View File

@ -0,0 +1,73 @@
#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)
// TODO: refactor in rust
uniform u_locals {
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);
}
// check whether the texel color is higher than threshold, if so output as brightness color
vec4 filterDim(vec4 color) {
// TODO: note where this constant came from if we keep it
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
if(brightness > 1.0)
return vec4(color.rgb, 1.0);
else
return vec4(0.0, 0.0, 0.0, 1.0);
}
vec4 filteredFetch(ivec2 uv) {
return filterDim(simplesample(uv));
}
// Derived 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 filteredDownsample(vec2 uv, vec2 halfpixel) {
vec2 tex_res = 2.0 / halfpixel;
// coordinate of the top left texel
// _ _ _ _
// |x|_|_|_|
// |_|_|_|_|
// |_|_|_|_|
// |_|_|_|_|
//
ivec2 tl_coord = ivec2(uv * tex_res + vec2(-1.5, 1.5));
// Fetch inner square
vec4 sum = filteredFetch(tl_coord + ivec2(1, 1));
sum += filteredFetch(tl_coord + ivec2(2, 1));
sum += filteredFetch(tl_coord + ivec2(1, 2));
sum += filteredFetch(tl_coord + ivec2(2, 2));
// Weight inner square
sum *= 5.0;
// Fetch border
sum += filteredFetch(tl_coord + ivec2(0, 0));
sum += filteredFetch(tl_coord + ivec2(1, 0));
sum += filteredFetch(tl_coord + ivec2(2, 0));
sum += filteredFetch(tl_coord + ivec2(3, 0));
sum += filteredFetch(tl_coord + ivec2(0, 1));
sum += filteredFetch(tl_coord + ivec2(3, 1));
sum += filteredFetch(tl_coord + ivec2(0, 2));
sum += filteredFetch(tl_coord + ivec2(3, 2));
sum += filteredFetch(tl_coord + ivec2(0, 3));
sum += filteredFetch(tl_coord + ivec2(1, 3));
sum += filteredFetch(tl_coord + ivec2(2, 3));
sum += filteredFetch(tl_coord + ivec2(3, 3));
return sum / 32.0;
}
void main() {
tgt_color = filteredDownsample(uv, halfpixel);
}

View File

@ -185,8 +185,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.75;
aa_color += bloom;
// divide by 4.0 to account for adding blurred layers together
vec4 bloom = textureLod(sampler2D(t_src_bloom, s_src_color), uv, 0) / 4.0;
float bloom_factor = 0.10;
aa_color = aa_color * (1.0 - bloom_factor) + bloom * bloom_factor;
// Tonemapping
float exposure_offset = 1.0;

View File

@ -257,6 +257,11 @@ impl PlayState for CharSelectionState {
if let Some(mut second_pass) = drawer.second_pass() {
second_pass.draw_clouds();
}
// Bloom
// TODO: make optional
{
drawer.run_bloom_passes()
}
// PostProcess and UI
let mut third_pass = drawer.third_pass();
third_pass.draw_postprocess();

View File

@ -1,7 +1,12 @@
//! Based on: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
use super::super::Consts;
use bytemuck::{Pod, Zeroable};
use vek::*;
// TODO: auto-tune the number of passes to maintain roughly constant blur per
// unit of FOV so changing resolution / FOV doesn't change the blur appearance
// significantly
/// 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.
@ -98,7 +103,7 @@ impl BloomLayout {
}
pub struct BloomPipelines {
//pub downsample_filtered: wgpu::RenderPipeline,
pub downsample_filtered: wgpu::RenderPipeline,
pub downsample: wgpu::RenderPipeline,
pub upsample: wgpu::RenderPipeline,
}
@ -107,7 +112,7 @@ impl BloomPipelines {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
//downsample_filtered_fs_module: &wgpu::ShaderModule,
downsample_filtered_fs_module: &wgpu::ShaderModule,
downsample_fs_module: &wgpu::ShaderModule,
upsample_fs_module: &wgpu::ShaderModule,
target_format: wgpu::TextureFormat,
@ -121,8 +126,9 @@ impl BloomPipelines {
bind_group_layouts: &[&layout.layout],
});
let downsample_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Bloom downsample pipeline"),
let create_pipeline = |label, fs_module, blend| {
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some(label),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
@ -145,51 +151,44 @@ impl BloomPipelines {
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: downsample_fs_module,
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: target_format,
blend: None,
blend,
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: &[],
let downsample_filtered_pipeline = create_pipeline(
"Bloom downsample filtered pipeline",
downsample_filtered_fs_module,
None,
);
let downsample_pipeline =
create_pipeline("Bloom downsample pipeline", downsample_fs_module, None);
let upsample_pipeline = create_pipeline(
"Bloom upsample pipeline",
upsample_fs_module,
Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
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,
// we don't really use this, but... we need something here
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
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_filtered: downsample_filtered_pipeline,
downsample: downsample_pipeline,
upsample: upsample_pipeline,
}

View File

@ -399,7 +399,7 @@ impl Renderer {
&views.tgt_color,
&views.tgt_depth,
[
&views.tgt_color,
&views.tgt_color_pp,
&views.bloom_tgts[1],
&views.bloom_tgts[2],
&views.bloom_tgts[3],
@ -576,7 +576,7 @@ impl Renderer {
&self.views.tgt_color,
&self.views.tgt_depth,
[
&self.views.tgt_color,
&self.views.tgt_color_pp,
&self.views.bloom_tgts[1],
&self.views.bloom_tgts[2],
&self.views.bloom_tgts[3],

View File

@ -252,20 +252,18 @@ impl<'frame> Drawer<'frame> {
let locals = &self.borrow.locals;
let views = &self.borrow.views;
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut encoder = self.encoder.as_mut().unwrap().scope("bloom", device);
let mut run_bloom_pass = |bind, view, label: String, pipeline| {
let mut run_bloom_pass = |bind, view, label: String, pipeline, load| {
let pass_label = format!("bloom {} pass", label);
let mut render_pass =
encoder.scoped_render_pass(&label, device, &wgpu::RenderPassDescriptor {
label: Some(&label),
label: Some(&pass_label),
color_attachments: &[wgpu::RenderPassColorAttachment {
resolve_target: None,
view,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
ops: wgpu::Operations { store: true, load },
}],
depth_stencil_attachment: None,
});
@ -279,16 +277,35 @@ impl<'frame> Drawer<'frame> {
(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);
let label = format!("downsample {}", index + 1);
run_bloom_pass(
bind,
view,
label,
&pipelines.bloom.downsample,
wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
);
});
// 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);
let label = format!("upsample {}", index + 1);
run_bloom_pass(
bind,
view,
label,
&pipelines.bloom.upsample,
if index + 2 == bloom::NUM_SIZES {
// Clear for the final image since that is just stuff from the pervious frame.
wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT)
} else {
// Add to less blurred images to get gradient of blur instead of a smudge>
// https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/
wgpu::LoadOp::Load
},
);
});
}

View File

@ -110,8 +110,9 @@ 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,
dual_downsample_filtered_frag: wgpu::ShaderModule,
dual_downsample_frag: wgpu::ShaderModule,
dual_upsample_frag: wgpu::ShaderModule,
postprocess_vert: wgpu::ShaderModule,
postprocess_frag: wgpu::ShaderModule,
blit_vert: wgpu::ShaderModule,
@ -260,8 +261,12 @@ 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)?,
dual_downsample_filtered_frag: create_shader(
"dual-downsample-filtered-frag",
ShaderKind::Fragment,
)?,
dual_downsample_frag: create_shader("dual-downsample-frag", ShaderKind::Fragment)?,
dual_upsample_frag: create_shader("dual-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)?,
@ -550,8 +555,9 @@ fn create_ingame_and_shadow_pipelines(
bloom::BloomPipelines::new(
device,
&shaders.blit_vert,
&shaders.duel_downsample_frag,
&shaders.duel_upsample_frag,
&shaders.dual_downsample_filtered_frag,
&shaders.dual_downsample_frag,
&shaders.dual_upsample_frag,
wgpu::TextureFormat::Rgba16Float,
&layouts.bloom,
)

View File

@ -68,8 +68,9 @@ impl assets::Compound for Shaders {
"lod-terrain-frag",
"clouds-vert",
"clouds-frag",
"duel-downsample-frag",
"duel-upsample-frag",
"dual-downsample-filtered-frag",
"dual-downsample-frag",
"dual-upsample-frag",
"clouds-frag",
"postprocess-vert",
"postprocess-frag",