use super::{ProgramSettings, GLSL_VERSION, default_settings, fbo};
use rin_gl::{self as gl, uniforms, Renderer3d, fbo::TextureLevelRef};
use rin_util::{Result, Error, LogErr};
use rin_math::{vec2, vec4, Angle};
#[cfg(not(feature = "ssao_alchemy"))]
use rin_math::{vec3, Pnt3, lerp, ToPnt, };
use std::rc::Rc;
use std::borrow::Borrow;
use std::cell::{Cell, UnsafeCell};
use rin_graphics::{CameraExt, Vertex2DTex};
use rin_events::{RangedPropertyMut, Property, ParamsDefault};
#[cfg(gui)]
use rin_gui::ParameterGroup;
use color::consts::BLACK;
#[cfg(any(feature = "ssao_alchemy", feature= "ssao_blur_gaussian"))]
use color::consts::WHITE;
#[cfg(not(feature = "ssao_blur_gaussian"))]
use color::{Rgb, rgb, };
#[cfg(not(feature = "ssao_alchemy"))]
use rand::Rng;
#[cfg(glsl_debug)]
use rin_util::AutoLoader;
pub struct Ssao {
fbo_depth_mipmaps: Vec<gl::Fbo<gl::fbo::TextureLevelRef<Rc<gl::Texture>>>>,
linear_depth_mipmaps: Rc<gl::Texture>,
fbo_ssao: gl::Fbo,
#[cfg(not(feature = "ssao_blur_gaussian"))]
fbo_ssao_blur: gl::Fbo,
#[cfg(feature = "ssao_blur_gaussian")]
fbo_ssao_blur: [gl::Fbo;2],
fbo_ssao_color: Rc<gl::Fbo>,
#[cfg(not(feature = "ssao_blur_gaussian"))]
ssao_noise: UnsafeCell<gl::Texture>,
#[cfg(feature = "ssao_alchemy")]
start_depth_minify: UnsafeCell<gl::Program>,
#[cfg(feature = "ssao_alchemy")]
other_depth_minify: UnsafeCell<gl::Program>,
ssao: UnsafeCell<gl::Program>,
ssao_blur: UnsafeCell<gl::Program>,
ssao_apply: UnsafeCell<gl::Program>,
combine_color_ambient: UnsafeCell<gl::Program>,
#[cfg(all(glsl_debug, feature="ssao_alchemy"))]
start_depth_minify_loader: UnsafeCell<AutoLoader<gl::Program>>,
#[cfg(all(glsl_debug, feature="ssao_alchemy"))]
other_depth_minify_loader: UnsafeCell<AutoLoader<gl::Program>>,
#[cfg(glsl_debug)]
ssao_loader: UnsafeCell<AutoLoader<gl::Program>>,
#[cfg(glsl_debug)]
ssao_blur_loader: UnsafeCell<AutoLoader<gl::Program>>,
#[cfg(glsl_debug)]
ssao_apply_loader: UnsafeCell<AutoLoader<gl::Program>>,
#[cfg(glsl_debug)]
combine_color_ambient_loader: UnsafeCell<AutoLoader<gl::Program>>,
ssao_kernel_samples: Cell<u32>,
ssao_blur_radius: Cell<u32>,
ssao_blur_step_size: Cell<u32>,
ssao_depth_samples: Cell<u32>,
ssao_high_quality_blur: Cell<bool>,
ssao_edge_sharpness: Cell<f32>,
ssao_linear_depth: Cell<bool>,
ssao_normals_from_position: Cell<bool>,
ssao_far_at_inifinity: Cell<bool>,
ssao_orthographic_camera: Cell<bool>,
ssao_reversed_z: Cell<bool>,
dithering: Cell<bool>,
fullscreen_quad: Rc<gl::SimpleVao<Vertex2DTex>>,
}
#[derive(Clone, Debug, ParamsDefault)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(gui, derive(ParameterGroup))]
pub struct SSAOParameters{
#[param(default = 0.75, range = 0.01 .. 10.0)]
pub radius: RangedPropertyMut<'static,f32>,
#[param(default = 0.02, range_log = 0.001 .. 1.0)]
pub bias: RangedPropertyMut<'static,f32>,
#[param(default = 1., range = 0.1 .. 10.0)]
pub power: RangedPropertyMut<'static,f32>,
#[cfg(not(feature = "ssao_alchemy"))]
#[param(default = 32, range = 4 .. 256)]
pub kernel_samples: RangedPropertyMut<'static,u32>,
#[cfg(feature = "ssao_alchemy")]
#[param(default = 8, range = 1 .. 256)]
pub kernel_samples: RangedPropertyMut<'static,u32>,
#[param(default = 2, range = 0 .. 6)]
pub blur_radius: RangedPropertyMut<'static,u32>,
#[param(default = 1, range = 1 .. 4)]
pub blur_step_size: RangedPropertyMut<'static,u32>,
#[param(default = 1., range = 0. .. 16.)]
pub edge_sharpness: RangedPropertyMut<'static,f32>,
#[param(default = true)]
pub high_quality_blur: Property<'static,bool>,
#[param(default = false)]
#[cfg_attr(gui, gui(not_parameter))]
pub dithering: Property<'static,bool>,
}
#[derive(Clone, Copy)]
pub enum SSAOPosition<'a>{
FromDepth(&'a gl::Texture),
FromLinearDepth(&'a gl::Texture, &'a gl::Texture),
Position(&'a gl::Texture, &'a gl::Texture),
}
#[cfg(not(feature = "ssao_alchemy"))]
fn ssao_kernel(len: u32) -> impl Iterator<Item=Pnt3>{
let mut rng = rand::thread_rng();
(0..len).map(move |i| {
let sample = vec3(rng.gen_range(-1., 1.), rng.gen_range(-1., 1.), rng.gen_range(0., 1.))
.normalize()
.to_pnt() * rng.gen_range(0., 1.);
let pct = i as f32 / len as f32;
let scale = lerp(0.1, 1., pct * pct);
sample * scale
})
}
#[cfg(not(feature = "ssao_alchemy"))]
fn ssao_kernel_str(kernel_size: u32) -> String{
let kernel = ssao_kernel(kernel_size)
.map(|p| format!("vec3({},{},{}),\n", p.x, p.y, p.z))
.collect::<String>();
format!("vec3[](
{}
)", &kernel[..kernel.len() - 2])
}
#[cfg(not(feature = "ssao_blur_gaussian"))]
fn ssao_noise_texture(size: u32) -> impl Iterator<Item = Rgb<f32>>{
let mut rng = rand::thread_rng();
(0 .. size * size).map(move |_|{
rgb!(rng.gen_range(-1., 1.), rng.gen_range(-1., 1.), 0.)
})
}
#[cfg(not(feature = "ssao_alchemy"))]
fn ssao_noise_str(noise_size: u32) -> String{
let kernel = ssao_noise_texture(noise_size)
.map(|p| format!("vec3({},{},{}),\n", p.r, p.g, p.b))
.collect::<String>();
format!("vec3[](
{}
);", &kernel[..kernel.len() - 2])
}
#[cfg(not(feature = "ssao_blur_gaussian"))]
fn ssao_noise(noise_radius: u32, gl: &gl::Renderer) -> gl::Result<gl::Texture>{
let noise_size = noise_radius * 2;
let ssao_noise_data = ssao_noise_texture(noise_size).collect::<Vec<Rgb<f32>>>();
let mut ssao_noise = gl.new_texture().from_data_format(gl::texture::LoadDataFormat{
format: gl::RGB,
ty: gl::FLOAT,
texture_format: gl::texture::Format::Texture2D {
internal_format: gl::RGB32F,
width: noise_size,
height: noise_size,
levels: 1,
},
}, &ssao_noise_data);
if let Ok(ssao_noise) = ssao_noise.as_mut() {
ssao_noise.set_wrap_r(gl::REPEAT);
ssao_noise.set_wrap_s(gl::REPEAT);
ssao_noise.set_min_mag_filters(gl::NEAREST, gl::NEAREST);
}
ssao_noise
}
#[cfg(not(feature = "ssao_alchemy"))]
fn ssao_settings(
kernel_size: u32,
noise_radius: u32,
w: u32, h: u32,
depth_samples: u32,
has_position: bool,
has_normals: bool,
has_linear_depth: bool) -> ProgramSettings
{
let noise_size = noise_radius * 2;
let kernel = ssao_kernel_str(kernel_size);
let shader_replace = vec![
(gl::FRAGMENT_SHADER, ("${kernel}".to_owned(), kernel)),
(gl::FRAGMENT_SHADER, ("${kernel_size}".to_owned(), kernel_size.to_string())),
(gl::FRAGMENT_SHADER, ("${noise_size}".to_owned(), format!("{:.1}", noise_size as f32))),
(gl::FRAGMENT_SHADER, ("${view_width}".to_owned(), format!("{:.1}", w as f32))),
(gl::FRAGMENT_SHADER, ("${view_height}".to_owned(), format!("{:.1}", h as f32))),
(gl::FRAGMENT_SHADER, ("${depth_samples}".to_owned(), depth_samples.to_string())),
];
let fragment = shader!("shaders/ssao.fs.glsl");
#[cfg(not(glsl_debug))]
let fragment = shader_replace.into_iter()
.fold(fragment, |shader_src, (_, (from, to))| {
shader_src.replace(&from, &to)
});
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("POSITION_FROM_DEPTH".to_owned(), (!has_position as u8).to_string()),
("NORMALS_FROM_POSITION".to_owned(), (!has_normals as u8).to_string()),
("LINEAR_DEPTH".to_owned(), (has_linear_depth as u8).to_string()),
("MSAA_DEPTH".to_owned(), ((depth_samples != 0) as u8).to_string()),
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/ssao.vs.glsl")),
(gl::FRAGMENT_SHADER, fragment),
],
#[cfg(glsl_debug)]
shader_replace,
.. default_settings()
}
}
#[cfg(feature = "ssao_alchemy")]
fn alchemy_ssao_settings(
w: u32, h: u32,
kernel_samples: u32,
depth_samples: u32,
has_position: bool,
has_normals: bool,
has_linear_depth: bool) -> ProgramSettings
{
let shader_replace = vec![
(gl::FRAGMENT_SHADER, ("${view_width}".to_owned(), format!("{:.1}", w as f32))),
(gl::FRAGMENT_SHADER, ("${view_height}".to_owned(), format!("{:.1}", h as f32))),
(gl::FRAGMENT_SHADER, ("${depth_samples}".to_owned(), depth_samples.to_string())),
];
let fragment = shader!("shaders/ssao_alchemy.fs.glsl");
#[cfg(not(glsl_debug))]
let fragment = shader_replace.into_iter()
.fold(fragment, |shader_src, (_, (from, to))| {
shader_src.replace(&from, &to)
});
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("POSITION_FROM_DEPTH".to_owned(), (!has_position as u8).to_string()),
("NORMALS_FROM_POSITION".to_owned(), (!has_normals as u8).to_string()),
("LINEAR_DEPTH".to_owned(), (has_linear_depth as u8).to_string()),
("MSAA_DEPTH".to_owned(), ((depth_samples != 0) as u8).to_string()),
("NUM_SAMPLES".to_owned(), kernel_samples.to_string()),
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/ssao.vs.glsl")),
(gl::FRAGMENT_SHADER, fragment),
],
#[cfg(glsl_debug)]
shader_replace,
.. default_settings()
}
}
#[cfg(not(feature = "ssao_blur_gaussian"))]
fn ssao_blur_settings(noise_radius: u32) -> ProgramSettings{
let noise_size = noise_radius * 2;
let shader_replace = vec![
(gl::FRAGMENT_SHADER, ("${half_kernel}".to_owned(), noise_radius.to_string())),
(gl::FRAGMENT_SHADER, ("${kernel_size}".to_owned(), format!("{:.1}", noise_size as f32))),
];
let fragment = shader!("shaders/ssao_blur_box.fs.glsl");
#[cfg(not(glsl_debug))]
let fragment = shader_replace.into_iter()
.fold(fragment, |shader_src, (_, (from, to))| {
shader_src.replace(&from, &to)
});
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, fragment),
],
#[cfg(glsl_debug)]
shader_replace,
.. default_settings()
}
}
#[cfg(feature = "ssao_blur_gaussian")]
fn ssao_blur_gaussian_settings(
w: u32, h: u32,
has_linear_depth: bool,
has_normals: bool,
noise_radius: u32,
scale: u32,
high_quality: bool,
edge_sharpness: f32) -> ProgramSettings
{
let shader_replace = vec![
(gl::FRAGMENT_SHADER, ("${view_width}".to_owned(), format!("{:.1}", w as f32))),
(gl::FRAGMENT_SHADER, ("${view_height}".to_owned(), format!("{:.1}", h as f32))),
];
let fragment = shader!("shaders/ssao_blur_gaussian.fs.glsl");
#[cfg(not(glsl_debug))]
let fragment = shader_replace.into_iter()
.fold(fragment, |shader_src, (_, (from, to))| {
shader_src.replace(&from, &to)
});
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("LINEAR_DEPTH".to_owned(), (has_linear_depth as u8).to_string()),
("R".to_owned(), noise_radius.to_string()),
("SCALE".to_owned(), scale.to_string()),
("HIGH_QUALITY".to_owned(), (high_quality as u8).to_string()),
("EDGE_SHARPNESS".to_owned(), edge_sharpness.to_string()),
("NORMALS_BUFFER".to_owned(), (has_normals as u8).to_string()),
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, fragment),
],
#[cfg(glsl_debug)]
shader_replace,
.. default_settings()
}
}
#[cfg(feature = "ssao_alchemy")]
fn ssao_depth_minify_settings(
has_linear_depth: bool,
start: bool,
orthographic_camera: bool,
far_at_inifinity: bool,
reversed_z: bool) -> ProgramSettings
{
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("LINEAR_DEPTH".to_owned(), (has_linear_depth as u8).to_string()),
("PHASE".to_owned(), "RECONSTRUCT_Z".to_string()),
("ORTHOGRAPHIC_CAMERA".to_owned(), (orthographic_camera as u8).to_string()),
("FAR_AT_INFINITY".to_owned(), (far_at_inifinity as u8).to_string()),
("REVERSED_Z".to_owned(), (reversed_z as u8).to_string()),
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/ssao_depth_minify.fs.glsl")),
],
.. default_settings()
}
}
fn ssao_apply_settings(dithering: bool) -> ProgramSettings{
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("USE_DITHERING".to_owned(), (dithering as u8).to_string()),
("HAS_SSAO".to_owned(), "1".to_string()),
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/apply_ssao.fs.glsl")),
],
.. default_settings()
}
}
fn combine_color_ambient_settings(dithering: bool) -> ProgramSettings{
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("USE_DITHERING".to_owned(), (dithering as u8).to_string()),
("HAS_SSAO".to_owned(), "0".to_string()),
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/apply_ssao.fs.glsl")),
],
.. default_settings()
}
}
impl Ssao {
pub fn new(gl: &gl::Renderer, w: u32, h: u32, format: gl::fbo::ColorFormat, fullscreen_quad: Rc<gl::SimpleVao<Vertex2DTex>>) -> Result<Ssao> {
let ssao_format = gl::fbo::ColorFormat::RG16F;
let mut fbo_ssao = fbo(gl, w, h, ssao_format)
.map_err(|err| Error::with_cause("Couldn't create ssao fbo", err))?;
fbo_ssao.color_tex_mut(0).unwrap().set_min_mag_filters(gl::NEAREST, gl::NEAREST);
#[cfg(renderer_ty="gl")]
fbo_ssao.color_tex_mut(0).unwrap().set_anisotropy_level(1.);
#[cfg(not(feature = "ssao_blur_gaussian"))]
let mut fbo_ssao_blur = fbo(gl, w, h, gl::fbo::ColorFormat::R8)
.map_err(|err| Error::with_cause("Couldn't create ssao blur fbo", err))?;
#[cfg(feature = "ssao_blur_gaussian")]
let mut fbo_ssao_blur = [
fbo(gl, w, h, ssao_format)
.map_err(|err| Error::with_cause("Couldn't create ssao blur fbo", err))?,
fbo(gl, w, h, ssao_format)
.map_err(|err| Error::with_cause("Couldn't create ssao blur fbo", err))?,
];
#[cfg(not(feature = "ssao_blur_gaussian"))]
fbo_ssao_blur.color_tex_mut(0).unwrap().set_min_mag_filters(gl::NEAREST, gl::NEAREST);
#[cfg(feature = "ssao_blur_gaussian")]
{
fbo_ssao_blur[0].color_tex_mut(0).unwrap().set_min_mag_filters(gl::NEAREST, gl::NEAREST);
#[cfg(renderer_ty="gl")]
fbo_ssao_blur[0].color_tex_mut(0).unwrap().set_anisotropy_level(1.);
fbo_ssao_blur[1].color_tex_mut(0).unwrap().set_min_mag_filters(gl::NEAREST, gl::NEAREST);
#[cfg(renderer_ty="gl")]
fbo_ssao_blur[1].color_tex_mut(0).unwrap().set_anisotropy_level(1.);
}
let fbo_ssao_color = fbo(gl, w, h, format)
.map_err(|err| Error::with_cause("Couldn't create ssao color fbo", err))?;
let linear_depth_format = gl::texture::Format::Texture2D{
internal_format: gl::R32F,
width: w,
height: h,
levels: 4,
};
let mut linear_depth_mipmaps = gl.new_texture().from_format(linear_depth_format).unwrap();
linear_depth_mipmaps.set_min_mag_filters(gl::NEAREST_MIPMAP_NEAREST, gl::NEAREST);
linear_depth_mipmaps.set_base_level(0);
linear_depth_mipmaps.set_max_level(3);
let linear_depth_mipmaps = Rc::new(linear_depth_mipmaps);
let fbo_depth_mipmaps = (0..4).map(|level| {
let attachment = TextureLevelRef::new(linear_depth_mipmaps.clone(), level);
gl.new_fbo().from_color_no_depth(attachment)
}).collect::<gl::Result<Vec<_>>>()
.map_err(|err| Error::with_cause("Couldn't create ssao depth mips fbos", err))?;
let ssao_depth_samples = 0;
let ssao_has_position = false;
let ssao_has_normals = false;
let ssao_linear_depth = false;
let ssao_kernel_samples = 8;
let ssao_blur_radius = 2;
let ssao_blur_step_size = 1;
let ssao_high_quality_blur = true;
let ssao_edge_sharpness = 1.;
#[cfg(not(feature = "ssao_alchemy"))]
let ssao_settings = ssao_settings(
ssao_kernel_samples,
ssao_blur_radius,
w, h,
ssao_depth_samples,
ssao_has_position,
ssao_has_normals,
ssao_linear_depth);
#[cfg(feature = "ssao_alchemy")]
let ssao_settings = alchemy_ssao_settings(
w, h,
ssao_kernel_samples,
ssao_depth_samples,
ssao_has_position,
ssao_has_normals,
ssao_linear_depth);
#[cfg(feature = "ssao_alchemy")]
let start_depth_minify_settings = ssao_depth_minify_settings(
ssao_linear_depth,
true,
false,
false,
false
);
#[cfg(feature = "ssao_alchemy")]
let other_depth_minify_settings = ssao_depth_minify_settings(
ssao_linear_depth,
false,
false,
false,
false
);
#[cfg(not(feature = "ssao_blur_gaussian"))]
let ssao_blur_settings = ssao_blur_settings(ssao_blur_radius);
#[cfg(feature = "ssao_blur_gaussian")]
let ssao_blur_settings = ssao_blur_gaussian_settings(
w, h,
ssao_linear_depth,
ssao_has_normals,
ssao_blur_radius,
ssao_blur_step_size,
ssao_high_quality_blur,
ssao_edge_sharpness,
);
let ssao_apply_settings = ssao_apply_settings(false);
let combine_color_ambient_settings = combine_color_ambient_settings(false);
#[cfg(all(glsl_debug, feature="ssao_alchemy"))]
let mut start_depth_minify_loader = gl.new_auto_program(start_depth_minify_settings);
#[cfg(all(glsl_debug, feature="ssao_alchemy"))]
let mut other_depth_minify_loader = gl.new_auto_program(other_depth_minify_settings);
#[cfg(glsl_debug)]
let mut ssao_loader = gl.new_auto_program(ssao_settings);
#[cfg(glsl_debug)]
let mut ssao_blur_loader = gl.new_auto_program(ssao_blur_settings);
#[cfg(glsl_debug)]
let mut ssao_apply_loader = gl.new_auto_program(ssao_apply_settings);
#[cfg(glsl_debug)]
let mut combine_color_ambient_loader = gl.new_auto_program(combine_color_ambient_settings);
#[cfg(feature = "ssao_alchemy")]
let start_depth_minify;
#[cfg(feature = "ssao_alchemy")]
let other_depth_minify;
let ssao;
let ssao_blur;
let ssao_apply;
let combine_color_ambient;
#[cfg(glsl_debug)]
{
ssao = ssao_loader.load().log_err("")?;
ssao_blur = ssao_blur_loader.load().log_err("")?;
ssao_apply = ssao_apply_loader.load().log_err("")?;
combine_color_ambient = combine_color_ambient_loader.load().log_err("")?;
}
#[cfg(all(glsl_debug, feature="ssao_alchemy"))]
{
start_depth_minify = start_depth_minify_loader.load().log_err("")?;
other_depth_minify = other_depth_minify_loader.load().log_err("")?;
}
#[cfg(not(glsl_debug))]
{
#[cfg(feature = "ssao_alchemy")]
{
start_depth_minify = gl.new_program().from_settings(start_depth_minify_settings)
.log_err("")
.map_err(|err| Error::with_cause("Error loading start depth minify shader", err))?;
other_depth_minify = gl.new_program().from_settings(other_depth_minify_settings)
.log_err("")
.map_err(|err| Error::with_cause("Error loading other depth minify shader", err))?;
}
ssao = gl.new_program().from_settings(ssao_settings)
.log_err("")
.map_err(|err| Error::with_cause("Error loading ssao shader", err))?;
ssao_blur = gl.new_program().from_settings(ssao_blur_settings)
.log_err("")
.map_err(|err| Error::with_cause("Error loading ssao blur shader", err))?;
ssao_apply = gl.new_program().from_settings(ssao_apply_settings)
.log_err("")
.map_err(|err| Error::with_cause("Error loading ssao apply shader", err))?;
combine_color_ambient = gl.new_program().from_settings(combine_color_ambient_settings)
.log_err("")
.map_err(|err| Error::with_cause("Error loading combine color + ambient shader", err))?;
}
#[cfg(not(feature = "ssao_blur_gaussian"))]
let ssao_noise = ssao_noise(ssao_blur_radius, gl)
.map_err(|err| Error::with_cause("Error creating SSAO noise texture", err))?;
Ok(Ssao{
fbo_depth_mipmaps,
linear_depth_mipmaps,
fbo_ssao,
fbo_ssao_blur,
fbo_ssao_color: Rc::new(fbo_ssao_color),
#[cfg(not(feature = "ssao_blur_gaussian"))]
ssao_noise: UnsafeCell::new(ssao_noise),
#[cfg(feature = "ssao_alchemy")]
start_depth_minify: UnsafeCell::new(start_depth_minify),
#[cfg(feature = "ssao_alchemy")]
other_depth_minify: UnsafeCell::new(other_depth_minify),
ssao: UnsafeCell::new(ssao),
ssao_blur: UnsafeCell::new(ssao_blur),
ssao_apply: UnsafeCell::new(ssao_apply),
combine_color_ambient: UnsafeCell::new(combine_color_ambient),
ssao_kernel_samples: Cell::new(ssao_kernel_samples),
ssao_blur_radius: Cell::new(ssao_blur_radius),
ssao_depth_samples: Cell::new(ssao_depth_samples),
ssao_blur_step_size: Cell::new(ssao_blur_step_size),
ssao_high_quality_blur: Cell::new(ssao_high_quality_blur),
ssao_edge_sharpness: Cell::new(ssao_edge_sharpness),
ssao_linear_depth: Cell::new(ssao_linear_depth),
ssao_normals_from_position: Cell::new(!ssao_has_normals),
ssao_far_at_inifinity: Cell::new(false),
ssao_orthographic_camera: Cell::new(false),
ssao_reversed_z: Cell::new(false),
dithering: Cell::new(false),
#[cfg(all(glsl_debug, feature="ssao_alchemy"))]
start_depth_minify_loader: UnsafeCell::new(start_depth_minify_loader),
#[cfg(all(glsl_debug, feature="ssao_alchemy"))]
other_depth_minify_loader: UnsafeCell::new(other_depth_minify_loader),
#[cfg(glsl_debug)]
ssao_loader: UnsafeCell::new(ssao_loader),
#[cfg(glsl_debug)]
ssao_blur_loader: UnsafeCell::new(ssao_blur_loader),
#[cfg(glsl_debug)]
ssao_apply_loader: UnsafeCell::new(ssao_apply_loader),
#[cfg(glsl_debug)]
combine_color_ambient_loader: UnsafeCell::new(combine_color_ambient_loader),
fullscreen_quad,
})
}
#[cfg(glsl_debug)]
pub fn update(&mut self){
#[cfg(feature="ssao_alchemy")]
match unsafe{ (*self.start_depth_minify_loader.get()).update() }{
Ok(Some(start_depth_minify)) => {
self.start_depth_minify = UnsafeCell::new(start_depth_minify);
println!("start depth minify shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
#[cfg(feature="ssao_alchemy")]
match unsafe{ (*self.other_depth_minify_loader.get()).update() }{
Ok(Some(other_depth_minify)) => {
self.other_depth_minify = UnsafeCell::new(other_depth_minify);
println!("other depth minify shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
match unsafe{ (*self.ssao_loader.get()).update() }{
Ok(Some(ssao)) => {
self.ssao = UnsafeCell::new(ssao);
println!("ssao shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
match unsafe{ (*self.ssao_blur_loader.get()).update() }{
Ok(Some(ssao_blur)) => {
self.ssao_blur = UnsafeCell::new(ssao_blur);
println!("ssao blur shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
match unsafe{ (*self.ssao_apply_loader.get()).update() } {
Ok(Some(ssao_apply)) => {
unsafe{ *self.ssao_apply.get() = ssao_apply };
println!("ssao apply shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
match unsafe{ (*self.combine_color_ambient_loader.get()).update() } {
Ok(Some(combine_color_ambient)) => {
unsafe{ *self.combine_color_ambient.get() = combine_color_ambient };
println!("combine color ambient shader reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
}
#[cfg(glsl_debug)]
fn refresh_color_ambient_combine(&self, gl: &gl::Renderer, dithering: bool) -> Result<()>{
if dithering != self.dithering.get() {
let mut ssao_apply_loader = gl.new_auto_program(ssao_apply_settings(dithering));
unsafe{ *self.ssao_apply.get() = ssao_apply_loader.load()? };
unsafe{ *self.ssao_apply_loader.get() = ssao_apply_loader };
let mut combine_color_ambient_loader = gl.new_auto_program(combine_color_ambient_settings(dithering));
unsafe{ *self.combine_color_ambient.get() = combine_color_ambient_loader.load()? };
unsafe{ *self.combine_color_ambient_loader.get() = combine_color_ambient_loader };
self.dithering.set(dithering);
}
Ok(())
}
#[cfg(not(glsl_debug))]
fn refresh_color_ambient_combine(&self, gl: &gl::Renderer, dithering: bool) -> Result<()>{
if dithering != self.dithering.get() {
let ssao_apply = gl.new_program()
.from_settings(ssao_apply_settings(dithering))
.map_err(|err| Error::with_cause("Error re-loading ssao apply shader", err))?;
unsafe{ *self.ssao_apply.get() = ssao_apply };
let combine_color_ambient = gl.new_program()
.from_settings(combine_color_ambient_settings(dithering))
.map_err(|err| Error::with_cause("Error re-loading color + ambient shader", err))?;
unsafe{ *self.combine_color_ambient.get() = combine_color_ambient };
self.dithering.set(dithering);
}
Ok(())
}
pub fn fbo(&self) -> &Rc<gl::Fbo> {
&self.fbo_ssao_color
}
pub fn ssao_texture(&self) -> &gl::Texture{
self.fbo_ssao.color_tex(0).unwrap()
}
pub fn ssao_blur_texture(&self) -> &gl::Texture{
#[cfg(not(feature = "ssao_blur_gaussian"))]
{
self.fbo_ssao_blur.color_tex(0).unwrap()
}
#[cfg(feature = "ssao_blur_gaussian")]
{
self.fbo_ssao_blur[1].color_tex(0).unwrap()
}
}
pub fn ssao_color_texture(&self) -> &gl::Texture{
self.fbo_ssao_color.color_tex(0).unwrap()
}
pub fn ssao_color_attachment(&self) -> &gl::fbo::ColorAttachment{
self.fbo_ssao_color.color(0).unwrap()
}
pub fn process(&self, gl: &gl::Renderer,
camera: &dyn CameraExt,
color: &gl::Texture,
position: SSAOPosition,
normals: Option<&gl::Texture>,
ambient: &gl::Texture,
parameters: &SSAOParameters) -> Result<&gl::Texture>
{
#[cfg(gl_debug_groups)]
let _debug_group = gl.new_debug_group(0, "SSAO");
let intensity: f32 = *parameters.power.borrow();
let radius: f32 = *parameters.radius.borrow();
let kernel_samples: u32 = *parameters.kernel_samples.borrow();
let blur_radius: u32 = *parameters.blur_radius.borrow();
let blur_step_size: u32 = *parameters.blur_step_size.borrow();
let high_quality_blur: bool = *parameters.high_quality_blur.borrow();
let edge_sharpness: f32 = *parameters.edge_sharpness.borrow();
let dithering: bool = *parameters.dithering.borrow();
let has_position;
let position_uniforms;
let depth_samples;
let has_linear_depth;
let depth_attachment;
match position{
SSAOPosition::Position(depth, position) => {
has_position = true;
depth_samples = 0;
has_linear_depth = false;
position_uniforms = uniforms!{
g_position: (position, 1)
};
depth_attachment = depth;
}
SSAOPosition::FromLinearDepth(depth, linear_depth) => {
has_position = false;
#[cfg(renderer_ty="gl")]
{
depth_samples = linear_depth.samples();
}
#[cfg(any(renderer_ty="gles", renderer_ty="webgl"))]
{
depth_samples = 0;
}
has_linear_depth = true;
position_uniforms = uniforms!{
g_linear_depth: (linear_depth, 1)
};
depth_attachment = depth;
}
SSAOPosition::FromDepth(depth) => {
has_position = false;
#[cfg(renderer_ty="gl")]
{
depth_samples = depth.samples();
}
#[cfg(any(renderer_ty="gles", renderer_ty="webgl"))]
{
depth_samples = 0;
}
has_linear_depth = false;
position_uniforms = uniforms!{
g_depth: (depth, 1)
};
depth_attachment = depth;
}
}
let normals_from_position = normals.is_none();
let orthographic_camera = camera.fov().is_none();
let far_at_inifinity = camera.far_clip().is_none();
let reversed_z = camera.reversed_z();
if kernel_samples != self.ssao_kernel_samples.get()
|| depth_samples != self.ssao_depth_samples.get()
|| has_linear_depth != self.ssao_linear_depth.get()
|| normals_from_position != self.ssao_normals_from_position.get()
|| orthographic_camera != self.ssao_orthographic_camera.get()
|| far_at_inifinity != self.ssao_far_at_inifinity.get()
|| reversed_z != self.ssao_reversed_z.get()
{
let w = self.fbo_ssao.width();
let h = self.fbo_ssao.height();
#[cfg(not(feature = "ssao_alchemy"))]
let ssao_settings = ssao_settings(
kernel_samples,
blur_radius,
w, h,
depth_samples,
has_position,
!normals_from_position,
has_linear_depth);
#[cfg(feature = "ssao_alchemy")]
let ssao_settings = alchemy_ssao_settings(
w, h,
kernel_samples,
depth_samples,
has_position,
!normals_from_position,
has_linear_depth);
#[cfg(feature = "ssao_alchemy")]
let start_minify_settings = ssao_depth_minify_settings(
has_linear_depth,
true,
orthographic_camera,
far_at_inifinity,
reversed_z
);
#[cfg(feature = "ssao_alchemy")]
let other_minify_settings = ssao_depth_minify_settings(
has_linear_depth,
false,
orthographic_camera,
far_at_inifinity,
reversed_z
);
#[cfg(glsl_debug)]
{
let mut ssao_loader = gl.new_auto_program(ssao_settings);
unsafe{ *self.ssao.get() = ssao_loader.load()? };
unsafe{ *self.ssao_loader.get() = ssao_loader };
#[cfg(feature = "ssao_alchemy")]
{
let mut start_minify_loader = gl.new_auto_program(start_minify_settings);
unsafe{ *self.start_depth_minify.get() = start_minify_loader.load()? };
unsafe{ *self.start_depth_minify_loader.get() = start_minify_loader };
let mut other_minify_loader = gl.new_auto_program(other_minify_settings);
unsafe{ *self.other_depth_minify.get() = other_minify_loader.load()? };
unsafe{ *self.other_depth_minify_loader.get() = other_minify_loader };
}
}
#[cfg(not(glsl_debug))]
{
let ssao = gl.new_program()
.from_settings(ssao_settings)
.map_err(|err| Error::with_cause("Error re-loading ssao shader", err))?;
unsafe{ *self.ssao.get() = ssao };
#[cfg(feature = "ssao_alchemy")]
{
let start_minify = gl.new_program()
.from_settings(start_minify_settings)
.map_err(|err| Error::with_cause("Error re-loading ssao shader", err))?;
unsafe{ *self.start_depth_minify.get() = start_minify };
let other_minify = gl.new_program()
.from_settings(other_minify_settings)
.map_err(|err| Error::with_cause("Error re-loading ssao shader", err))?;
unsafe{ *self.other_depth_minify.get() = other_minify };
}
}
self.ssao_kernel_samples.set(kernel_samples);
self.ssao_depth_samples.set(depth_samples);
self.ssao_orthographic_camera.set(orthographic_camera);
self.ssao_far_at_inifinity.set(far_at_inifinity);
self.ssao_reversed_z.set(reversed_z);
}
#[cfg(not(feature = "ssao_blur_gaussian"))]
{
if blur_radius != self.ssao_blur_radius.get() {
let ssao_noise = ssao_noise(blur_radius, gl)
.map_err(|err| Error::with_cause("Error creating SSAO noise texture", err))?;
unsafe{ *self.ssao_noise.get() = ssao_noise };
#[cfg(glsl_debug)]
{
let mut ssao_blur_loader = gl.new_auto_program(ssao_blur_settings(blur_radius));
unsafe{ *self.ssao_blur.get() = ssao_blur_loader.load()? };
unsafe{ *self.ssao_blur_loader.get() = ssao_blur_loader };
}
#[cfg(not(glsl_debug))]
{
let ssao_blur = gl.new_program()
.from_settings(ssao_blur_settings(blur_radius))
.map_err(|err| Error::with_cause("Error re-loading ssao blur shader", err))?;
unsafe{ *self.ssao_blur.get() = ssao_blur };
}
self.ssao_blur_radius.set(blur_radius);
}
}
#[cfg(feature = "ssao_blur_gaussian")]
{
if blur_radius > 0
&& (blur_radius != self.ssao_blur_radius.get()
|| blur_step_size != self.ssao_blur_step_size.get()
|| high_quality_blur != self.ssao_high_quality_blur.get()
|| edge_sharpness != self.ssao_edge_sharpness.get()
|| has_linear_depth != self.ssao_linear_depth.get()
|| normals_from_position != self.ssao_normals_from_position.get())
{
let w = self.fbo_ssao.width();
let h = self.fbo_ssao.height();
let settings = ssao_blur_gaussian_settings(
w, h,
has_linear_depth,
!normals_from_position,
blur_radius,
blur_step_size,
high_quality_blur,
edge_sharpness);
#[cfg(glsl_debug)]
{
let mut ssao_blur_loader = gl.new_auto_program(settings);
unsafe{ *self.ssao_blur.get() = ssao_blur_loader.load()? };
unsafe{ *self.ssao_blur_loader.get() = ssao_blur_loader };
}
#[cfg(not(glsl_debug))]
{
let ssao_blur = gl.new_program()
.from_settings(settings)
.map_err(|err| Error::with_cause("Error re-loading ssao blur shader", err))?;
unsafe{ *self.ssao_blur.get() = ssao_blur };
}
self.ssao_blur_radius.set(blur_radius);
self.ssao_blur_step_size.set(blur_step_size);
self.ssao_high_quality_blur.set(high_quality_blur);
self.ssao_edge_sharpness.set(edge_sharpness);
self.ssao_linear_depth.set(has_linear_depth);
self.ssao_normals_from_position.set(normals_from_position);
}
}
self.refresh_color_ambient_combine(gl, dithering)?;
#[cfg(not(feature = "ssao_alchemy"))]
{
let tex_noise = unsafe{ &*self.ssao_noise.get() };
let ssao = unsafe{ &*self.ssao.get() };
let projection = camera.projection();
let tan_half_fov = (camera.fov().unwrap().to_rad() / 2.).tan();
let top = camera.near_clip() * tan_half_fov;
let right = camera.aspect_ratio() * top;
let right_top = vec2(right, top);
let mut ssao_uniforms = uniforms!{
tex_noise: (tex_noise, 2),
projectionMatrix: projection,
invProjectionMatrix: projection.try_inverse().unwrap(),
right_top: right_top,
znear: camera.near_clip(),
zfar: camera.far_clip().unwrap_or(f32::INFINITY),
radius: parameters.radius,
bias: parameters.bias,
power: parameters.power,
};
ssao_uniforms.extend(position_uniforms.iter().cloned());
ssao_uniforms.extend(normals.into_iter().flat_map(|normals| uniforms!{
g_normal: (normals, 3)
}));
let gl = gl.with_fbo(&self.fbo_ssao);
gl.clear_color(&BLACK);
gl.draw_vao(&*self.fullscreen_quad, ssao, &ssao_uniforms);
}
let projection = camera.projection();
let width = self.fbo_ssao.width() as f32;
let height = self.fbo_ssao.height() as f32;
let proj_info = vec4(
-2.0 / (width * projection[(0,0)]),
-2.0 / (height * projection[(1,1)]),
(1.0 - projection[(0,2)]) / projection[(0,0)],
(1.0 - projection[(1,2)]) / projection[(1,1)]
);
let tan_half_fov = (camera.fov().unwrap().to_rad() * 0.5).tan();
let scale = 2. * tan_half_fov;
let pixels_per_meter = (height / scale).abs();
const MIN_AO_SS_RADIUS: f32 = 1.0;
#[allow(unused_mut)]
let mut zfar = camera.far_clip()
.unwrap_or(f32::INFINITY)
.min(pixels_per_meter * radius / MIN_AO_SS_RADIUS);
let depth_func = gl::GREATER;
#[cfg(feature = "ssao_alchemy")]
{
let start_depth_minify = unsafe{ &*self.start_depth_minify.get() };
let mut depth_minify_uniforms = uniforms!{
znear: camera.near_clip(),
zfar: camera.far_clip().unwrap_or(f32::INFINITY),
source_mipmap: 0i32,
projection: projection,
};
depth_minify_uniforms.extend(position_uniforms.iter().cloned());
let fbo = self.fbo_depth_mipmaps[0].with_depth(depth_attachment).unwrap();
let gl_fbo = gl.with_fbo(&fbo);
let gl_fbo = gl_fbo.with_properties(&[
gl::Property::DepthMask(false),
gl::Property::DepthTest(true),
gl::Property::DepthFunc(depth_func)
]);
gl_fbo.draw_vao(&*self.fullscreen_quad, start_depth_minify, &depth_minify_uniforms);
let other_depth_minify = unsafe{ &*self.other_depth_minify.get() };
for (i, fbo) in self.fbo_depth_mipmaps[1..].iter().enumerate(){
let depth_minify_uniforms = uniforms!{
znear: camera.near_clip(),
zfar: camera.far_clip().unwrap_or(f32::INFINITY),
source_mipmap: i as i32,
g_depth: (&*self.linear_depth_mipmaps, 1),
projection: projection,
};
let gl_fbo = gl.with_fbo(fbo);
let gl_fbo = gl_fbo.with_properties(&[
gl::Property::DepthMask(false),
]);
gl_fbo.draw_vao(&*self.fullscreen_quad, other_depth_minify, &depth_minify_uniforms);
}
let ssao = unsafe{ &*self.ssao.get() };
let mut ssao_uniforms = uniforms!{
znear: camera.near_clip(),
zfar: zfar,
radius: parameters.radius,
bias: parameters.bias,
intensity: intensity,
neg_inv_radius2: -1. / (radius * radius),
proj_scale: pixels_per_meter,
proj_info: proj_info,
source_depth_mipmaps: (&*self.linear_depth_mipmaps, 4),
};
ssao_uniforms.extend(position_uniforms.iter().cloned());
ssao_uniforms.extend(normals.into_iter().flat_map(|normals| uniforms!{
g_normal: (normals, 3)
}));
let fbo = self.fbo_ssao.with_depth(depth_attachment).unwrap();
let gl = gl.with_fbo(&fbo);
let gl = gl.with_properties(&[
gl::Property::DepthMask(false),
gl::Property::DepthTest(true),
gl::Property::DepthFunc(depth_func)
]);
gl.clear_color(&WHITE);
gl.draw_vao(&*self.fullscreen_quad, ssao, &ssao_uniforms);
}
#[cfg(not(feature = "ssao_blur_gaussian"))]
{
if blur_radius > 0 {
let ssao_blur = unsafe{ &*self.ssao_blur.get() };
let ssao_tex = self.fbo_ssao.color_tex(0).unwrap();
let ssao_uniforms = uniforms!{
ssao: ssao_tex,
};
let gl = gl.with_fbo(&self.fbo_ssao_blur);
gl.clear_color(&BLACK);
gl.draw_vao(&*self.fullscreen_quad, ssao_blur, &ssao_uniforms);
}
}
#[cfg(feature = "ssao_blur_gaussian")]
{
if blur_radius > 0 {
let ssao_blur = unsafe{ &*self.ssao_blur.get() };
let ssao_tex = self.fbo_ssao.color_tex(0).unwrap();
let mut ssao_uniforms = uniforms!{
source_ssao: (ssao_tex, 0),
g_depth: (&*self.linear_depth_mipmaps, 1),
camera_z_near: camera.near_clip(),
camera_z_far: zfar,
axis: vec2(1i32, 0),
edge_sharpness: 1.,
filter_scale: 1i32,
invRadius: 1. / radius,
proj_info: proj_info,
};
ssao_uniforms.extend(normals.into_iter().flat_map(|normals| uniforms!{
g_normal: (normals, 2)
}));
let fbo = self.fbo_ssao_blur[0].with_depth(depth_attachment).unwrap();
let gl_fbo = gl.with_fbo(&fbo);
let gl_fbo = gl_fbo.with_properties(&[
gl::Property::DepthMask(false),
gl::Property::DepthTest(true),
gl::Property::DepthFunc(depth_func)
]);
gl_fbo.clear_color(&WHITE);
gl_fbo.draw_vao(&*self.fullscreen_quad, ssao_blur, &ssao_uniforms);
let ssao_tex = self.fbo_ssao_blur[0].color_tex(0).unwrap();
let mut ssao_uniforms = uniforms!{
source_ssao: (ssao_tex, 0),
g_depth: (&*self.linear_depth_mipmaps, 1),
camera_z_near: camera.near_clip(),
camera_z_far: zfar,
axis: vec2(0i32, 1),
edge_sharpness: 1.,
filter_scale: 1i32,
invRadius: 1. / radius,
proj_info: proj_info,
};
ssao_uniforms.extend(normals.into_iter().flat_map(|normals| uniforms!{
g_normal: (normals, 2)
}));
let fbo = self.fbo_ssao_blur[1].with_depth(depth_attachment).unwrap();
let gl_fbo = gl.with_fbo(&fbo);
let gl_fbo = gl_fbo.with_properties(&[
gl::Property::DepthMask(false),
gl::Property::DepthTest(true),
gl::Property::DepthFunc(depth_func)
]);
gl_fbo.clear_color(&WHITE);
gl_fbo.draw_vao(&*self.fullscreen_quad, ssao_blur, &ssao_uniforms);
}
}
{
let ssao_apply = unsafe{ &*self.ssao_apply.get() };
let ssao_tex;
if blur_radius > 0 {
#[cfg(not(feature = "ssao_blur_gaussian"))]
{
ssao_tex = self.fbo_ssao_blur.color_tex(0).unwrap();
}
#[cfg(feature = "ssao_blur_gaussian")]
{
ssao_tex = self.fbo_ssao_blur[1].color_tex(0).unwrap();
}
}else{
ssao_tex = self.fbo_ssao.color_tex(0).unwrap();
}
let ssao_uniforms = uniforms!{
ssao: (ssao_tex, 0),
color: (color, 1),
ambient: (ambient, 2),
};
let gl = gl.with_fbo(&*self.fbo_ssao_color);
gl.clear_color(&BLACK);
gl.draw_vao(&*self.fullscreen_quad, &ssao_apply, &ssao_uniforms);
}
Ok(self.fbo_ssao_color.color_tex(0).unwrap())
}
fn combine_color_ambient_(&self,
gl: &gl::Renderer,
color: &gl::Texture,
ambient: &gl::Texture,
dithering: bool) -> Result<&gl::Texture>
{
#[cfg(gl_debug_groups)]
let _debug_group = gl.new_debug_group(0, "Combine color & ambient");
self.refresh_color_ambient_combine(gl, dithering)?;
let combine_color_ambient = unsafe{ &*self.combine_color_ambient.get() };
let uniforms = uniforms!{
color: (color, 0),
ambient: (ambient, 1)
};
let gl = gl.with_fbo(&*self.fbo_ssao_color);
gl.clear_color(&BLACK);
gl.draw_vao(&*self.fullscreen_quad, &combine_color_ambient, &uniforms);
Ok(self.fbo_ssao_color.color_tex(0).unwrap())
}
pub fn combine_color_ambient(&self,
gl: &gl::Renderer,
color: &gl::Texture,
ambient: &gl::Texture) -> Result<&gl::Texture>
{
self.combine_color_ambient_(gl, color, ambient, false)
}
pub fn combine_color_ambient_with_dither(&self,
gl: &gl::Renderer,
color: &gl::Texture,
ambient: &gl::Texture) -> Result<&gl::Texture>
{
self.combine_color_ambient_(gl, color, ambient, true)
}
}