From 35e1a43a38f1e8f13303fe27f39907edeb417b35 Mon Sep 17 00:00:00 2001 From: robojumper Date: Mon, 6 May 2019 10:22:47 +0200 Subject: [PATCH 1/2] Add post-process pipeline Former-commit-id: aed1fff25f7fd95fe040b0877011cbe57e77eba3 --- voxygen/shaders/postprocess.frag | 30 ++++++ voxygen/shaders/postprocess.vert | 27 +++++ voxygen/src/render/mod.rs | 3 + voxygen/src/render/pipelines/mod.rs | 1 + voxygen/src/render/pipelines/postprocess.rs | 71 +++++++++++++ voxygen/src/render/renderer.rs | 110 ++++++++++++++++++-- voxygen/src/scene/mod.rs | 22 +++- voxygen/src/window.rs | 1 + 8 files changed, 255 insertions(+), 10 deletions(-) create mode 100644 voxygen/shaders/postprocess.frag create mode 100644 voxygen/shaders/postprocess.vert create mode 100644 voxygen/src/render/pipelines/postprocess.rs diff --git a/voxygen/shaders/postprocess.frag b/voxygen/shaders/postprocess.frag new file mode 100644 index 0000000000..d3adcf5169 --- /dev/null +++ b/voxygen/shaders/postprocess.frag @@ -0,0 +1,30 @@ +#version 330 core + +uniform sampler2D src_color; + +in vec2 f_pos; + +layout (std140) +uniform u_locals { + vec4 nul; +}; + +layout (std140) +uniform u_globals { + mat4 view_mat; + mat4 proj_mat; + vec4 cam_pos; + vec4 focus_pos; + vec4 view_distance; + vec4 time_of_day; + vec4 tick; +}; + +out vec4 tgt_color; + +void main() { + // Uncomment to invert colors + //tgt_color = vec4(vec3(1.0, 1.0, 1.0) - texture2D(src_color, (f_pos + 1.0) / 2.0).xyz, 1.0); + tgt_color = texture2D(src_color, (f_pos + 1.0) / 2.0); + +} diff --git a/voxygen/shaders/postprocess.vert b/voxygen/shaders/postprocess.vert new file mode 100644 index 0000000000..91cde43fb7 --- /dev/null +++ b/voxygen/shaders/postprocess.vert @@ -0,0 +1,27 @@ +#version 330 core + +in vec2 v_pos; + +layout (std140) +uniform u_locals { + vec4 nul; +}; + +layout (std140) +uniform u_globals { + mat4 view_mat; + mat4 proj_mat; + vec4 cam_pos; + vec4 focus_pos; + vec4 view_distance; + vec4 time_of_day; + vec4 tick; +}; + +out vec2 f_pos; + +void main() { + f_pos = v_pos; + + gl_Position = vec4(v_pos, 0.0, 1.0); +} diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index a8cd2936c7..d11de34885 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -13,6 +13,9 @@ pub use self::{ model::Model, pipelines::{ figure::{BoneData as FigureBoneData, FigurePipeline, Locals as FigureLocals}, + postprocess::{ + create_mesh as create_pp_mesh, Locals as PostProcessLocals, PostProcessPipeline, + }, skybox::{create_mesh as create_skybox_mesh, Locals as SkyboxLocals, SkyboxPipeline}, terrain::{Locals as TerrainLocals, TerrainPipeline}, ui::{ diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index 2b4b8ba7d9..c425c2d5eb 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -1,4 +1,5 @@ pub mod figure; +pub mod postprocess; pub mod skybox; pub mod terrain; pub mod ui; diff --git a/voxygen/src/render/pipelines/postprocess.rs b/voxygen/src/render/pipelines/postprocess.rs new file mode 100644 index 0000000000..feca548759 --- /dev/null +++ b/voxygen/src/render/pipelines/postprocess.rs @@ -0,0 +1,71 @@ +// Library +use gfx::{ + self, + gfx_constant_struct_meta, + // Macros + gfx_defines, + gfx_impl_struct_meta, + gfx_pipeline, + gfx_pipeline_inner, + gfx_vertex_struct_meta, +}; + +// Local +use super::{ + super::{Mesh, Pipeline, TgtColorFmt, TgtDepthFmt, Tri}, + Globals, +}; + +gfx_defines! { + vertex Vertex { + pos: [f32; 2] = "v_pos", + } + + constant Locals { + nul: [f32; 4] = "nul", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + + src_sampler: gfx::TextureSampler<::View> = "src_color", + + tgt_color: gfx::RenderTarget = "tgt_color", + tgt_depth: gfx::DepthTarget = gfx::preset::depth::PASS_TEST, + } +} + +impl Locals { + pub fn default() -> Self { + Self { nul: [0.0; 4] } + } +} + +pub struct PostProcessPipeline; + +impl Pipeline for PostProcessPipeline { + type Vertex = Vertex; +} + +pub fn create_mesh() -> Mesh { + let mut mesh = Mesh::new(); + + #[rustfmt::skip] + mesh.push_tri(Tri::new( + Vertex { pos: [ 1.0, -1.0] }, + Vertex { pos: [-1.0, 1.0] }, + Vertex { pos: [-1.0, -1.0] }, + )); + + #[rustfmt::skip] + mesh.push_tri(Tri::new( + Vertex { pos: [1.0, -1.0] }, + Vertex { pos: [1.0, 1.0] }, + Vertex { pos: [-1.0, 1.0] }, + )); + + mesh +} diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 3c468bd196..edd9705eaf 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -3,13 +3,14 @@ use super::{ gfx_backend, mesh::Mesh, model::Model, - pipelines::{figure, skybox, terrain, ui, Globals}, + pipelines::{figure, postprocess, skybox, terrain, ui, Globals}, texture::Texture, Pipeline, RenderError, }; use gfx::{ self, - traits::{Device, FactoryExt}, + handle::Sampler, + traits::{Device, Factory, FactoryExt}, }; use image; use vek::*; @@ -24,6 +25,12 @@ pub type TgtColorView = gfx::handle::RenderTargetView; +/// A handle to a render color target as a resource. +pub type TgtColorRes = gfx::handle::ShaderResourceView< + gfx_backend::Resources, + ::View, +>; + /// A type that encapsulates rendering state. `Renderer` is central to Voxygen's rendering /// subsystem and contains any state necessary to interact with the GPU, along with pipeline state /// objects (PSOs) needed to renderer different kinds of models to the screen. @@ -32,13 +39,21 @@ pub struct Renderer { encoder: gfx::Encoder, factory: gfx_backend::Factory, + win_color_view: TgtColorView, + win_depth_view: TgtDepthView, + tgt_color_view: TgtColorView, tgt_depth_view: TgtDepthView, + tgt_color_res: TgtColorRes, + + sampler: Sampler, + skybox_pipeline: GfxPipeline>, figure_pipeline: GfxPipeline>, terrain_pipeline: GfxPipeline>, ui_pipeline: GfxPipeline>, + postprocess_pipeline: GfxPipeline>, } impl Renderer { @@ -47,8 +62,8 @@ impl Renderer { pub fn new( device: gfx_backend::Device, mut factory: gfx_backend::Factory, - tgt_color_view: TgtColorView, - tgt_depth_view: TgtDepthView, + win_color_view: TgtColorView, + win_depth_view: TgtDepthView, ) -> Result { // Construct a pipeline for rendering skyboxes let skybox_pipeline = create_pipeline( @@ -82,29 +97,86 @@ impl Renderer { include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/ui.frag")), )?; + // Construct a pipeline for rendering our post-processing + let postprocess_pipeline = create_pipeline( + &mut factory, + postprocess::pipe::new(), + include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/shaders/postprocess.vert" + )), + include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/shaders/postprocess.frag" + )), + )?; + + let dims = win_color_view.get_dimensions(); + let d_dims = win_depth_view.get_dimensions(); + + let (_, tgt_color_res, tgt_color_view) = factory + .create_render_target::(dims.0, dims.1) + .map_err(RenderError::CombinedError)?; + let (_, _, tgt_depth_view) = factory + .create_depth_stencil(d_dims.0, d_dims.1) + .map_err(RenderError::CombinedError)?; + + let sampler = factory.create_sampler_linear(); + Ok(Self { device, encoder: factory.create_command_buffer().into(), factory, + win_color_view, + win_depth_view, + tgt_color_view, tgt_depth_view, + tgt_color_res, + sampler, + skybox_pipeline, figure_pipeline, terrain_pipeline, ui_pipeline, + postprocess_pipeline, }) } /// Get references to the internal render target views that get displayed directly by the window. pub fn target_views(&self) -> (&TgtColorView, &TgtDepthView) { - (&self.tgt_color_view, &self.tgt_depth_view) + (&self.win_color_view, &self.win_depth_view) } /// Get mutable references to the internal render target views that get displayed directly by the window. pub fn target_views_mut(&mut self) -> (&mut TgtColorView, &mut TgtDepthView) { - (&mut self.tgt_color_view, &mut self.tgt_depth_view) + (&mut self.win_color_view, &mut self.win_depth_view) + } + + pub fn on_resize(&mut self) -> Result<(), RenderError> { + let dims = self.win_color_view.get_dimensions(); + let d_dims = self.win_depth_view.get_dimensions(); + + if dims.0 > 0 && dims.1 > 0 { + let (_, tgt_color_res, tgt_color_view) = self + .factory + .create_render_target::(dims.0, dims.1) + .map_err(RenderError::CombinedError)?; + self.tgt_color_res = tgt_color_res; + self.tgt_color_view = tgt_color_view; + } + + if d_dims.0 > 0 && d_dims.1 > 0 { + let (_, _, tgt_depth_view) = self + .factory + .create_depth_stencil(d_dims.0, d_dims.1) + .map_err(RenderError::CombinedError)?; + self.tgt_depth_view = tgt_depth_view; + } + + Ok(()) } /// Get the resolution of the render target. @@ -120,6 +192,8 @@ impl Renderer { pub fn clear(&mut self, col: Rgba) { self.encoder.clear(&self.tgt_color_view, col.into_array()); self.encoder.clear_depth(&self.tgt_depth_view, 1.0); + self.encoder.clear(&self.win_color_view, col.into_array()); + self.encoder.clear_depth(&self.win_depth_view, 1.0); } /// Perform all queued draw calls for this frame and clean up discarded items. @@ -261,11 +335,31 @@ impl Renderer { h: max.y - min.y, }, tex: (tex.srv.clone(), tex.sampler.clone()), - tgt_color: self.tgt_color_view.clone(), - tgt_depth: self.tgt_depth_view.clone(), + tgt_color: self.win_color_view.clone(), + tgt_depth: self.win_depth_view.clone(), }, ); } + + pub fn render_post_process( + &mut self, + model: &Model, + globals: &Consts, + locals: &Consts, + ) { + self.encoder.draw( + &model.slice, + &self.postprocess_pipeline.pso, + &postprocess::pipe::Data { + vbuf: model.vbuf.clone(), + locals: locals.buf.clone(), + globals: globals.buf.clone(), + src_sampler: (self.tgt_color_res.clone(), self.sampler.clone()), + tgt_color: self.win_color_view.clone(), + tgt_depth: self.win_depth_view.clone(), + }, + ) + } } struct GfxPipeline { diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 95c875c845..0c5c2d9376 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -10,8 +10,8 @@ use crate::{ }, mesh::Meshable, render::{ - create_skybox_mesh, Consts, FigureLocals, Globals, Model, Renderer, SkyboxLocals, - SkyboxPipeline, + create_pp_mesh, create_skybox_mesh, Consts, FigureLocals, Globals, Model, + PostProcessLocals, PostProcessPipeline, Renderer, SkyboxLocals, SkyboxPipeline, }, window::Event, }; @@ -28,11 +28,17 @@ struct Skybox { locals: Consts, } +struct PostProcess { + model: Model, + locals: Consts, +} + pub struct Scene { globals: Consts, camera: Camera, skybox: Skybox, + postprocess: PostProcess, terrain: Terrain, figure_cache: FigureCache, @@ -51,6 +57,12 @@ impl Scene { model: renderer.create_model(&create_skybox_mesh()).unwrap(), locals: renderer.create_consts(&[SkyboxLocals::default()]).unwrap(), }, + postprocess: PostProcess { + model: renderer.create_model(&create_pp_mesh()).unwrap(), + locals: renderer + .create_consts(&[PostProcessLocals::default()]) + .unwrap(), + }, terrain: Terrain::new(), figure_cache: FigureCache::new(), } @@ -145,5 +157,11 @@ impl Scene { // Render terrain and figures self.terrain.render(renderer, &self.globals); self.figure_cache.render(renderer, client, &self.globals); + + renderer.render_post_process( + &self.postprocess.model, + &self.globals, + &self.postprocess.locals, + ); } } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index a1e80b33a6..824b679d0b 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -102,6 +102,7 @@ impl Window { glutin::WindowEvent::Resized(glutin::dpi::LogicalSize { width, height }) => { let (mut color_view, mut depth_view) = renderer.target_views_mut(); gfx_window_glutin::update_views(&window, &mut color_view, &mut depth_view); + renderer.on_resize().unwrap(); events.push(Event::Resize(Vec2::new(width as u32, height as u32))); } glutin::WindowEvent::ReceivedCharacter(c) => events.push(Event::Char(c)), From 7034419fd8da98d352e2804d777940ae0d63f5ae Mon Sep 17 00:00:00 2001 From: robojumper Date: Mon, 6 May 2019 14:03:15 +0200 Subject: [PATCH 2/2] Slightly improve render target creation code Former-commit-id: 7ce92cce35834b006df9dacb4b4dd3e88d603b4e --- voxygen/src/render/renderer.rs | 46 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index edd9705eaf..f3645326d7 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -112,14 +112,8 @@ impl Renderer { )?; let dims = win_color_view.get_dimensions(); - let d_dims = win_depth_view.get_dimensions(); - - let (_, tgt_color_res, tgt_color_view) = factory - .create_render_target::(dims.0, dims.1) - .map_err(RenderError::CombinedError)?; - let (_, _, tgt_depth_view) = factory - .create_depth_stencil(d_dims.0, d_dims.1) - .map_err(RenderError::CombinedError)?; + let (tgt_color_view, tgt_depth_view, tgt_color_res) = + Self::create_rt_views(&mut factory, (dims.0, dims.1))?; let sampler = factory.create_sampler_linear(); @@ -155,30 +149,32 @@ impl Renderer { (&mut self.win_color_view, &mut self.win_depth_view) } + /// Resize internal render targets to match window render target dimensions pub fn on_resize(&mut self) -> Result<(), RenderError> { let dims = self.win_color_view.get_dimensions(); - let d_dims = self.win_depth_view.get_dimensions(); - if dims.0 > 0 && dims.1 > 0 { - let (_, tgt_color_res, tgt_color_view) = self - .factory - .create_render_target::(dims.0, dims.1) - .map_err(RenderError::CombinedError)?; - self.tgt_color_res = tgt_color_res; - self.tgt_color_view = tgt_color_view; - } - - if d_dims.0 > 0 && d_dims.1 > 0 { - let (_, _, tgt_depth_view) = self - .factory - .create_depth_stencil(d_dims.0, d_dims.1) - .map_err(RenderError::CombinedError)?; - self.tgt_depth_view = tgt_depth_view; - } + let (tgt_color_view, tgt_depth_view, tgt_color_res) = + Self::create_rt_views(&mut self.factory, (dims.0, dims.1))?; + self.tgt_color_res = tgt_color_res; + self.tgt_color_view = tgt_color_view; + self.tgt_depth_view = tgt_depth_view; Ok(()) } + fn create_rt_views( + factory: &mut gfx_device_gl::Factory, + size: (u16, u16), + ) -> Result<(TgtColorView, TgtDepthView, TgtColorRes), RenderError> { + let (_, tgt_color_res, tgt_color_view) = factory + .create_render_target::(size.0, size.1) + .map_err(RenderError::CombinedError)?;; + let tgt_depth_view = factory + .create_depth_stencil_view_only::(size.0, size.1) + .map_err(RenderError::CombinedError)?;; + Ok((tgt_color_view, tgt_depth_view, tgt_color_res)) + } + /// Get the resolution of the render target. pub fn get_resolution(&self) -> Vec2 { Vec2::new(