use super::{ProgramSettings, GLSL_VERSION, default_settings, fbo};
use rin_gl::{self as gl, uniforms, Renderer3d};
#[cfg(feature="gaussian_bloom")]
use gl::{types::GLenum, fbo::TextureLevelRef};
use rin_util::{Result, Error, LogErr};
use rin_math::{clamp, vec2, vec4};
use std::rc::Rc;
#[cfg(feature="gaussian_bloom")]
use std::cell::Cell;
use std::borrow::Borrow;
use rin_graphics::Vertex2DTex;
use rin_events::{RangedPropertyMut, RangedProperty, Property, ParamsDefault};
#[cfg(gui)]
use rin_gui::{ParameterGroup, EnumParam};
#[cfg(feature="gaussian_bloom")]
use std::f32;
#[cfg(feature="gaussian_bloom")]
use color::consts::BLACK;
#[cfg(glsl_debug)]
use rin_util::AutoLoader;
pub struct Bloom {
#[cfg(not(feature="gaussian_bloom"))]
fbo_bloom_down: Vec<gl::Fbo>,
#[cfg(not(feature="gaussian_bloom"))]
fbo_bloom_up: Vec<gl::Fbo>,
#[cfg(not(feature="gaussian_bloom"))]
fbo_bloom_upsample_final: gl::Fbo,
#[cfg(feature="gaussian_bloom")]
bloom_levels: Rc<gl::Texture>,
#[cfg(feature="gaussian_bloom")]
fbo_bloom_bright: gl::Fbo,
#[cfg(feature="gaussian_bloom")]
fbo_bloom_levels: Vec<[gl::Fbo<gl::fbo::TextureLevelRef<Rc<gl::Texture>>>;2]>,
bright: gl::Program,
#[cfg(feature="gaussian_bloom")]
blur_v: gl::Program,
#[cfg(feature="gaussian_bloom")]
blur_h: gl::Program,
#[cfg(feature="gaussian_bloom")]
blur_v5: gl::Program,
#[cfg(feature="gaussian_bloom")]
blur_h5: gl::Program,
#[cfg(not(feature="gaussian_bloom"))]
bloom_downsample: gl::Program,
#[cfg(not(feature="gaussian_bloom"))]
bloom_upsample: gl::Program,
#[cfg(not(feature="gaussian_bloom"))]
bloom_upsample_final: gl::Program,
fullscreen_quad: Rc<gl::SimpleVao<Vertex2DTex>>,
#[cfg(glsl_debug)]
bright_loader: AutoLoader<gl::Program>,
#[cfg(glsl_debug)]
#[cfg(feature="gaussian_bloom")]
blur_v_loader: AutoLoader<gl::Program>,
#[cfg(glsl_debug)]
#[cfg(feature="gaussian_bloom")]
blur_h_loader: AutoLoader<gl::Program>,
#[cfg(glsl_debug)]
#[cfg(not(feature="gaussian_bloom"))]
bloom_downsample_loader: AutoLoader<gl::Program>,
#[cfg(glsl_debug)]
#[cfg(not(feature="gaussian_bloom"))]
bloom_upsample_loader: AutoLoader<gl::Program>,
#[cfg(glsl_debug)]
#[cfg(not(feature="gaussian_bloom"))]
bloom_upsample_final_loader: AutoLoader<gl::Program>,
#[cfg(feature="gaussian_bloom")]
bloom_sigma: Cell<f32>,
#[cfg(feature="gaussian_bloom")]
bloom_passes: Cell<Option<u8>>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(gui, derive(EnumParam))]
#[allow(non_camel_case_types)]
pub enum BloomBlend{
Screen,
Add,
SoftLight,
HardLight,
VividLight,
Replace
}
impl BloomBlend{
pub fn to_str(self) -> &'static str{
match self{
BloomBlend::SoftLight => "BLOOM_BLEND_SOFTLIGHT",
BloomBlend::HardLight => "BLOOM_BLEND_HARDLIGHT",
BloomBlend::VividLight => "BLOOM_BLEND_VIVIDLIGHT",
BloomBlend::Screen => "BLOOM_BLEND_SCREEN",
BloomBlend::Add => "BLOOM_BLEND_ADD",
BloomBlend::Replace => "BLOOM_BLEND_REPLACE",
}
}
}
impl Default for BloomBlend{
fn default() -> BloomBlend{
BloomBlend::Screen
}
}
#[derive(Clone, Debug, ParamsDefault)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(gui, derive(ParameterGroup))]
pub struct BloomParameters{
#[cfg(feature="gaussian_bloom")]
#[param(default = 0.9, range = 0.0001 .. 10.0)]
pub sigma: RangedPropertyMut<'static, f32>,
#[param(default = 6.5, range = 0. .. 10.)]
pub radius: RangedPropertyMut<'static, f32>,
#[param(default = 0.8, range = 0. .. 10.)]
pub threshold: RangedPropertyMut<'static, f32>,
#[param(default = 0.5, range = 0. .. 1.)]
pub knee: RangedPropertyMut<'static, f32>,
#[param(default = 0.2, range = 0. .. 2.)]
pub intensity: RangedPropertyMut<'static,f32>,
#[cfg_attr(feature = "serialize", serde(skip_serializing))]
#[param(with = bloom_response_function(&threshold, &knee, &intensity))]
pub response_function: RangedProperty<'static, Vec<f32>, f32>,
pub blend: Property<'static, BloomBlend>,
}
fn bright_settings() -> ProgramSettings{
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("PASS_H".to_string(), "".to_string()),
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/bright.fs.glsl")),
],
.. default_settings()
}
}
#[cfg(feature="gaussian_bloom")]
fn blur_v_settings(kernel_size: u8) -> ProgramSettings{
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("PASS_V".to_string(), "".to_string()),
if kernel_size == 9 {
("BLUR9".to_string(), "".to_string())
}else if kernel_size == 5{
("BLUR5".to_string(), "".to_string())
}else{
unreachable!()
},
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/blur.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/blur.fs.glsl")),
],
.. default_settings()
}
}
#[cfg(feature="gaussian_bloom")]
fn blur_h_settings(kernel_size: u8) -> ProgramSettings{
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("PASS_H".to_string(), "".to_string()),
if kernel_size == 9 {
("BLUR9".to_string(), "".to_string())
}else if kernel_size == 5{
("BLUR5".to_string(), "".to_string())
}else{
unreachable!()
},
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/blur.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/blur.fs.glsl")),
],
.. default_settings()
}
}
#[cfg(not(feature="gaussian_bloom"))]
fn bloom_downsample_settings() -> ProgramSettings{
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::Medium,
extensions: vec![],
defines: vec![
("DOWNSAMPLE".to_string(), "1".to_string()),
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/kino_bloom.frag")),
],
.. default_settings()
}
}
#[cfg(not(feature="gaussian_bloom"))]
fn bloom_upsample_settings() -> ProgramSettings{
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::Medium,
extensions: vec![],
defines: vec![
("UPSAMPLE".to_string(), "1".to_string()),
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/kino_bloom.frag")),
],
.. default_settings()
}
}
#[cfg(not(feature="gaussian_bloom"))]
fn bloom_upsample_final_settings() -> ProgramSettings{
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::Medium,
extensions: vec![],
defines: vec![
("UPSAMPLE_FINAL".to_string(), "1".to_string()),
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/kino_bloom.frag")),
],
.. default_settings()
}
}
#[cfg(feature="gaussian_bloom")]
fn gaussian(x: f32, mu: f32, sigma: f32) -> f32{
let d = x - mu;
let n = 1.0 / (f32::consts::TAU.sqrt() * sigma);
(-d * d/(2.0 * sigma * sigma)).exp() * n
}
impl Bloom {
pub fn new(gl: &gl::Renderer, w: u32, h: u32, format: gl::fbo::ColorFormat, fullscreen_quad: Rc<gl::SimpleVao<Vertex2DTex>>) -> Result<Bloom>{
#[cfg(feature="gaussian_bloom")]
let fbo_bloom_bright = fbo(gl, w, h, format)
.map_err(|err| Error::with_cause("Couldn't create blur fbo", err))?;
#[cfg(feature="gaussian_bloom")]
let (bloom_levels, fbo_bloom_levels) = {
let mut fbos = vec![];
let mut texture_h = gl.new_texture().from_format(gl::texture::Format::Texture2D{
internal_format: format as GLenum,
width: w,
height: h,
levels: 8,
}).map_err(|err| Error::with_cause("Creating h bloom levels", err))?;
let mut texture_v = gl.new_texture().from_format(gl::texture::Format::Texture2D{
internal_format: format as GLenum,
width: w / 2,
height: h / 2,
levels: 8,
}).map_err(|err| Error::with_cause("Creating v bloom levels", err))?;
texture_h.set_min_mag_filters(gl::LINEAR_MIPMAP_NEAREST, gl::LINEAR);
texture_h.set_base_level(0);
texture_h.set_max_level(7);
texture_v.set_min_mag_filters(gl::LINEAR_MIPMAP_NEAREST, gl::LINEAR);
texture_v.set_base_level(0);
texture_v.set_max_level(7);
let texture_h = Rc::new(texture_h);
let texture_v = Rc::new(texture_v);
for level in 0..8 {
let attach_h = TextureLevelRef::new(texture_h.clone(), level);
let attach_v = TextureLevelRef::new(texture_v.clone(), level);
fbos.push([
gl.new_fbo()
.from_color_no_depth(attach_h)
.map_err(|err| Error::with_cause(
&format!("Creating h bloom fbo level {} size: {}x{}", level, texture_h.width() >> level, texture_h.height() >> level),
err
))?,
gl.new_fbo()
.from_color_no_depth(attach_v)
.map_err(|err| Error::with_cause(
&format!("Creating v bloom fbo level {} size: {}x{}", level, texture_v.width() >> level, texture_v.height() >> level),
err
))?,
]);
}
(texture_v, fbos)
};
#[cfg(not(feature="gaussian_bloom"))]
let fbo_bloom_down = {
let mut w = w;
let mut h = h;
let mut fbos = vec![];
for _ in 0..16 {
w = (w >> 1).max(2);
h = (h >> 1).max(2);
fbos.push(fbo(gl, w, h, format)
.map_err(|err| Error::with_cause("Couldn't create blur fbo", err))?);
}
fbos
};
#[cfg(not(feature="gaussian_bloom"))]
let fbo_bloom_up = {
let mut w = w;
let mut h = h;
let mut fbos = vec![];
for _ in 0..15 {
w = (w >> 1).max(2);
h = (h >> 1).max(2);
fbos.push(fbo(gl, w, h, format)
.map_err(|err| Error::with_cause("Couldn't create blur fbo", err))?);
}
fbos
};
#[cfg(not(feature="gaussian_bloom"))]
let fbo_bloom_upsample_final = fbo(gl, w, h, format)
.map_err(|err| Error::with_cause("Couldn't create blur fbo", err))?;
#[cfg(glsl_debug)]
#[cfg(feature="gaussian_bloom")]
let mut blur_v_loader = gl.new_auto_program(blur_v_settings(9));
#[cfg(glsl_debug)]
#[cfg(feature="gaussian_bloom")]
let mut blur_h_loader = gl.new_auto_program(blur_h_settings(9));
#[cfg(glsl_debug)]
#[cfg(feature="gaussian_bloom")]
let mut blur_v5_loader = gl.new_auto_program(blur_v_settings(5));
#[cfg(glsl_debug)]
#[cfg(feature="gaussian_bloom")]
let mut blur_h5_loader = gl.new_auto_program(blur_h_settings(5));
#[cfg(glsl_debug)]
let mut bright_loader = gl.new_auto_program(bright_settings());
#[cfg(glsl_debug)]
#[cfg(not(feature="gaussian_bloom"))]
let mut bloom_downsample_loader = gl.new_auto_program(bloom_downsample_settings());
#[cfg(glsl_debug)]
#[cfg(not(feature="gaussian_bloom"))]
let mut bloom_upsample_loader = gl.new_auto_program(bloom_upsample_settings());
#[cfg(glsl_debug)]
#[cfg(not(feature="gaussian_bloom"))]
let mut bloom_upsample_final_loader = gl.new_auto_program(bloom_upsample_final_settings());
#[cfg(feature="gaussian_bloom")]
let blur_v;
#[cfg(feature="gaussian_bloom")]
let blur_h;
#[cfg(feature="gaussian_bloom")]
let blur_v5;
#[cfg(feature="gaussian_bloom")]
let blur_h5;
let bright;
#[cfg(not(feature="gaussian_bloom"))]
let bloom_downsample;
#[cfg(not(feature="gaussian_bloom"))]
let bloom_upsample;
#[cfg(not(feature="gaussian_bloom"))]
let bloom_upsample_final;
#[cfg(glsl_debug)]
{
bright = bright_loader.load().log_err("")?;
#[cfg(feature="gaussian_bloom")]
{
blur_v = blur_v_loader.load().log_err("")?;
blur_h = blur_h_loader.load().log_err("")?;
blur_v5 = blur_v5_loader.load().log_err("")?;
blur_h5 = blur_h5_loader.load().log_err("")?;
}
#[cfg(not(feature="gaussian_bloom"))]
{
bloom_downsample = bloom_downsample_loader.load().log_err("")?;
bloom_upsample = bloom_upsample_loader.load().log_err("")?;
bloom_upsample_final = bloom_upsample_final_loader.load().log_err("")?;
}
}
#[cfg(not(glsl_debug))]
{
bright = gl.new_program().from_settings(bright_settings())
.log_err("")
.map_err(|err| Error::with_cause("Error loading bright shader", err))?;
#[cfg(feature="gaussian_bloom")]
{
blur_v = gl.new_program().from_settings(blur_v_settings(9))
.log_err("")
.map_err(|err| Error::with_cause("Error loading blur_v shader", err))?;
blur_h = gl.new_program().from_settings(blur_h_settings(9))
.log_err("")
.map_err(|err| Error::with_cause("Error loading blur_h shader", err))?;
blur_v5 = gl.new_program().from_settings(blur_v_settings(5))
.log_err("")
.map_err(|err| Error::with_cause("Error loading blur_v shader", err))?;
blur_h5 = gl.new_program().from_settings(blur_h_settings(5))
.log_err("")
.map_err(|err| Error::with_cause("Error loading blur_h shader", err))?;
}
#[cfg(not(feature="gaussian_bloom"))]
{
bloom_downsample = gl.new_program().from_settings(bloom_downsample_settings())
.log_err("")
.map_err(|err| Error::with_cause("Error loading bloom downsample shader", err))?;
bloom_upsample = gl.new_program().from_settings(bloom_upsample_settings())
.log_err("")
.map_err(|err| Error::with_cause("Error loading bloom upsample shader", err))?;
bloom_upsample_final = gl.new_program().from_settings(bloom_upsample_final_settings())
.log_err("")
.map_err(|err| Error::with_cause("Error loading bloom upsample shader", err))?;
}
}
#[cfg(feature="gaussian_bloom")]
let bloom_passes = Some(1);
Ok(Bloom{
#[cfg(not(feature="gaussian_bloom"))]
fbo_bloom_down,
#[cfg(not(feature="gaussian_bloom"))]
fbo_bloom_up,
#[cfg(not(feature="gaussian_bloom"))]
fbo_bloom_upsample_final,
#[cfg(feature="gaussian_bloom")]
fbo_bloom_bright,
#[cfg(feature="gaussian_bloom")]
fbo_bloom_levels,
#[cfg(feature="gaussian_bloom")]
bloom_levels,
bright,
#[cfg(not(feature="gaussian_bloom"))]
bloom_downsample,
#[cfg(not(feature="gaussian_bloom"))]
bloom_upsample,
#[cfg(not(feature="gaussian_bloom"))]
bloom_upsample_final,
#[cfg(feature="gaussian_bloom")]
blur_v,
#[cfg(feature="gaussian_bloom")]
blur_h,
#[cfg(feature="gaussian_bloom")]
blur_v5,
#[cfg(feature="gaussian_bloom")]
blur_h5,
#[cfg(glsl_debug)]
bright_loader,
#[cfg(glsl_debug)]
#[cfg(not(feature="gaussian_bloom"))]
bloom_downsample_loader,
#[cfg(glsl_debug)]
#[cfg(not(feature="gaussian_bloom"))]
bloom_upsample_loader,
#[cfg(glsl_debug)]
#[cfg(not(feature="gaussian_bloom"))]
bloom_upsample_final_loader,
#[cfg(glsl_debug)]
#[cfg(feature="gaussian_bloom")]
blur_v_loader,
#[cfg(glsl_debug)]
#[cfg(feature="gaussian_bloom")]
blur_h_loader,
#[cfg(glsl_debug)]
#[cfg(feature="gaussian_bloom")]
blur_v5_loader,
#[cfg(glsl_debug)]
#[cfg(feature="gaussian_bloom")]
blur_h5_loader,
#[cfg(feature="gaussian_bloom")]
bloom_sigma: Cell::new(0.),
#[cfg(feature="gaussian_bloom")]
bloom_passes: Cell::new(bloom_passes),
fullscreen_quad,
})
}
#[cfg(glsl_debug)]
pub fn update(&mut self){
match self.bright_loader.update(){
Ok(Some(bright)) => {
self.bright = bright;
println!("bright shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
#[cfg(feature="gaussian_bloom")]
match self.blur_v_loader.update(){
Ok(Some(blur_v)) => {
self.blur_v = blur_v;
self.bloom_sigma.set(0.);
println!("blur_v shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
#[cfg(feature="gaussian_bloom")]
match self.blur_h_loader.update(){
Ok(Some(blur_h)) => {
self.blur_h = blur_h;
self.bloom_sigma.set(0.);
println!("blur_h shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
#[cfg(feature="gaussian_bloom")]
match self.blur_v5_loader.update(){
Ok(Some(blur_v)) => {
self.blur_v = blur_v;
self.bloom_sigma.set(0.);
println!("blur_v shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
#[cfg(feature="gaussian_bloom")]
match self.blur_h5_loader.update(){
Ok(Some(blur_h)) => {
self.blur_h = blur_h;
self.bloom_sigma.set(0.);
println!("blur_h shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
#[cfg(not(feature="gaussian_bloom"))]
match self.bloom_downsample_loader.update(){
Ok(Some(bloom_downsample)) => {
self.bloom_downsample = bloom_downsample;
println!("bloom_downsample_loader shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
#[cfg(not(feature="gaussian_bloom"))]
match self.bloom_upsample_loader.update(){
Ok(Some(bloom_upsample)) => {
self.bloom_upsample = bloom_upsample;
println!("bloom_upsample_loader shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
#[cfg(not(feature="gaussian_bloom"))]
match self.bloom_upsample_final_loader.update(){
Ok(Some(bloom_upsample_final)) => {
self.bloom_upsample_final = bloom_upsample_final;
println!("bloom_upsample_loader shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
}
#[cfg(not(feature="gaussian_bloom"))]
pub fn bloom_down_texture(&self, idx: usize) -> &gl::Texture{
self.fbo_bloom_down[idx].color_tex(0).unwrap()
}
#[cfg(not(feature="gaussian_bloom"))]
pub fn bloom_up_texture(&self, idx: usize) -> &gl::Texture{
self.fbo_bloom_up[idx].color_tex(0).unwrap()
}
#[cfg(feature="gaussian_bloom")]
pub fn process(&self, gl: &gl::Renderer, tex: &gl::Texture, parameters: &BloomParameters) -> &gl::Texture{
#[cfg(gl_debug_groups)]
let _debug_group = gl.new_debug_group(0, "Bloom");
let threshold: f32 = *parameters.threshold.borrow();
let threshold = threshold.max(0.);
let knee: f32 = *parameters.knee.borrow();
let radius: f32 = *parameters.radius.borrow();
let std_dev: f32 = *parameters.sigma.borrow();
let min_dim = tex.width().min(tex.height()) as f32;
let log_min_dim = min_dim.log(2.) + radius - 8.;
let passes = clamp(log_min_dim as usize, 1, 8);
let knee = threshold * knee + 1e-5;
let curve = vec4(
threshold - knee,
knee * 2.,
0.25 / knee,
threshold
);
let source_texel_size = vec2(1. / tex.width() as f32, 1. / tex.height() as f32);
let bright_uniforms = uniforms! {
tex0: tex,
texel_size: source_texel_size,
curve_threshold: curve,
};
{
let gl = gl.with_fbo(&self.fbo_bloom_bright);
gl.clear(&BLACK);
gl.draw_vao(&*self.fullscreen_quad, &self.bright, &bright_uniforms);
}
if self.bloom_passes.get() != Some(passes as u8) {
for [fbo_h, fbo_v] in &self.fbo_bloom_levels {
gl.with_fbo(fbo_h).clear(&BLACK);
gl.with_fbo(fbo_v).clear(&BLACK);
}
self.bloom_passes.set(Some(passes as u8));
}
if self.bloom_sigma.get() != std_dev {
self.bloom_sigma.set(std_dev);
let w0 = gaussian(0.0, 0.0, std_dev);
let w1 = gaussian(1.0, 0.0, std_dev);
let w2 = gaussian(2.0, 0.0, std_dev);
let w3 = gaussian(3.0, 0.0, std_dev);
let w4 = gaussian(4.0, 0.0, std_dev);
let w5 = gaussian(5.0, 0.0, std_dev);
let w6 = gaussian(6.0, 0.0, std_dev);
let w7 = gaussian(7.0, 0.0, std_dev);
let w8 = gaussian(8.0, 0.0, std_dev);
let sw9 = w0 + 2.0 * (w1 + w2 + w3 + w4 + w5 + w6 + w7 + w8);
let weights9 = uniforms!{
w0: w0/sw9,
w1: w1/sw9,
w2: w2/sw9,
w3: w3/sw9,
w4: w4/sw9,
w5: w5/sw9,
w6: w6/sw9,
w7: w7/sw9,
w8: w8/sw9
};
let _ = self.blur_h.set_uniforms(&weights9);
let _ = self.blur_v.set_uniforms(&weights9);
let sw5 = w0 + 2.0 * (w1 + w2 + w3 + w4);
let weights5 = uniforms!{
w0: w0/sw5,
w1: w1/sw5,
w2: w2/sw5,
w3: w3/sw5,
w4: w4/sw5,
};
let _ = self.blur_h5.set_uniforms(&weights5);
let _ = self.blur_v5.set_uniforms(&weights5);
}
for pass in 0 .. passes {
let texture = if pass == 0 {
self.fbo_bloom_bright.color_tex(0).unwrap()
}else{
self.fbo_bloom_levels[pass - 1][1].color(0).unwrap().texture()
};
let texel_size = if pass == 0 {
[
1. / self.fbo_bloom_bright.width() as f32,
1. / self.fbo_bloom_bright.height() as f32
]
}else{
[
1. / self.fbo_bloom_levels[pass - 1][1].width() as f32,
1. / self.fbo_bloom_levels[pass - 1][1].height() as f32
]
};
let level = if pass == 0 { 0 } else { pass - 1 };
let blur_v_uniforms = uniforms!{
tex0: texture,
texel_size: texel_size,
lod: level as f32,
};
{
let gl = gl.with_fbo(&self.fbo_bloom_levels[pass][0]);
gl.clear(&BLACK);
if pass == 0 {
gl.draw_vao(&*self.fullscreen_quad, &self.blur_v, &blur_v_uniforms);
}else{
gl.draw_vao(&*self.fullscreen_quad, &self.blur_v, &blur_v_uniforms);
}
}
let blur_h_uniforms = uniforms!{
tex0: self.fbo_bloom_levels[pass][0].color(0).unwrap().texture(),
texel_size: texel_size,
lod: pass as f32,
};
let gl = gl.with_fbo(&self.fbo_bloom_levels[pass][1]);
gl.clear(&BLACK);
if pass == 0 {
gl.draw_vao(&*self.fullscreen_quad, &self.blur_h, &blur_h_uniforms);
}else{
gl.draw_vao(&*self.fullscreen_quad, &self.blur_h, &blur_h_uniforms);
}
}
&self.bloom_levels
}
#[cfg(not(feature="gaussian_bloom"))]
pub fn process(&self, gl: &gl::Renderer, tex: &gl::Texture, parameters: &BloomParameters) -> &gl::Texture{
#[cfg(gl_debug_groups)]
let _debug_group = gl.new_debug_group(0, "Bloom");
let threshold: f32 = *parameters.threshold.borrow();
let threshold = threshold.max(0.);
let knee: f32 = *parameters.knee.borrow();
let radius: f32 = *parameters.radius.borrow();
let knee = threshold * knee + 1e-5;
let curve = vec4(
threshold - knee,
knee * 2.,
0.25 / knee,
threshold
);
let source_texel_size = vec2(1. / tex.width() as f32, 1. / tex.height() as f32);
let bright_uniforms = uniforms! {
tex0: tex,
texel_size: source_texel_size,
curve_threshold: curve,
};
gl.with_fbo(&self.fbo_bloom_down[0])
.draw_vao(&*self.fullscreen_quad, &self.bright, &bright_uniforms);
let min_dim = tex.width().min(tex.height()) as f32;
let log_min_dim = min_dim.log(2.) + radius - 8.;
let iterations = clamp(log_min_dim as usize, 1, self.fbo_bloom_down.len());
for i in 1 .. iterations {
let tex = self.fbo_bloom_down[i - 1].color_tex(0).unwrap();
let source_texel_size = vec2(1. / tex.width() as f32, 1. / tex.height() as f32);
let downsample_uniforms = uniforms! {
main_tex: tex,
main_texel_size: source_texel_size,
};
gl.with_fbo(&self.fbo_bloom_down[i])
.draw_vao(&*self.fullscreen_quad, &self.bloom_downsample, &downsample_uniforms);
}
let mut last = self.fbo_bloom_down[iterations as usize - 1].color_tex(0).unwrap();
let sample_scale = 0.5 + log_min_dim.fract();
for i in (0 .. iterations - 1).rev() {
let source_tex = self.fbo_bloom_down[i].color_tex(0).unwrap();
let base_tex = last;
let source_texel_size = vec2(1. / source_tex.width() as f32, 1. / source_tex.height() as f32);
let upsample_uniforms = uniforms! {
main_tex: (source_tex, 0),
main_texel_size: source_texel_size,
base_tex: (base_tex, 1),
sample_scale: sample_scale,
};
gl.with_fbo(&self.fbo_bloom_up[i])
.draw_vao(&*self.fullscreen_quad, &self.bloom_upsample, &upsample_uniforms);
last = self.fbo_bloom_up[i].color_tex(0).unwrap();
}
let base_tex = tex;
let source_tex = last;
let source_texel_size = vec2(1. / source_tex.width() as f32, 1. / source_tex.height() as f32);
let upsample_final_uniforms = uniforms! {
main_tex: (source_tex, 0),
main_texel_size: source_texel_size,
base_tex: (base_tex, 1),
sample_scale: sample_scale
};
gl.with_fbo(&self.fbo_bloom_upsample_final)
.draw_vao(&*self.fullscreen_quad, &self.bloom_upsample_final, &upsample_final_uniforms);
self.fbo_bloom_upsample_final.color_tex(0).unwrap()
}
}
fn bloom_response_function(
threshold: &RangedPropertyMut<'static,f32>,
knee: &RangedPropertyMut<'static,f32>,
intensity: &RangedPropertyMut<'static,f32>,
) -> RangedProperty<'static, Vec<f32>, f32>{
let curve_data = threshold.clone().into_property()
.zip(knee.clone().into_property())
.zip(intensity.clone().into_property())
.flatten()
.map(|(threshold, knee, intensity)| {
(0usize .. 200).map(|sample| {
let x = sample as f32 / 10.;
let knee = knee + threshold + 1e-5f32;
let intensity = intensity.min(10.);
let rq = clamp(x - threshold + knee, 0., knee * 2.);
let rq = rq * rq * 0.25 / knee;
rq.max(x - threshold) * intensity
}).collect()
});
RangedProperty::new(curve_data, 0. .. 1.5)
}