#[cfg(glsl_debug)]
use rin_util::AutoLoader;
#[cfg(glsl_debug)]
use std::path::{Path, PathBuf};
use rin_math::{pnt2, Rect};
use rin_graphics as graphics;
use rin_util::{self as util, Error};
#[cfg(feature = "serialize")]
use std::fmt;
use std::borrow::Borrow;
use rin_graphics::CameraExt;
use rin_gl as gl;
use std::rc::Rc;
#[cfg(feature="gaussian_bloom")]
use rin_gl::types::GLenum;
#[cfg(feature = "serialize")]
use serde::de::{self, Deserialize, Deserializer, Visitor, SeqAccess, MapAccess};
#[cfg(feature = "serialize")]
use rin_events::RangedPropertyMut;
use rin_events::Property;
#[cfg(gui)]
use rin_gui::ParameterGroup;
pub use fxaa::*;
pub use ssao::*;
pub use dof::*;
pub use bloom::*;
pub use tonemap::*;
pub use lut::*;
#[cfg(glsl_debug)]
macro_rules! shader {
($file: expr) => {
std::path::Path::new(file!()).parent().unwrap().join($file)
};
}
#[cfg(not(glsl_debug))]
macro_rules! shader {
($file: expr) => {
include_str!($file).to_owned()
};
}
pub mod fxaa;
pub mod ssao;
pub mod dof;
pub mod bloom;
pub mod tonemap;
pub mod lut;
const GLSL_VERSION: usize = gl::default_glsl_version();
pub struct PostProcessing{
fxaa: Fxaa,
bloom: Bloom,
ssao: Ssao,
dof: Dof,
tonemap: Tonemap,
lut: Lut,
}
fn fbo(gl: &gl::Renderer, w: u32, h: u32, format: gl::fbo::ColorFormat) -> util::Result<gl::Fbo>{
let color = gl.new_fbo_color_attachment().texture(w, h, format)
.map_err(|e| Error::with_cause("Error creating color attachment", e))?;
gl.new_fbo().from_color_no_depth(color)
.map_err(|e| Error::with_cause("Error creating fbo", e))
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(gui, derive(ParameterGroup))]
pub struct Parameters{
pub fxaa: Property<'static, bool>,
pub bloom: Property<'static, bool>,
pub bloom_parameters: BloomParameters,
pub ssao: Property<'static, bool>,
pub ssao_parameters: SSAOParameters,
pub dithering: Property<'static, bool>,
pub dof: Property<'static, bool>,
pub dof_parameters: DofParameters,
pub tonemap_parameters: TonemapParameters,
pub lut: Property<'static, bool>,
pub lut_parameters: LutParameters,
}
impl Default for Parameters {
fn default() -> Parameters {
let bloom_parameters = BloomParameters::default();
let mut tonemap_parameters = TonemapParameters::default();
let mut ssao_parameters = SSAOParameters::default();
#[cfg(feature="gaussian_bloom")]
{
tonemap_parameters.bloom_radius = bloom_parameters.radius.clone().into_property();
}
tonemap_parameters.bloom_strength = bloom_parameters.intensity.clone().into_property();
tonemap_parameters.bloom_blend = bloom_parameters.blend.clone();
let dithering = Property::new(false);
ssao_parameters.dithering = dithering.clone();
Parameters {
fxaa: Property::new(false),
bloom: Property::new(false),
bloom_parameters,
ssao: Property::new(false),
ssao_parameters,
dithering,
dof: Property::new(false),
dof_parameters: DofParameters::default(),
tonemap_parameters,
lut: Property::new(false),
lut_parameters: LutParameters::default(),
}
}
}
#[cfg(glsl_debug)]
type ProgramSettings = gl::autoload::ProgramSettings<PathBuf>;
#[cfg(not(glsl_debug))]
type ProgramSettings = gl::program::Settings<String>;
fn default_settings() -> ProgramSettings {
#[cfg(not(glsl_debug))]
let includes = {
let mut includes = rin_gl::HashMap::new();
includes.insert(
"reconstruct_depth.glsl".to_owned(),
include_str!("shaders/reconstruct_depth.glsl").to_owned()
);
includes
};
ProgramSettings{
version: GLSL_VERSION,
precision: gl::program::ShaderPrecision::High,
extensions: Vec::new(),
defines: Vec::new(),
shaders: Vec::new(),
#[cfg(not(glsl_debug))]
base_path: String::new(),
#[cfg(not(glsl_debug))]
bindings: gl::default_attribute_bindings().clone(),
#[cfg(not(glsl_debug))]
includes,
#[cfg(glsl_debug)]
includes_dictionary: rin_gl::HashMap::new(),
#[cfg(glsl_debug)]
shader_replace: Vec::new(),
#[cfg(glsl_debug)]
includes_search_paths: Vec::new(),
}
}
impl PostProcessing{
pub fn new(gl: &gl::Renderer, w: u32, h: u32, format: gl::fbo::ColorFormat) -> util::Result<PostProcessing>{
let fullscreen_quad = gl.new_simple_vao().from_data_bindings(
&graphics::rectangle_texcoords(&pnt2(-1., -1.), 2., 2., 0., 1., 1., 0.),
gl::default_attribute_bindings(),
gl::STATIC_DRAW
).unwrap();
let fullscreen_quad = Rc::new(fullscreen_quad);
let fxaa = Fxaa::new(gl, w, h, format, fullscreen_quad.clone())?;
let bloom = Bloom::new(gl, w, h, format, fullscreen_quad.clone())?;
let ssao = Ssao::new(gl, w, h, format, fullscreen_quad.clone())?;
let dof = Dof::new(gl, w, h, format, fullscreen_quad.clone())?;
let tonemap = Tonemap::new(gl, w, h, format, fullscreen_quad.clone())?;
let lut = Lut::new(gl, w, h, format, fullscreen_quad.clone())?;
Ok(PostProcessing{
fxaa,
bloom,
ssao,
dof,
tonemap,
lut,
})
}
#[cfg(glsl_debug)]
pub fn update(&mut self){
self.fxaa.update();
self.bloom.update();
self.ssao.update();
self.dof.update();
self.tonemap.update();
self.lut.update();
}
pub fn ssao(&self, gl: &gl::Renderer,
camera: &dyn CameraExt,
color: &gl::Texture,
position: SSAOPosition,
normals: Option<&gl::Texture>,
ambient: &gl::Texture,
parameters: &SSAOParameters) -> util::Result<&gl::Texture>
{
self.ssao.process(gl, camera, color, position, normals, ambient, parameters)
}
pub fn dof(
&self,
gl: &gl::Renderer,
camera: &dyn CameraExt,
viewport: &Rect<i32>,
color: &gl::Texture,
depth: DofDepth,
parameters: &DofParameters) -> &gl::Texture
{
self.dof.process(gl, camera, viewport, color, depth, parameters)
}
pub fn fxaa_texture(&self) -> &gl::Texture{
self.fxaa.texture()
}
#[cfg(not(feature="gaussian_bloom"))]
pub fn bloom_down_texture(&self, idx: usize) -> &gl::Texture{
self.bloom.bloom_down_texture(idx)
}
#[cfg(not(feature="gaussian_bloom"))]
pub fn bloom_up_texture(&self, idx: usize) -> &gl::Texture{
self.bloom.bloom_up_texture(idx)
}
pub fn ssao_texture(&self) -> &gl::Texture{
self.ssao.ssao_texture()
}
pub fn ssao_blur_texture(&self) -> &gl::Texture{
self.ssao.ssao_blur_texture()
}
pub fn ssao_color_texture(&self) -> &gl::Texture{
self.ssao.ssao_color_texture()
}
pub fn ssao_color_attachment(&self) -> &gl::fbo::ColorAttachment{
self.ssao.ssao_color_attachment()
}
pub fn ssao_fbo(&self) -> &Rc<gl::Fbo> {
self.ssao.fbo()
}
pub fn last_fbo(&self, parameters: &Parameters) -> &Rc<gl::Fbo> {
if *parameters.lut.get() && self.lut.is_loaded() {
self.lut.fbo()
}else{
self.tonemap.fbo()
}
}
pub fn combine_color_ambient(&self,
gl: &gl::Renderer,
color: &gl::Texture,
ambient: &gl::Texture) -> util::Result<&gl::Texture>
{
self.ssao.combine_color_ambient(gl, color, ambient)
}
pub fn combine_color_ambient_with_dither(&self,
gl: &gl::Renderer,
color: &gl::Texture,
ambient: &gl::Texture) -> util::Result<&gl::Texture>
{
self.ssao.combine_color_ambient_with_dither(gl, color, ambient)
}
pub fn fxaa(&self, gl: &gl::Renderer, tex: &gl::Texture) -> &gl::Texture {
self.fxaa.process(gl, tex)
}
pub fn bloom(&self, gl: &gl::Renderer, tex: &gl::Texture, parameters: &BloomParameters) -> &gl::Texture{
self.bloom.process(gl, tex, parameters)
}
pub fn tonemap(&self, gl: &gl::Renderer,
tex: &gl::Texture,
bloomed_tex: Option<&gl::Texture>,
parameters: &TonemapParameters) -> util::Result<&gl::Texture>
{
self.tonemap.process(gl, tex, bloomed_tex, parameters)
}
pub fn lut<'a>(&'a self, gl: &gl::Renderer,
tex: &'a gl::Texture,
parameters: &LutParameters) -> &gl::Texture
{
self.lut.process(gl, tex, parameters)
}
pub fn process_all(&self, gl: &gl::Renderer,
camera: &dyn CameraExt,
viewport: &Rect<i32>,
color: &gl::Texture,
position: Option<SSAOPosition>,
normals: Option<&gl::Texture>,
dof_depth: Option<DofDepth>,
ambient: &gl::Texture,
parameters: &Parameters) -> util::Result<&gl::Texture>
{
let tex = self.process_until_ssao(gl, camera, color, position, normals, ambient, parameters)?;
self.process_after_ssao(gl, camera, viewport, tex, dof_depth, parameters)
}
pub fn process_until_ssao(&self, gl: &gl::Renderer,
camera: &dyn CameraExt,
color: &gl::Texture,
position: Option<SSAOPosition>,
normals: Option<&gl::Texture>,
ambient: &gl::Texture,
parameters: &Parameters) -> util::Result<&gl::Texture>
{
let ssao = *parameters.ssao.borrow();
let dithering = *parameters.ssao_parameters.dithering.borrow();
if ssao {
if let Some(position) = position {
self.ssao(gl, camera, color, position, normals, ambient, ¶meters.ssao_parameters)
}else{
return Err(util::Error::new("Trying to apply ssao without a position source"));
}
}else if dithering {
self.combine_color_ambient_with_dither(gl, color, ambient)
}else{
self.combine_color_ambient(gl, color, ambient)
}
}
pub fn process_after_ssao<'a>(&'a self,
gl: &gl::Renderer,
camera: &dyn CameraExt,
viewport: &Rect<i32>,
tex: &'a gl::Texture,
depth: Option<DofDepth>,
parameters: &Parameters) -> util::Result<&'a gl::Texture>
{
let aa_tex = if *parameters.fxaa.borrow() {
self.fxaa(gl, tex)
}else{
tex
};
let pre_bloomed_tex = if *parameters.dof.borrow() {
if let Some(depth) = depth {
self.dof(gl, camera, viewport, aa_tex, depth, ¶meters.dof_parameters)
}else{
return Err(util::Error::new("Trying to apply dof without a depth source"));
}
}else{
aa_tex
};
let tonedmapped = if *parameters.bloom.borrow() {
let bloomed_tex = self.bloom(gl, pre_bloomed_tex, ¶meters.bloom_parameters);
self.tonemap(gl, pre_bloomed_tex, Some(bloomed_tex), ¶meters.tonemap_parameters)?
}else{
self.tonemap(gl, pre_bloomed_tex, None, ¶meters.tonemap_parameters)?
};
if *parameters.lut.get() {
Ok(self.lut(gl, tonedmapped, ¶meters.lut_parameters))
}else{
Ok(tonedmapped)
}
}
}
#[cfg(all(gui, feature = "serialize"))]
impl<'de> Deserialize<'de> for TonemapParameters{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
#[allow(non_camel_case_types)]
enum Field{
Contrast, Brightness, Ty, Exposure_Bias,
A,B,C,D,E,F,W,
}
struct ParametersVisitor;
impl<'de> Visitor<'de> for ParametersVisitor {
type Value = TonemapParameters;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Parameters")
}
fn visit_seq<V>(self, mut seq: V) -> Result<TonemapParameters, V::Error>
where V: SeqAccess<'de>
{
let contrast = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(5, &self))?;
let brightness = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(6, &self))?;
let ty = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(7, &self))?;
let exposure_bias = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(8, &self))?;
let a: RangedPropertyMut<'static,f32> = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(9, &self))?;
let b: RangedPropertyMut<'static,f32> = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(10, &self))?;
let c: RangedPropertyMut<'static,f32> = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(11, &self))?;
let d: RangedPropertyMut<'static,f32> = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(12, &self))?;
let e: RangedPropertyMut<'static,f32> = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(13, &self))?;
let f: RangedPropertyMut<'static,f32> = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(14, &self))?;
let w: RangedPropertyMut<'static,f32> = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(15, &self))?;
let curve_data = tonemap_response_function(&a,&b,&c,&d,&e,&f,&w);
Ok(TonemapParameters{
contrast, brightness,
ty, exposure_bias,
a, b, c, d, e, f, w, curve_data,
})
}
fn visit_map<V>(self, mut map: V) -> Result<TonemapParameters, V::Error>
where V: MapAccess<'de>
{
let mut contrast = None;
let mut brightness = None;
let mut ty = None;
let mut exposure_bias = None;
let mut a = None;
let mut b = None;
let mut c = None;
let mut d = None;
let mut e = None;
let mut f = None;
let mut w = None;
while let Some(key) = map.next_key()? {
match key {
Field::Contrast => {
if contrast.is_some() {
return Err(de::Error::duplicate_field("contrast"));
}
contrast = Some(map.next_value()?);
}
Field::Brightness => {
if brightness.is_some() {
return Err(de::Error::duplicate_field("brightness"));
}
brightness = Some(map.next_value()?);
}
Field::Ty => {
if ty.is_some() {
return Err(de::Error::duplicate_field("tonemap_type"));
}
ty = Some(map.next_value()?);
}
Field::Exposure_Bias => {
if exposure_bias.is_some() {
return Err(de::Error::duplicate_field("exposure_bias"));
}
exposure_bias = Some(map.next_value()?);
}
Field::A => {
if a.is_some() {
return Err(de::Error::duplicate_field("a"));
}
a = Some(map.next_value()?);
}
Field::B => {
if b.is_some() {
return Err(de::Error::duplicate_field("b"));
}
b = Some(map.next_value()?);
}
Field::C => {
if c.is_some() {
return Err(de::Error::duplicate_field("c"));
}
c = Some(map.next_value()?);
}
Field::D => {
if d.is_some() {
return Err(de::Error::duplicate_field("d"));
}
d = Some(map.next_value()?);
}
Field::E => {
if e.is_some() {
return Err(de::Error::duplicate_field("e"));
}
e = Some(map.next_value()?);
}
Field::F => {
if f.is_some() {
return Err(de::Error::duplicate_field("f"));
}
f = Some(map.next_value()?);
}
Field::W => {
if w.is_some() {
return Err(de::Error::duplicate_field("w"));
}
w = Some(map.next_value()?);
}
}
}
let contrast = contrast.ok_or_else(|| de::Error::missing_field("contrast"))?;
let brightness = brightness.ok_or_else(|| de::Error::missing_field("brightness"))?;
let ty = ty.ok_or_else(|| de::Error::missing_field("ty"))?;
let exposure_bias = exposure_bias.ok_or_else(|| de::Error::missing_field("exposure_bias"))?;
let a = a.ok_or_else(|| de::Error::missing_field("a"))?;
let b = b.ok_or_else(|| de::Error::missing_field("b"))?;
let c = c.ok_or_else(|| de::Error::missing_field("c"))?;
let d = d.ok_or_else(|| de::Error::missing_field("d"))?;
let e = e.ok_or_else(|| de::Error::missing_field("e"))?;
let f = f.ok_or_else(|| de::Error::missing_field("f"))?;
let w = w.ok_or_else(|| de::Error::missing_field("w"))?;
let curve_data = tonemap_response_function(&a,&b,&c,&d,&e,&f,&w);
Ok(TonemapParameters{
contrast, brightness,
ty, exposure_bias,
a, b, c, d, e, f, w, curve_data,
})
}
}
const FIELDS: &'static [&'static str] = &[
"contrast", "brightness",
"ty", "exposure_bias",
"a", "b", "c", "d", "e", "f", "w"
];
deserializer.deserialize_struct("TonemapParameters", FIELDS, ParametersVisitor)
}
}