use super::{ProgramSettings, GLSL_VERSION, default_settings, fbo};
use rin_gl::{self as gl, uniforms, Renderer3d};
use rin_util::{Result, Error, LogErr};
use rin_math::{vec2, Angle, Rect};
use std::rc::Rc;
use rin_graphics::{CameraExt, Vertex2DTex};
use std::cell::{Cell, UnsafeCell};
use rin_events::{RangedPropertyMut, Property, ParamsDefault};
#[cfg(gui)]
use rin_gui::{ParameterGroup, EnumParam};
#[cfg(glsl_debug)]
use rin_util::AutoLoader;
const LOW_RESOLUTION_FACTOR: u32 = 2;
pub struct Dof {
fbo_coc: gl::Fbo,
fbo_blur_h: gl::Fbo,
fbo_blur_v: gl::Fbo,
fbo_composite: gl::Fbo,
coc: gl::Program,
blur_h: gl::Program,
blur_v: gl::Program,
composite: UnsafeCell<gl::Program>,
#[cfg(glsl_debug)]
coc_loader: AutoLoader<gl::Program>,
#[cfg(glsl_debug)]
blur_h_loader: AutoLoader<gl::Program>,
#[cfg(glsl_debug)]
blur_v_loader: AutoLoader<gl::Program>,
#[cfg(glsl_debug)]
composite_loader: UnsafeCell<AutoLoader<gl::Program>>,
fullscreen_quad: Rc<gl::SimpleVao<Vertex2DTex>>,
current_dof_debug: Cell<DofDebug>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(gui, derive(EnumParam))]
pub enum DofTy {
Artistic,
Physical,
}
impl DofTy {
fn to_str(&self) -> &'static str {
match self {
DofTy::Artistic => "ARTIST",
DofTy::Physical => "PHYSICAL",
}
}
}
impl Default for DofTy {
fn default() -> Self {
DofTy::Physical
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(gui, derive(EnumParam))]
pub enum DofDebug {
None,
ShowCoc,
ShowRegion,
ShowSignedCoc,
}
impl Default for DofDebug {
fn default() -> Self {
DofDebug::None
}
}
impl ToString for DofDebug {
fn to_string(&self) -> String {
match self {
DofDebug::None => "NO_DEBUG",
DofDebug::ShowCoc => "SHOW_COC",
DofDebug::ShowRegion => "SHOW_REGION",
DofDebug::ShowSignedCoc => "SHOW_SIGNED_COC",
}.to_owned()
}
}
#[derive(ParamsDefault, Clone, Debug)]
#[cfg_attr(gui, derive(ParameterGroup))]
pub struct DofParameters {
#[param(default = 2.2, range = 0.1 .. 128.)]
fstop: RangedPropertyMut<'static, f32>,
#[param(default = 0., range = 0. .. 100.)]
focus_plane_z: RangedPropertyMut<'static, f32>,
#[param(default = 7, range = 3 .. 100)]
max_coc_radius_pixels: RangedPropertyMut<'static, i32>,
#[param(default = 0.14, range = 0.01 .. 1.)]
near_blur_radius_factor: RangedPropertyMut<'static, f32>,
dof_debug: Property<'static, DofDebug>
}
fn coc_settings(ty: DofTy) -> ProgramSettings {
ProgramSettings {
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("MODEL".to_owned(), ty.to_str().to_owned())
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/dof_coc.frag")),
],
.. default_settings()
}
}
fn blur_h_settings() -> ProgramSettings {
ProgramSettings {
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("HORIZONTAL".to_owned(), "1".to_owned())
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/dof_blur.frag")),
],
.. default_settings()
}
}
fn blur_v_settings() -> ProgramSettings {
ProgramSettings {
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("HORIZONTAL".to_owned(), "0".to_owned())
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/dof_blur.frag")),
],
.. default_settings()
}
}
fn composite_settings(dof_debug: DofDebug) -> ProgramSettings {
ProgramSettings {
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("DOF_DEBUG_OPTION".to_owned(), dof_debug.to_string())
],
shaders: vec![
(gl::VERTEX_SHADER, shader!("shaders/full_quad.vs.glsl")),
(gl::FRAGMENT_SHADER, shader!("shaders/dof_composite.frag")),
],
.. default_settings()
}
}
#[derive(Clone, Copy)]
pub enum DofDepth<'a>{
Depth(&'a gl::Texture),
LinearDepth(&'a gl::Texture),
}
impl Dof {
pub fn new(gl: &gl::Renderer, w: u32, h: u32, format: gl::fbo::ColorFormat, fullscreen_quad: Rc<gl::SimpleVao<Vertex2DTex>>) -> Result<Dof> {
let mut fbo_coc = fbo(gl, w, h, gl::fbo::ColorFormat::RGBA16F)
.map_err(|err| Error::with_cause("Couldn't create dof coc fbo", err))?;
fbo_coc.color_tex_mut(0).unwrap().set_min_mag_filters(gl::NEAREST, gl::NEAREST);
let blur_h_near_attachment = gl.new_fbo_color_attachment()
.texture(w/LOW_RESOLUTION_FACTOR, h, gl::fbo::ColorFormat::RGBA16F)
.map_err(|err| Error::with_cause("Couldn't create dof blur attachment", err))?;
let blur_h_blur_attachment = gl.new_fbo_color_attachment()
.texture(w/LOW_RESOLUTION_FACTOR, h, gl::fbo::ColorFormat::RGBA16F)
.map_err(|err| Error::with_cause("Couldn't create dof blur attachment", err))?;
let mut fbo_blur_h = gl.new_fbo().from_colors_no_depth(vec![
blur_h_near_attachment,
blur_h_blur_attachment,
]).map_err(|err| Error::with_cause("Couldn't create dof blur fbo", err))?;
fbo_blur_h.color_tex_mut(0).unwrap().set_min_mag_filters(gl::NEAREST, gl::NEAREST);
fbo_blur_h.color_tex_mut(1).unwrap().set_min_mag_filters(gl::NEAREST, gl::NEAREST);
let blur_v_near_attachment = gl.new_fbo_color_attachment()
.texture(w/LOW_RESOLUTION_FACTOR, h/LOW_RESOLUTION_FACTOR, gl::fbo::ColorFormat::RGBA16F)
.map_err(|err| Error::with_cause("Couldn't create dof blur attachment", err))?;
let blur_v_blur_attachment = gl.new_fbo_color_attachment()
.texture(w/LOW_RESOLUTION_FACTOR, h/LOW_RESOLUTION_FACTOR, gl::fbo::ColorFormat::RGBA16F)
.map_err(|err| Error::with_cause("Couldn't create dof blur attachment", err))?;
let mut fbo_blur_v = gl.new_fbo().from_colors_no_depth(vec![
blur_v_near_attachment,
blur_v_blur_attachment,
]).map_err(|err| Error::with_cause("Couldn't create dof blur fbo", err))?;
fbo_blur_v.color_tex_mut(0).unwrap().set_min_mag_filters(gl::NEAREST, gl::NEAREST);
fbo_blur_v.color_tex_mut(1).unwrap().set_min_mag_filters(gl::NEAREST, gl::NEAREST);
let mut fbo_composite = fbo(gl, w, h, format)
.map_err(|err| Error::with_cause("Couldn't create dof composite fbo", err))?;
fbo_composite.color_tex_mut(0).unwrap().set_min_mag_filters(gl::NEAREST, gl::NEAREST);
let coc_settings = coc_settings(DofTy::default());
let blur_h_settings = blur_h_settings();
let blur_v_settings = blur_v_settings();
let composite_settings = composite_settings(DofDebug::default());
let coc;
let blur_h;
let blur_v;
let composite;
#[cfg(glsl_debug)]
let mut coc_loader = gl.new_auto_program(coc_settings);
#[cfg(glsl_debug)]
let mut blur_h_loader = gl.new_auto_program(blur_h_settings);
#[cfg(glsl_debug)]
let mut blur_v_loader = gl.new_auto_program(blur_v_settings);
#[cfg(glsl_debug)]
let mut composite_loader = gl.new_auto_program(composite_settings);
#[cfg(glsl_debug)]
{
coc = coc_loader.load().log_err("")?;
blur_h = blur_h_loader.load().log_err("")?;
blur_v = blur_v_loader.load().log_err("")?;
composite = composite_loader.load().log_err("")?;
}
#[cfg(not(glsl_debug))]
{
coc = gl.new_program().from_settings(coc_settings)
.log_err("")
.map_err(|err| Error::with_cause("Error loading dof coc shader", err))?;
blur_h = gl.new_program().from_settings(blur_h_settings)
.log_err("")
.map_err(|err| Error::with_cause("Error loading dof blur_h shader", err))?;
blur_v = gl.new_program().from_settings(blur_v_settings)
.log_err("")
.map_err(|err| Error::with_cause("Error loading dof blur_v shader", err))?;
composite = gl.new_program().from_settings(composite_settings)
.log_err("")
.map_err(|err| Error::with_cause("Error loading dof composite shader", err))?;
}
Ok(Dof{
fbo_coc,
fbo_blur_h,
fbo_blur_v,
fbo_composite,
coc,
blur_h,
blur_v,
composite: UnsafeCell::new(composite),
#[cfg(glsl_debug)]
coc_loader,
#[cfg(glsl_debug)]
blur_h_loader,
#[cfg(glsl_debug)]
blur_v_loader,
#[cfg(glsl_debug)]
composite_loader: UnsafeCell::new(composite_loader),
fullscreen_quad,
current_dof_debug: Cell::default(),
})
}
#[cfg(glsl_debug)]
pub fn update(&mut self) {
match self.coc_loader.update(){
Ok(Some(program)) => {
self.coc = program;
println!("dof coc program reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
match self.blur_h_loader.update(){
Ok(Some(program)) => {
self.blur_h = program;
println!("dof blur_h program reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
match self.blur_v_loader.update(){
Ok(Some(program)) => {
self.blur_v = program;
println!("dof blur_v program reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
match unsafe{ (*self.composite_loader.get()).update() }{
Ok(Some(program)) => {
unsafe{ *self.composite.get() = program };
println!("dof composite program reloaded correctly");
}
Err(err) => {
println!("{}", err);
}
_ => {}
}
}
pub fn process(
&self,
gl: &gl::Renderer,
camera: &dyn CameraExt,
viewport: &Rect<i32>,
color: &gl::Texture,
depth: DofDepth,
parameters: &DofParameters) -> &gl::Texture
{
#[cfg(gl_debug_groups)]
let _debug_group = gl.new_debug_group(0, "Depth of field");
if *parameters.dof_debug.get() != self.current_dof_debug.get() {
let composite_settings = composite_settings(*parameters.dof_debug.get());
#[cfg(glsl_debug)]
unsafe {
*self.composite_loader.get() = gl.new_auto_program(composite_settings);
*self.composite.get() = (*self.composite_loader.get()).load().log_err("").unwrap();
}
#[cfg(not(glsl_debug))]
unsafe{
*self.composite.get() = gl.new_program().from_settings(composite_settings)
.log_err("")
.unwrap();
}
self.current_dof_debug.set(*parameters.dof_debug.get());
}
let to_meters = 0.001;
let focal_length_m = to_meters * camera.focal_length().unwrap();
let aperture_m = focal_length_m / parameters.fstop.get();
let sensor_m = to_meters * camera.sensor_width_mm().unwrap();
let view_sensor = viewport.width as f32 / sensor_m;
let scale = aperture_m * focal_length_m / (parameters.focus_plane_z.get() - focal_length_m) * view_sensor;
let mut coc_uniforms = uniforms! {
zfar: camera.far_clip().unwrap_or(f32::INFINITY),
znear: camera.near_clip(),
focusPlaneZ: parameters.focus_plane_z,
scale: scale,
aperture: aperture_m,
focallength: focal_length_m,
view_sensor: view_sensor,
COLOR_buffer: (color, 2),
};
match depth {
DofDepth::Depth(depth) => coc_uniforms.extend_from_slice(&uniforms!{
g_depth: (depth, 1)
}),
DofDepth::LinearDepth(linear_depth) => coc_uniforms.extend_from_slice(&uniforms!{
g_linear_depth: (linear_depth, 1)
}),
}
gl.with_fbo(&self.fbo_coc)
.draw_vao(&*self.fullscreen_quad, &self.coc, &coc_uniforms);
let blur_source = self.fbo_coc.color_tex(0).unwrap();
let blur_h_uniforms = uniforms!{
maxCoCRadiusPixels: parameters.max_coc_radius_pixels,
blurSourceBuffer: (blur_source, 1),
invNearBlurRadiusPixels: parameters.near_blur_radius_factor,
lowResolutionFactor: LOW_RESOLUTION_FACTOR as f32,
fieldOfView: camera.fov().unwrap().to_rad().value(),
};
gl.with_fbo(&self.fbo_blur_h)
.draw_vao(&*self.fullscreen_quad, &self.blur_h, &blur_h_uniforms);
let near_h_tex = self.fbo_blur_h.color_tex(0).unwrap();
let blur_h_tex = self.fbo_blur_h.color_tex(1).unwrap();
let blur_v_uniforms = uniforms!{
maxCoCRadiusPixels: parameters.max_coc_radius_pixels,
blurSourceBuffer: (blur_h_tex, 1),
nearSourceBuffer: (near_h_tex, 2),
invNearBlurRadiusPixels: parameters.near_blur_radius_factor,
lowResolutionFactor: LOW_RESOLUTION_FACTOR as f32,
fieldOfView: camera.fov().unwrap().to_rad().value(),
};
gl.with_fbo(&self.fbo_blur_v)
.draw_vao(&*self.fullscreen_quad, &self.blur_v, &blur_v_uniforms);
let near_v_tex = self.fbo_blur_v.color_tex(0).unwrap();
let blur_v_tex = self.fbo_blur_v.color_tex(1).unwrap();
let composite_uniforms = uniforms!{
packedBuffer: (blur_source, 1),
packedBufferInvSize: vec2(1. / color.width() as f32, 1. / color.height() as f32),
blurBuffer: (blur_v_tex, 2),
nearBuffer: (near_v_tex, 3),
farRadiusRescale: 1.,
};
gl.with_fbo(&self.fbo_composite)
.draw_vao(&*self.fullscreen_quad, unsafe{ &*self.composite.get() }, &composite_uniforms);
self.fbo_composite.color_tex(0).unwrap()
}
}