From 5dc91a31ff860c52f3dbdc1cfdfba7c1084ad16d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Bergstr=C3=B6m?= Date: Sun, 28 Mar 2021 22:47:05 +0200 Subject: [PATCH] Tonemapper options (#111) * Add a few different tonemapper options * Process imgui input before beginning imgui frame (https://github.com/ocornut/imgui/issues/3575) * Fix issue with internal #![rustfmt::skip] attribute on nightly --- .../bloom_combine.frag.cookedshaderpackage | Bin 2572 -> 11071 bytes .../generated_msl/bloom_combine.frag.metal | 106 ++++++++++++++++- demo/shaders/glsl/bloom_combine.frag | 12 +- demo/shaders/glsl/mesh.frag | 10 -- demo/shaders/glsl/tonemapping.glsl | 107 ++++++++++++++++++ demo/shaders/src/bloom_combine_frag.rs | 47 ++++++++ demo/src/lib.rs | 81 +++++++++++-- .../bloom_combine_pass.rs | 7 +- rafx-api/src/backends/empty.rs | 3 +- rafx-api/src/backends/mod.rs | 1 + 10 files changed, 345 insertions(+), 29 deletions(-) create mode 100644 demo/shaders/glsl/tonemapping.glsl diff --git a/demo/assets/shaders/bloom_combine.frag.cookedshaderpackage b/demo/assets/shaders/bloom_combine.frag.cookedshaderpackage index 2d3b22f4426c6a4a1f08412a7ea01095cde37b6f..e633e17d1d937f7c25282c766da9f6f08f556cb0 100644 GIT binary patch literal 11071 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 KD8lJseSx7>NeWV z-QBg<0{8l(3b$C`o*h>mgi+iMhNHZyQ)FMUG6Y$dprS~X|3q9AkB2R(sYg4C8D%bQ z^oO0eY>Tj78txBVoZIIG%TuG&qgb(u%<^#G1yQ`aJ8wI~SgJx^Gnv~b9BpOTE5=qd z+^m`_61l@9wOKew`c86?GzL);^rC+5($&>|6uU5`9n zlnwKAm>e}0o6U04$2ZZm?7Drbd$Ou(gynYDPd3@|t810*G}O(!t}pweou-|8bp$(n zWx!a`W%ZgQuB&E>>aD}0pX`Ed94m8Wcgu~%R@>i0-M9R{*Q@Te77{TLy|LDh)s<7H zM;P-u*6Ppd_2P5*W2*cdCQi+sF{Wmw&3W@#X)>40DOrp;XHFM#bL-(&OW#RrOMnv=Zj3cwYRbaM)EqrwL^>=1&DpnzLqeE4csdgCKL-T$sBcdlGhC z8VvV-aGg8@QwOuTy}h~hRnSQieW*u$7qIi78>K=;2M*ly7_R!Z#{g48jQ56qR(%u` zlAO}NHzfUbb(Cd7y&Izq>pWT9hb@=BNpoEX;n{8B|3G>pNRJSQFB$6{lC`C?w0)u|!I=9#!T>Es}m zI_S(8+ZE|8Nh^lPi@qTCb=x`4;qLty@uh0l@P%%1n@Ytb{iMqiho*_;u>V@sx}`Jq@NMYE+z%T$=w()nZCkguk{^n(!B9=i%sl=hoyYOFrDKC3kEgzs z-V}pjx4%`kPjsdZK6+xt*r;J8KNVxUFFgBO6jPJEf?=0SV(i}tgT5>#4)V4Yow3oA zx8131-05pgfEnBs6AvCgx#^2{hVMUR_*_gLh`sQ3Ao6$`_t$t#kpJW6fq-5B(g9Tc zRr5aZ{!GD|uc*I_-cFe2Q4;?Hc*k?FuL{IF$tLnH%rCjT`f?AZ<~>D*DiD-sZ+}7l E52fvMnE(I) diff --git a/demo/shaders/generated_msl/bloom_combine.frag.metal b/demo/shaders/generated_msl/bloom_combine.frag.metal index ee5eb22cf..448a9bc5d 100644 --- a/demo/shaders/generated_msl/bloom_combine.frag.metal +++ b/demo/shaders/generated_msl/bloom_combine.frag.metal @@ -1,12 +1,20 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + #include #include using namespace metal; +struct Config +{ + int tonemapper_type; +}; + struct spvDescriptorSetBuffer0 { texture2d in_color [[id(0)]]; texture2d in_blur [[id(1)]]; + constant Config* config [[id(3)]]; }; struct main0_out @@ -19,13 +27,107 @@ struct main0_in float2 inUV [[user(locn0)]]; }; +static inline __attribute__((always_inline)) +float3 RRT_and_ODT_fit(thread const float3& v) +{ + float3 a = (v * (v + float3(0.02457859925925731658935546875))) - float3(9.0537003416102379560470581054688e-05); + float3 b = (v * ((v * 0.98372900485992431640625) + float3(0.4329510033130645751953125))) + float3(0.23808099329471588134765625); + return a / b; +} + +static inline __attribute__((always_inline)) +float3 tonemap_aces_fitted(thread float3& color) +{ + color = float3x3(float3(0.59719002246856689453125, 0.354579985141754150390625, 0.048229999840259552001953125), float3(0.075999997556209564208984375, 0.908339977264404296875, 0.0156599991023540496826171875), float3(0.0284000001847743988037109375, 0.13382999598979949951171875, 0.837769985198974609375)) * color; + float3 param = color; + color = RRT_and_ODT_fit(param); + color = float3x3(float3(1.60475003719329833984375, -0.5310800075531005859375, -0.0736699998378753662109375), float3(-0.10208000242710113525390625, 1.108129978179931640625, -0.00604999996721744537353515625), float3(-0.00326999998651444911956787109375, -0.07276000082492828369140625, 1.0760200023651123046875)) * color; + color = fast::clamp(color, float3(0.0), float3(1.0)); + return color; +} + +static inline __attribute__((always_inline)) +float3 tonemap_aces_film_simple(thread const float3& x) +{ + float a = 2.5099999904632568359375; + float b = 0.02999999932944774627685546875; + float c = 2.4300000667572021484375; + float d = 0.589999973773956298828125; + float e = 0.14000000059604644775390625; + return fast::clamp((x * ((x * a) + float3(b))) / ((x * ((x * c) + float3(d))) + float3(e)), float3(0.0), float3(1.0)); +} + +static inline __attribute__((always_inline)) +float3 visualize_value(thread const float& val) +{ + float g = 1.0 - ((0.20000000298023223876953125 * (val - 3.2360498905181884765625)) * (val - 3.2360498905181884765625)); + float b = val; + float r = 1.0 - (1.0 / ((0.5 * val) - 0.5)); + if (val > 1.0) + { + b = 0.0; + } + if (val < 3.0) + { + r = 0.0; + } + return fast::clamp(float3(r, g, b), float3(0.0), float3(1.0)); +} + +static inline __attribute__((always_inline)) +float luma(thread const float3& color) +{ + return dot(color, float3(0.2989999949932098388671875, 0.58700001239776611328125, 0.114000000059604644775390625)); +} + +static inline __attribute__((always_inline)) +float3 tonemap(thread const float3& color, thread const int& tonemapper_type) +{ + switch (tonemapper_type) + { + case 1: + { + float3 param = color; + float3 _196 = tonemap_aces_fitted(param); + return _196; + } + case 2: + { + float3 param_1 = color; + return tonemap_aces_film_simple(param_1); + } + case 3: + { + return color / (color + float3(1.0)); + } + case 4: + { + float max_val = fast::max(color.x, fast::max(color.y, color.z)); + float param_2 = max_val; + return visualize_value(param_2); + } + case 5: + { + float3 param_3 = color; + float l = luma(param_3); + float param_4 = l; + return visualize_value(param_4); + } + default: + { + return color; + } + } +} + fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { constexpr sampler smp(mip_filter::linear, compare_func::never, max_anisotropy(1)); main0_out out = {}; float4 color = spvDescriptorSet0.in_color.sample(smp, in.inUV) + spvDescriptorSet0.in_blur.sample(smp, in.inUV); - float3 mapped = color.xyz / (color.xyz + float3(1.0)); - out.out_sdr = float4(mapped, color.w); + float3 param = color.xyz; + int param_1 = (*spvDescriptorSet0.config).tonemapper_type; + out.out_sdr = float4(tonemap(param, param_1), color.w); return out; } diff --git a/demo/shaders/glsl/bloom_combine.frag b/demo/shaders/glsl/bloom_combine.frag index 2aa89788f..ea050c1d2 100644 --- a/demo/shaders/glsl/bloom_combine.frag +++ b/demo/shaders/glsl/bloom_combine.frag @@ -1,5 +1,6 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable +#include "tonemapping.glsl" // @[export] layout (set = 0, binding = 0) uniform texture2D in_color; @@ -19,15 +20,20 @@ layout (set = 0, binding = 1) uniform texture2D in_blur; // ])] layout (set = 0, binding = 2) uniform sampler smp; +// @[export] +// @[internal_buffer] +layout (set = 0, binding = 3) uniform Config { + int tonemapper_type; +} config; + layout (location = 0) in vec2 inUV; layout (location = 0) out vec4 out_sdr; + void main() { vec4 color = texture(sampler2D(in_color, smp), inUV) + texture(sampler2D(in_blur, smp), inUV); - // tonemapping.. TODO: implement auto-exposure - vec3 mapped = color.rgb / (color.rgb + vec3(1.0)); - out_sdr = vec4(mapped, color.a); + out_sdr = vec4(tonemap(color.rgb, config.tonemapper_type), color.a); } diff --git a/demo/shaders/glsl/mesh.frag b/demo/shaders/glsl/mesh.frag index 3ebd15bbe..b1a80581a 100644 --- a/demo/shaders/glsl/mesh.frag +++ b/demo/shaders/glsl/mesh.frag @@ -885,16 +885,6 @@ vec4 pbr_path( vec3 color = ambient + total_light + emissive_color.rgb; #endif return vec4(color, base_color.a); - - // tonemapping - //vec3 mapped = color.rgb / (color.rgb + vec3(1.0)); - - // gamma correction - //const float gamma = 2.2; - //mapped = pow(mapped, vec3(1.0 / gamma)); - - // output - //return vec4(mapped, base_color.a); } diff --git a/demo/shaders/glsl/tonemapping.glsl b/demo/shaders/glsl/tonemapping.glsl new file mode 100644 index 000000000..3c29ddc94 --- /dev/null +++ b/demo/shaders/glsl/tonemapping.glsl @@ -0,0 +1,107 @@ +// The code for ACESFitted was originally written by Stephen Hill (@self_shadow), who deserves all +// credit for coming up with this fit and implementing it. Buy him a beer next time you see him. :) +// The code is licensed under the MIT license. The code has been converted to glsl, and was originally found at: +// https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl + +// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT +const mat3 ACESInputMat = +{ + {0.59719, 0.35458, 0.04823}, + {0.07600, 0.90834, 0.01566}, + {0.02840, 0.13383, 0.83777} +}; + +// ODT_SAT => XYZ => D60_2_D65 => sRGB +const mat3 ACESOutputMat = +{ + { 1.60475, -0.53108, -0.07367}, + {-0.10208, 1.10813, -0.00605}, + {-0.00327, -0.07276, 1.07602} +}; + +vec3 RRT_and_ODT_fit(vec3 v) +{ + vec3 a = v * (v + 0.0245786f) - 0.000090537f; + vec3 b = v * (0.983729f * v + 0.4329510f) + 0.238081f; + return a / b; +} + +vec3 tonemap_aces_fitted(vec3 color) +{ + color = ACESInputMat * color; + + // Apply RRT and ODT + color = RRT_and_ODT_fit(color); + + color = ACESOutputMat * color; + + // Clamp to [0, 1] + color = clamp(color, 0, 1); + + return color; +} + +// source: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ +vec3 tonemap_aces_film_simple(vec3 x) +{ + float a = 2.51f; + float b = 0.03f; + float c = 2.43f; + float d = 0.59f; + float e = 0.14f; + return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0.0, 1.0); +} + +float luma(vec3 color) { + return dot(color, vec3(0.299, 0.587, 0.114)); +} + +vec3 visualize_value(float val) { + // blue is used to visualize 0-1 exclusively in a linear fashion. + // green covers 0-5 and is a parabolic curve, so it transitions over into red. + // red is 3+ and is a log-ish curve so that it can handle differences in very large values + float g = 1 - 0.2 * (val - 3.23605) * (val - 3.23605); + float b = val; + float r = 1 - 1 / (0.5 * val - 0.5); + // the transition blue -> green is hard, to make it easier to spot when values go over 1 + if (val > 1.0) { + b = 0; + } + if (val < 3.0) { + r = 0; + } + return clamp(vec3(r, g, b), 0, 1); +} + +// Should be kept in sync with the constants in TonemapperType +const int TM_StephenHillACES = 1; +const int TM_SimplifiedLumaACES = 2; +const int TM_LogDerivative = 3; +const int TM_VisualizeRGBMax = 4; +const int TM_VisualizeLuma = 5; + +vec3 tonemap(vec3 color, int tonemapper_type) { + // tonemapping.. TODO: implement auto-exposure + switch (tonemapper_type) { + case TM_StephenHillACES: { + return tonemap_aces_fitted(color.rgb); + } break; + case TM_SimplifiedLumaACES: { + return tonemap_aces_film_simple(color.rgb); + } break; + case TM_LogDerivative: { + return color.rgb / (color.rgb + vec3(1.0)); + } break; + case TM_VisualizeRGBMax: { + float max_val = max(color.r, max(color.g, color.b)); + return visualize_value(max_val); + } break; + case TM_VisualizeLuma: { + float l = luma(color.rgb); + return visualize_value(l); + } break; + default: { + return color; + } break; + } +} \ No newline at end of file diff --git a/demo/shaders/src/bloom_combine_frag.rs b/demo/shaders/src/bloom_combine_frag.rs index 99adb8d3f..26523aa96 100644 --- a/demo/shaders/src/bloom_combine_frag.rs +++ b/demo/shaders/src/bloom_combine_frag.rs @@ -9,16 +9,37 @@ use rafx_framework::{ ImageViewResource, ResourceArc, }; +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct ConfigStd140 { + pub tonemapper_type: i32, // +0 (size: 4) + pub _padding0: [u8; 12], // +4 (size: 12) +} // 16 bytes + +impl Default for ConfigStd140 { + fn default() -> Self { + ConfigStd140 { + tonemapper_type: ::default(), + _padding0: [u8::default(); 12], + } + } +} + +pub type ConfigUniform = ConfigStd140; + pub const IN_COLOR_DESCRIPTOR_SET_INDEX: usize = 0; pub const IN_COLOR_DESCRIPTOR_BINDING_INDEX: usize = 0; pub const IN_BLUR_DESCRIPTOR_SET_INDEX: usize = 0; pub const IN_BLUR_DESCRIPTOR_BINDING_INDEX: usize = 1; pub const SMP_DESCRIPTOR_SET_INDEX: usize = 0; pub const SMP_DESCRIPTOR_BINDING_INDEX: usize = 2; +pub const CONFIG_DESCRIPTOR_SET_INDEX: usize = 0; +pub const CONFIG_DESCRIPTOR_BINDING_INDEX: usize = 3; pub struct DescriptorSet0Args<'a> { pub in_color: &'a ResourceArc, pub in_blur: &'a ResourceArc, + pub config: &'a ConfigUniform, } impl<'a> DescriptorSetInitializer<'a> for DescriptorSet0Args<'a> { @@ -53,6 +74,7 @@ impl DescriptorSet0 { ) { descriptor_set.set_image(IN_COLOR_DESCRIPTOR_BINDING_INDEX as u32, args.in_color); descriptor_set.set_image(IN_BLUR_DESCRIPTOR_BINDING_INDEX as u32, args.in_blur); + descriptor_set.set_buffer_data(CONFIG_DESCRIPTOR_BINDING_INDEX as u32, args.config); } pub fn set_args( @@ -61,6 +83,7 @@ impl DescriptorSet0 { ) { self.set_in_color(args.in_color); self.set_in_blur(args.in_blur); + self.set_config(args.config); } pub fn set_in_color( @@ -79,6 +102,14 @@ impl DescriptorSet0 { .set_image(IN_BLUR_DESCRIPTOR_BINDING_INDEX as u32, in_blur); } + pub fn set_config( + &mut self, + config: &ConfigUniform, + ) { + self.0 + .set_buffer_data(CONFIG_DESCRIPTOR_BINDING_INDEX as u32, config); + } + pub fn flush( &mut self, descriptor_set_allocator: &mut DescriptorSetAllocator, @@ -86,3 +117,19 @@ impl DescriptorSet0 { self.0.flush(descriptor_set_allocator) } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_struct_config_std140() { + assert_eq!(std::mem::size_of::(), 16); + assert_eq!(std::mem::size_of::(), 4); + assert_eq!(std::mem::align_of::(), 4); + assert_eq!(memoffset::offset_of!(ConfigStd140, tonemapper_type), 0); + assert_eq!(std::mem::size_of::<[u8; 12]>(), 12); + assert_eq!(std::mem::align_of::<[u8; 12]>(), 1); + assert_eq!(memoffset::offset_of!(ConfigStd140, _padding0), 4); + } +} diff --git a/demo/src/lib.rs b/demo/src/lib.rs index ddbd6ffb2..1b2678cf8 100644 --- a/demo/src/lib.rs +++ b/demo/src/lib.rs @@ -30,12 +30,53 @@ mod time; mod demo_plugin; pub use demo_plugin::DemoRendererPlugin; +// Should be kept in sync with the constants in tonemapper.glsl +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub enum TonemapperType { + None, + StephenHillACES, + SimplifiedLumaACES, + LogDerivative, + VisualizeRGBMax, + VisualizeLuma, + MAX, +} +impl TonemapperType { + pub fn display_name(&self) -> &'static str { + match self { + TonemapperType::None => "None", + TonemapperType::StephenHillACES => "Stephen Hill ACES", + TonemapperType::SimplifiedLumaACES => "SimplifiedLumaACES", + TonemapperType::LogDerivative => "LogDerivative", + TonemapperType::VisualizeRGBMax => "Visualize RGB Max", + TonemapperType::VisualizeLuma => "Visualize RGB Luma", + TonemapperType::MAX => "MAX_TONEMAPPER_VALUE", + } + } +} +impl From for TonemapperType { + fn from(v: i32) -> Self { + assert!(v <= Self::MAX as i32); + unsafe { std::mem::transmute(v) } + } +} + +impl std::fmt::Display for TonemapperType { + fn fmt( + &self, + f: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + write!(f, "{}", self.display_name()) + } +} #[derive(Clone)] pub struct RenderOptions { pub enable_msaa: bool, pub enable_hdr: bool, pub enable_bloom: bool, pub blur_pass_count: usize, + pub tonemapper_type: TonemapperType, } impl Default for RenderOptions { @@ -45,6 +86,7 @@ impl Default for RenderOptions { enable_hdr: true, enable_bloom: true, blur_pass_count: 5, + tonemapper_type: TonemapperType::LogDerivative, } } } @@ -80,6 +122,24 @@ impl RenderOptions { .build(ui, &mut blur_pass_count); self.blur_pass_count = blur_pass_count as usize; + // iterate over the valid tonemapper values and convert them into their names + let tonemapper_names: Vec = (0..(TonemapperType::MAX as i32)) + .map(|t| imgui::ImString::new(TonemapperType::from(t).display_name())) + .collect(); + let mut current_tonemapper_type = self.tonemapper_type as i32; + if let Some(combo) = imgui::ComboBox::new(imgui::im_str!("tonemapper_type")) + .preview_value(&tonemapper_names[current_tonemapper_type as usize]) + .begin(ui) + { + ui.list_box( + imgui::im_str!(""), + &mut current_tonemapper_type, + &tonemapper_names.iter().collect::>(), + tonemapper_names.len() as i32, + ); + combo.end(ui); + self.tonemapper_type = current_tonemapper_type.into(); + } } } @@ -179,17 +239,6 @@ pub fn run(args: &DemoArgs) -> RafxResult<()> { } } - // - // Notify imgui of frame begin - // - #[cfg(feature = "use-imgui")] - { - use crate::features::imgui::Sdl2ImguiManager; - use sdl2::mouse::MouseState; - let imgui_manager = resources.get::().unwrap(); - imgui_manager.begin_frame(&sdl2_systems.window, &MouseState::new(&event_pump)); - } - // // Update assets // @@ -215,6 +264,16 @@ pub fn run(args: &DemoArgs) -> RafxResult<()> { if !process_input(&resources, &mut event_pump) { break 'running; } + // + // Notify imgui of frame begin + // + #[cfg(feature = "use-imgui")] + { + use crate::features::imgui::Sdl2ImguiManager; + use sdl2::mouse::MouseState; + let imgui_manager = resources.get::().unwrap(); + imgui_manager.begin_frame(&sdl2_systems.window, &MouseState::new(&event_pump)); + } { resources diff --git a/demo/src/render_graph_generator/bloom_combine_pass.rs b/demo/src/render_graph_generator/bloom_combine_pass.rs index df7a528cd..30ad9c159 100644 --- a/demo/src/render_graph_generator/bloom_combine_pass.rs +++ b/demo/src/render_graph_generator/bloom_combine_pass.rs @@ -1,4 +1,4 @@ -use crate::phases::PostProcessRenderPhase; +use crate::{phases::PostProcessRenderPhase, RenderOptions}; use rafx::framework::{MaterialPassResource, ResourceArc}; use rafx::graph::*; use rafx::nodes::RenderPhase; @@ -18,6 +18,7 @@ pub(super) fn bloom_combine_pass( bloom_extract_pass: &BloomExtractPass, blurred_color: RenderGraphImageUsageId, ) -> BloomCombinePass { + let render_options = context.extract_resources.fetch::().clone(); let node = context .graph .add_node("BloomCombine", RenderGraphQueue::DefaultGraphics); @@ -77,6 +78,10 @@ pub(super) fn bloom_combine_pass( shaders::bloom_combine_frag::DescriptorSet0Args { in_color: &sdr_image, in_blur: &hdr_image, + config: &shaders::bloom_combine_frag::ConfigStd140 { + tonemapper_type: render_options.tonemapper_type as i32, + ..Default::default() + }, }, )?; diff --git a/rafx-api/src/backends/empty.rs b/rafx-api/src/backends/empty.rs index faa6ece5e..e6e6787fe 100644 --- a/rafx-api/src/backends/empty.rs +++ b/rafx-api/src/backends/empty.rs @@ -1,5 +1,4 @@ // Don't use standard formatting in this file -#![rustfmt::skip] #![allow(unused_attributes)] #![allow(unused_variables)] @@ -15,7 +14,7 @@ impl RafxApiEmpty { pub fn destroy(&mut self) -> RafxResult<()> { unimplemented!() } } - + #[derive(Clone)] pub struct RafxDeviceContextEmpty; impl RafxDeviceContextEmpty { diff --git a/rafx-api/src/backends/mod.rs b/rafx-api/src/backends/mod.rs index dc4bc04d6..b7fc83c45 100644 --- a/rafx-api/src/backends/mod.rs +++ b/rafx-api/src/backends/mod.rs @@ -9,4 +9,5 @@ pub mod vulkan; not(any(feature = "rafx-metal", feature = "rafx-vulkan")) ))] #[doc(hidden)] +#[rustfmt::skip] pub mod empty;