// SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2011-2015 the SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 3 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA #include "post_processing.hpp" #include "config/user_config.hpp" #include "graphics/callbacks.hpp" #include "graphics/camera.hpp" #include "graphics/central_settings.hpp" #include "graphics/glwrap.hpp" #include "graphics/gl_headers.hpp" #include "graphics/irr_driver.hpp" #include "graphics/mlaa_areamap.hpp" #include "graphics/shaders.hpp" #include "graphics/shared_gpu_objects.hpp" #include "graphics/stk_mesh_scene_node.hpp" #include "graphics/weather.hpp" #include "io/file_manager.hpp" #include "karts/abstract_kart.hpp" #include "karts/kart_model.hpp" #include "modes/world.hpp" #include "physics/physics.hpp" #include "race/race_manager.hpp" #include "tracks/track.hpp" #include "utils/log.hpp" #include "utils/profiler.hpp" #include "utils/cpp2011.hpp" #include using namespace video; using namespace scene; // ============================================================================ class Gaussian3HBlurShader : public TextureShader { public: Gaussian3HBlurShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "gaussian3h.frag"); assignUniforms("pixel"); assignSamplerNames(0, "tex", ST_BILINEAR_CLAMPED_FILTERED); } // Gaussian3HBlurShader // ------------------------------------------------------------------------ void render(const FrameBuffer &auxiliary, float inv_width, float inv_height) { setTextureUnits(auxiliary.getRTT()[0]); drawFullScreenEffect(core::vector2df(inv_width, inv_height)); } // render }; // Gaussian3HBlurShader // ============================================================================ class ComputeShadowBlurVShader : public TextureShader > { public: GLuint m_dest_tu; ComputeShadowBlurVShader() { loadProgram(OBJECT, GL_COMPUTE_SHADER, "blurshadowV.comp"); m_dest_tu = 1; assignUniforms("pixel", "weights"); assignSamplerNames(0, "source", ST_NEARED_CLAMPED_FILTERED); assignTextureUnit(m_dest_tu, "dest"); } // ComputeShadowBlurVShader }; // ComputeShadowBlurVShader // ============================================================================ class Gaussian6VBlurShader : public TextureShader { public: Gaussian6VBlurShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "gaussian6v.frag"); assignUniforms("pixel", "sigma"); assignSamplerNames(0, "tex", ST_BILINEAR_CLAMPED_FILTERED); } // Gaussian6VBlurShader // ------------------------------------------------------------------------ void render(GLuint layer_tex, int width, int height, float sigma_v) { setTextureUnits(layer_tex); drawFullScreenEffect(core::vector2df(1.f / width, 1.f / height), sigma_v); } // render }; // Gaussian6VBlurShader // ============================================================================ class Gaussian3VBlurShader : public TextureShader { public: Gaussian3VBlurShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "gaussian3v.frag"); assignUniforms("pixel"); assignSamplerNames(0, "tex", ST_BILINEAR_CLAMPED_FILTERED); } // Gaussian3VBlurShader // ------------------------------------------------------------------------ void render(const FrameBuffer &in_fbo, float inv_width, float inv_height) { setTextureUnits(in_fbo.getRTT()[0]); drawFullScreenEffect(core::vector2df(inv_width, inv_height)); } // render }; // Gaussian3VBlurShader // ============================================================================ class ComputeGaussian6VBlurShader : public TextureShader > { public: GLuint m_dest_tu; ComputeGaussian6VBlurShader() { loadProgram(OBJECT, GL_COMPUTE_SHADER, "gaussian6v.comp"); m_dest_tu = 1; assignUniforms("pixel", "weights"); assignSamplerNames(0, "source", ST_BILINEAR_CLAMPED_FILTERED); assignTextureUnit(m_dest_tu, "dest"); } // ComputeGaussian6VBlurShader }; // ComputeGaussian6VBlurShader // ============================================================================ class ComputeGaussian6HBlurShader : public TextureShader > { public: GLuint m_dest_tu; ComputeGaussian6HBlurShader() { loadProgram(OBJECT, GL_COMPUTE_SHADER, "gaussian6h.comp"); m_dest_tu = 1; assignUniforms("pixel", "weights"); assignSamplerNames(0, "source", ST_BILINEAR_CLAMPED_FILTERED); assignTextureUnit(m_dest_tu, "dest"); } // ComputeGaussian6HBlurShader }; // ComputeGaussian6HBlurShader // ============================================================================ class ComputeShadowBlurHShader : public TextureShader > { public: GLuint m_dest_tu; ComputeShadowBlurHShader() { loadProgram(OBJECT, GL_COMPUTE_SHADER, "blurshadowH.comp"); m_dest_tu = 1; assignUniforms("pixel", "weights"); assignSamplerNames(0, "source", ST_NEARED_CLAMPED_FILTERED); assignTextureUnit(m_dest_tu, "dest"); } // ComputeShadowBlurHShader }; // ComputeShadowBlurHShader // ============================================================================ class Gaussian6HBlurShader : public TextureShader { public: Gaussian6HBlurShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "gaussian6h.frag"); assignUniforms("pixel", "sigma"); assignSamplerNames(0, "tex", ST_BILINEAR_CLAMPED_FILTERED); } // Gaussian6HBlurShader // ------------------------------------------------------------------------ void render(const FrameBuffer &fb, int width, int height, float sigma_h) { setTextureUnits(fb.getRTT()[0]); drawFullScreenEffect( core::vector2df(1.f / width, 1.f / height), sigma_h); } // renderq }; // Gaussian6HBlurShader // ============================================================================ class Gaussian17TapHShader : public TextureShader { public: Gaussian17TapHShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "bilateralH.frag"); assignUniforms("pixel"); assignSamplerNames(0, "tex", ST_BILINEAR_CLAMPED_FILTERED, 1, "depth", ST_BILINEAR_CLAMPED_FILTERED); } // Gaussian17TapHShader // ------------------------------------------------------------------------ void render(const FrameBuffer &fb, int width, int height) { setTextureUnits(fb.getRTT()[0], irr_driver->getFBO(FBO_LINEAR_DEPTH).getRTT()[0] ); drawFullScreenEffect(core::vector2df(1.0f/width, 1.0f/height)); } // render }; // Gaussian17TapHShader // ============================================================================ class ComputeGaussian17TapHShader : public TextureShader { public: GLuint m_dest_tu; ComputeGaussian17TapHShader() { loadProgram(OBJECT, GL_COMPUTE_SHADER, "bilateralH.comp"); m_dest_tu = 2; assignUniforms("pixel"); assignSamplerNames(0, "source", ST_NEARED_CLAMPED_FILTERED, 1, "depth", ST_NEARED_CLAMPED_FILTERED); assignTextureUnit(m_dest_tu, "dest"); } // ComputeGaussian17TapHShader // ------------------------------------------------------------------------ void render(const FrameBuffer &fb, const FrameBuffer &auxiliary, int width, int height) { use(); glBindSampler(m_dest_tu, 0); setTextureUnits(fb.getRTT()[0], irr_driver->getFBO(FBO_LINEAR_DEPTH).getRTT()[0]); glBindImageTexture(m_dest_tu, auxiliary.getRTT()[0], 0, false, 0, GL_WRITE_ONLY, GL_R16F); setUniforms(core::vector2df(1.0f/width, 1.0f/height)); glDispatchCompute((int)width / 8 + 1, (int)height / 8 + 1, 1); } // render }; // ComputeGaussian17TapHShader // ============================================================================ class Gaussian17TapVShader : public TextureShader { public: Gaussian17TapVShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "bilateralV.frag"); assignUniforms("pixel"); assignSamplerNames(0, "tex", ST_BILINEAR_CLAMPED_FILTERED, 1, "depth", ST_BILINEAR_CLAMPED_FILTERED); } // Gaussian17TapVShader // ------------------------------------------------------------------------ void render(const FrameBuffer &auxiliary, int width, int height) { setTextureUnits(auxiliary.getRTT()[0], irr_driver->getFBO(FBO_LINEAR_DEPTH).getRTT()[0]); drawFullScreenEffect(core::vector2df(1.0f/width, 1.0f/height)); } // render }; // Gaussian17TapVShader // ============================================================================ class ComputeGaussian17TapVShader : public TextureShader { public: GLuint m_dest_tu; ComputeGaussian17TapVShader() { loadProgram(OBJECT, GL_COMPUTE_SHADER, "bilateralV.comp"); m_dest_tu = 2; assignUniforms("pixel"); assignSamplerNames(0, "source", ST_NEARED_CLAMPED_FILTERED, 1, "depth", ST_NEARED_CLAMPED_FILTERED); assignTextureUnit(m_dest_tu, "dest"); } // ComputeGaussian17TapVShader // ------------------------------------------------------------------------ void render(const FrameBuffer &auxiliary, const FrameBuffer &fb, int width, int height) { use(); glBindSampler(m_dest_tu, 0); setTextureUnits(auxiliary.getRTT()[0], irr_driver->getFBO(FBO_LINEAR_DEPTH).getRTT()[0]); glBindImageTexture(m_dest_tu, fb.getRTT()[0], 0, false, 0, GL_WRITE_ONLY, GL_R16F); setUniforms(core::vector2df(1.0f/width, 1.0f/height)); glDispatchCompute((int)fb.getWidth() / 8 + 1, (int)fb.getHeight() / 8 + 1, 1); } // render }; // ComputeGaussian17TapVShader // ============================================================================ class BloomShader : public TextureShader { public: BloomShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "utils/getCIEXYZ.frag", GL_FRAGMENT_SHADER, "utils/getRGBfromCIEXxy.frag", GL_FRAGMENT_SHADER, "bloom.frag"); assignUniforms(); assignSamplerNames(0, "tex", ST_NEAREST_FILTERED); } // BloomShader // ------------------------------------------------------------------------ void render(GLuint in) { BloomShader::getInstance()->setTextureUnits(in); drawFullScreenEffect(); } // render }; // BloomShader static video::ITexture *lensDustTex = 0; // ============================================================================ class BloomBlendShader : public TextureShader { public: BloomBlendShader() { if (!lensDustTex) lensDustTex = irr_driver->getTexture(FileManager::TEXTURE, "gfx_lensDust_a.png"); loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "bloomblend.frag"); assignUniforms(); assignSamplerNames(0, "tex_128", ST_BILINEAR_FILTERED, 1, "tex_256", ST_BILINEAR_FILTERED, 2, "tex_512", ST_BILINEAR_FILTERED, 3, "tex_dust", ST_BILINEAR_FILTERED); } // BloomBlendShader // ------------------------------------------------------------------------ void render() { setTextureUnits(irr_driver->getRenderTargetTexture(RTT_BLOOM_128), irr_driver->getRenderTargetTexture(RTT_BLOOM_256), irr_driver->getRenderTargetTexture(RTT_BLOOM_512), getTextureGLuint(lensDustTex)); drawFullScreenEffect(); } // render }; // BloomBlendShader // ============================================================================ class LensBlendShader : public TextureShader { public: LensBlendShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "lensblend.frag"); assignUniforms(); assignSamplerNames(0, "tex_128", ST_BILINEAR_FILTERED, 1, "tex_256", ST_BILINEAR_FILTERED, 2, "tex_512", ST_BILINEAR_FILTERED); } // LensBlendShader // ------------------------------------------------------------------------ void render() { setTextureUnits(irr_driver->getRenderTargetTexture(RTT_LENS_128), irr_driver->getRenderTargetTexture(RTT_LENS_256), irr_driver->getRenderTargetTexture(RTT_LENS_512)); drawFullScreenEffect(); } // render }; // LensBlendShader // ============================================================================ class ToneMapShader : public TextureShader { public: ToneMapShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "utils/getRGBfromCIEXxy.frag", GL_FRAGMENT_SHADER, "utils/getCIEXYZ.frag", GL_FRAGMENT_SHADER, "tonemap.frag"); assignUniforms("vignette_weight"); assignSamplerNames(0, "text", ST_NEAREST_FILTERED); } // ToneMapShader // ---------------------------------------------------------------------------- void render(const FrameBuffer &fbo, GLuint rtt, float vignette_weight) { fbo.bind(); setTextureUnits(rtt); drawFullScreenEffect(vignette_weight); } // render }; // ToneMapShader // ============================================================================ class DepthOfFieldShader : public TextureShader { public: DepthOfFieldShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "dof.frag"); assignUniforms(); assignSamplerNames(0, "tex", ST_BILINEAR_FILTERED, 1, "dtex", ST_NEAREST_FILTERED); } // DepthOfFieldShader // ------------------------------------------------------------------------ void render(const FrameBuffer &fb, GLuint rtt) { fb.bind(); setTextureUnits(rtt, irr_driver->getDepthStencilTexture()); drawFullScreenEffect(); } // render }; // DepthOfFieldShader // ============================================================================ class IBLShader : public TextureShader { public: IBLShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "utils/decodeNormal.frag", GL_FRAGMENT_SHADER, "utils/getPosFromUVDepth.frag", GL_FRAGMENT_SHADER, "utils/DiffuseIBL.frag", GL_FRAGMENT_SHADER, "utils/SpecularIBL.frag", GL_FRAGMENT_SHADER, "IBL.frag"); assignUniforms(); assignSamplerNames(0, "ntex", ST_NEAREST_FILTERED, 1, "dtex", ST_NEAREST_FILTERED, 2, "probe", ST_TRILINEAR_CUBEMAP); } // IBLShader }; // IBLShader // ============================================================================ class DegradedIBLShader : public TextureShader { public: DegradedIBLShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "utils/decodeNormal.frag", GL_FRAGMENT_SHADER, "utils/getPosFromUVDepth.frag", GL_FRAGMENT_SHADER, "utils/DiffuseIBL.frag", GL_FRAGMENT_SHADER, "utils/SpecularIBL.frag", GL_FRAGMENT_SHADER, "degraded_ibl.frag"); assignUniforms(); assignSamplerNames(0, "ntex", ST_NEAREST_FILTERED); } // DegradedIBLShader }; // DegradedIBLShader // ============================================================================ class RHDebug : public Shader { public: GLuint m_tu_shr, m_tu_shg, m_tu_shb; RHDebug() { loadProgram(OBJECT, GL_VERTEX_SHADER, "rhdebug.vert", GL_FRAGMENT_SHADER, "rhdebug.frag"); assignUniforms("rh_matrix", "extents"); m_tu_shr = 0; m_tu_shg = 1; m_tu_shb = 2; assignTextureUnit(m_tu_shr, "SHR", m_tu_shg, "SHG", m_tu_shb, "SHB"); } // RHDebug }; // RHDebug // ============================================================================ class GlobalIlluminationReconstructionShader : public TextureShader { public: GlobalIlluminationReconstructionShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "utils/decodeNormal.frag", GL_FRAGMENT_SHADER, "utils/getPosFromUVDepth.frag", GL_FRAGMENT_SHADER, "gi.frag"); assignUniforms("rh_matrix", "inv_rh_matrix", "extents"); assignSamplerNames(0, "ntex", ST_NEAREST_FILTERED, 1, "dtex", ST_NEAREST_FILTERED, 2, "SHR", ST_VOLUME_LINEAR_FILTERED, 3, "SHG", ST_VOLUME_LINEAR_FILTERED, 4, "SHB", ST_VOLUME_LINEAR_FILTERED); } // GlobalIlluminationReconstructionShader // ------------------------------------------------------------------------ void render(const core::matrix4 &rh_matrix, const core::vector3df &rh_extend, const FrameBuffer &fb) { core::matrix4 inv_rh_matrix; rh_matrix.getInverse(inv_rh_matrix); glDisable(GL_DEPTH_TEST); setTextureUnits(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture(), fb.getRTT()[0], fb.getRTT()[1], fb.getRTT()[2]); drawFullScreenEffect(rh_matrix, inv_rh_matrix, rh_extend); } // render }; // GlobalIlluminationReconstructionShader // ============================================================================ class PassThroughShader : public TextureShader { public: PassThroughShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "passthrough.frag"); assignUniforms("width", "height"); assignSamplerNames(0, "tex", ST_BILINEAR_FILTERED); } // PassThroughShader // ------------------------------------------------------------------------ void render(GLuint tex, unsigned width, unsigned height) { PassThroughShader::getInstance()->setTextureUnits(tex); drawFullScreenEffect(width, height); } // render }; // PassThroughShader // ============================================================================ class LayerPassThroughShader : public Shader { private: GLuint m_tu_texture; GLuint m_vao; public: LayerPassThroughShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "layertexturequad.frag"); m_tu_texture = 0; assignUniforms("layer"); assignTextureUnit(m_tu_texture, "tex"); m_vao = createVAO(); } // LayerPassThroughShader // ------------------------------------------------------------------------ void bindVertexArray() { glBindVertexArray(m_vao); } // bindVertexArray // ------------------------------------------------------------------------ void activateTexture() { glActiveTexture(GL_TEXTURE0 + m_tu_texture); } // activateTexture }; // LayerPassThroughShader // ============================================================================ class LinearizeDepthShader : public TextureShader { public: LinearizeDepthShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "linearizedepth.frag"); assignUniforms("zn", "zf"); assignSamplerNames(0, "texture", ST_BILINEAR_FILTERED); } // LinearizeDepthShader // ------------------------------------------------------------------------ void render() { setTextureUnits(irr_driver->getDepthStencilTexture()); scene::ICameraSceneNode *c = irr_driver->getSceneManager()->getActiveCamera(); drawFullScreenEffect(c->getNearValue(), c->getFarValue() ); } // render }; // LinearizeDepthShader // ============================================================================ class GlowShader : public TextureShader < GlowShader, 1 > { private: GLuint m_vao; public: GlowShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "glow.frag"); assignUniforms(); assignSamplerNames(0, "tex", ST_BILINEAR_FILTERED); m_vao = createVAO(); } // GlowShader // ------------------------------------------------------------------------ void render(unsigned tex) { use(); glBindVertexArray(m_vao); setTextureUnits(tex); setUniforms(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } // render }; // GlowShader // ============================================================================ class SSAOShader : public TextureShader { public: SSAOShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "utils/decodeNormal.frag", GL_FRAGMENT_SHADER, "utils/getPosFromUVDepth.frag", GL_FRAGMENT_SHADER, "ssao.frag"); assignUniforms("radius", "k", "sigma"); assignSamplerNames(0, "dtex", ST_SEMI_TRILINEAR); } // SSAOShader // ------------------------------------------------------------------------ void render() { setTextureUnits(irr_driver->getRenderTargetTexture(RTT_LINEAR_DEPTH)); glGenerateMipmap(GL_TEXTURE_2D); drawFullScreenEffect(irr_driver->getSSAORadius(), irr_driver->getSSAOK(), irr_driver->getSSAOSigma()); } // render }; // SSAOShader // ============================================================================ class MotionBlurShader : public TextureShader { public: MotionBlurShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "utils/getPosFromUVDepth.frag", GL_FRAGMENT_SHADER, "motion_blur.frag"); assignUniforms("previous_viewproj", "center", "boost_amount", "mask_radius"); assignSamplerNames(0, "color_buffer", ST_BILINEAR_CLAMPED_FILTERED, 1, "dtex", ST_NEAREST_FILTERED); } // MotionBlurShader // ------------------------------------------------------------------------ void render(const FrameBuffer &fb, float boost_time) { setTextureUnits(fb.getRTT()[0], irr_driver->getDepthStencilTexture()); Camera *cam = Camera::getActiveCamera(); // Todo : use a previousPVMatrix per cam, not global drawFullScreenEffect(cam->getPreviousPVMatrix(), core::vector2df(0.5, 0.5), boost_time, // Todo : should be framerate dependent= 0.15f); } // render }; // MotionBlurShader // ============================================================================ class GodFadeShader : public TextureShader { public: GodFadeShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "godfade.frag"); assignUniforms("col"); assignSamplerNames(0, "tex", ST_BILINEAR_FILTERED); } // GodFadeShader // ---------------------------------------------------------------------------- void render(GLuint tex, const SColor &col) { setTextureUnits(tex); drawFullScreenEffect(col); } // render }; // GodFadeShader // ============================================================================ class GodRayShader : public TextureShader { public: GodRayShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "godray.frag"); assignUniforms("sunpos"); assignSamplerNames(0, "tex", ST_BILINEAR_FILTERED); } // GodRayShader // ---------------------------------------------------------------------------- void render(GLuint tex, const core::vector2df &sunpos) { setTextureUnits(tex); drawFullScreenEffect(sunpos); } // render }; // GodRayShader // ============================================================================ class MLAAColorEdgeDetectionSHader : public TextureShader { public: MLAAColorEdgeDetectionSHader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "mlaa_color1.frag"); assignUniforms("PIXEL_SIZE"); assignSamplerNames(0, "colorMapG", ST_NEAREST_FILTERED); } // MLAAColorEdgeDetectionSHader // ------------------------------------------------------------------------ void render(const core::vector2df &pixel_size) { use(); setTextureUnits(irr_driver->getRenderTargetTexture(RTT_MLAA_COLORS)); drawFullScreenEffect(pixel_size); } // render }; // MLAAColorEdgeDetectionSHader // ============================================================================ class MLAABlendWeightSHader : public TextureShader { public: MLAABlendWeightSHader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "mlaa_blend2.frag"); assignUniforms("PIXEL_SIZE"); assignSamplerNames(0, "edgesMap", ST_BILINEAR_FILTERED, 1, "areaMap", ST_NEAREST_FILTERED); } // MLAABlendWeightSHader // ------------------------------------------------------------------------ void render(video::ITexture *area_map, const core::vector2df &pixel_size) { use(); setTextureUnits(irr_driver->getRenderTargetTexture(RTT_MLAA_TMP), getTextureGLuint(area_map)); drawFullScreenEffect(pixel_size); } // render }; // MLAABlendWeightSHader // ============================================================================ class MLAAGatherSHader : public TextureShader { public: MLAAGatherSHader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "mlaa_neigh3.frag"); assignUniforms("PIXEL_SIZE"); assignSamplerNames(0, "blendMap", ST_NEAREST_FILTERED, 1, "colorMap", ST_NEAREST_FILTERED); } // MLAAGatherSHader // ------------------------------------------------------------------------ void render(const core::vector2df &pixel_size) { use(); setTextureUnits(irr_driver->getRenderTargetTexture(RTT_MLAA_BLEND), irr_driver->getRenderTargetTexture(RTT_MLAA_TMP)); drawFullScreenEffect(pixel_size); } // render }; // MLAAGatherSHader // ============================================================================ class SunLightShader : public TextureShader { public: SunLightShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "utils/decodeNormal.frag", GL_FRAGMENT_SHADER, "utils/SpecularBRDF.frag", GL_FRAGMENT_SHADER, "utils/DiffuseBRDF.frag", GL_FRAGMENT_SHADER, "utils/getPosFromUVDepth.frag", GL_FRAGMENT_SHADER, "utils/SunMRP.frag", GL_FRAGMENT_SHADER, "sunlight.frag"); assignSamplerNames(0, "ntex", ST_NEAREST_FILTERED, 1, "dtex", ST_NEAREST_FILTERED); assignUniforms("direction", "col"); } // SunLightShader // ------------------------------------------------------------------------ void render(const core::vector3df &direction, const video::SColorf &col) { glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); setTextureUnits(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture()); drawFullScreenEffect(direction, col); } // render }; // SunLightShader // ============================================================================ class LightningShader : public TextureShader { public: LightningShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "screenquad.vert", GL_FRAGMENT_SHADER, "lightning.frag"); assignUniforms("intensity"); } // LightningShader // ------------------------------------------------------------------------ void render(core::vector3df intensity) { drawFullScreenEffect(intensity); } // render }; // LightningShader // ============================================================================ PostProcessing::PostProcessing(IVideoDriver* video_driver) { // Initialization m_material.Wireframe = false; m_material.Lighting = false; m_material.ZWriteEnable = false; m_material.ZBuffer = ECFN_ALWAYS; m_material.setFlag(EMF_TRILINEAR_FILTER, true); for (u32 i = 0; i < MATERIAL_MAX_TEXTURES; ++i) { m_material.TextureLayer[i].TextureWrapU = m_material.TextureLayer[i].TextureWrapV = ETC_CLAMP_TO_EDGE; } // Load the MLAA area map io::IReadFile *areamap = irr_driver->getDevice()->getFileSystem()-> createMemoryReadFile((void *) AreaMap33, sizeof(AreaMap33), "AreaMap33", false); if (!areamap) { Log::fatal("postprocessing", "Failed to load the areamap"); return; } m_areamap = irr_driver->getVideoDriver()->getTexture(areamap); areamap->drop(); } // PostProcessing // ---------------------------------------------------------------------------- PostProcessing::~PostProcessing() { // TODO: do we have to delete/drop anything? } // ~PostProcessing // ---------------------------------------------------------------------------- /** Initialises post processing at the (re-)start of a race. This sets up * the vertices, normals and texture coordinates for each */ void PostProcessing::reset() { const u32 n = Camera::getNumCameras(); m_boost_time.resize(n); m_vertices.resize(n); m_center.resize(n); m_direction.resize(n); MotionBlurProvider * const cb = (MotionBlurProvider *) Shaders::getCallback(ES_MOTIONBLUR); for(unsigned int i=0; igetViewport(); // Map viewport to [-1,1] x [-1,1]. First define the coordinates // left, right, top, bottom: float right = vp.LowerRightCorner.X < (int)irr_driver->getActualScreenSize().Width ? 0.0f : 1.0f; float left = vp.UpperLeftCorner.X > 0.0f ? 0.0f : -1.0f; float top = vp.UpperLeftCorner.Y > 0.0f ? 0.0f : 1.0f; float bottom = vp.LowerRightCorner.Y < (int)irr_driver->getActualScreenSize().Height ? 0.0f : -1.0f; // Use left etc to define 4 vertices on which the rendered screen // will be displayed: m_vertices[i].v0.Pos = core::vector3df(left, bottom, 0); m_vertices[i].v1.Pos = core::vector3df(left, top, 0); m_vertices[i].v2.Pos = core::vector3df(right, top, 0); m_vertices[i].v3.Pos = core::vector3df(right, bottom, 0); // Define the texture coordinates of each vertex, which must // be in [0,1]x[0,1] m_vertices[i].v0.TCoords = core::vector2df(left ==-1.0f ? 0.0f : 0.5f, bottom==-1.0f ? 0.0f : 0.5f); m_vertices[i].v1.TCoords = core::vector2df(left ==-1.0f ? 0.0f : 0.5f, top == 1.0f ? 1.0f : 0.5f); m_vertices[i].v2.TCoords = core::vector2df(right == 0.0f ? 0.5f : 1.0f, top == 1.0f ? 1.0f : 0.5f); m_vertices[i].v3.TCoords = core::vector2df(right == 0.0f ? 0.5f : 1.0f, bottom==-1.0f ? 0.0f : 0.5f); // Set normal and color: core::vector3df normal(0,0,1); m_vertices[i].v0.Normal = m_vertices[i].v1.Normal = m_vertices[i].v2.Normal = m_vertices[i].v3.Normal = normal; SColor white(0xFF, 0xFF, 0xFF, 0xFF); m_vertices[i].v0.Color = m_vertices[i].v1.Color = m_vertices[i].v2.Color = m_vertices[i].v3.Color = white; m_center[i].X=(m_vertices[i].v0.TCoords.X +m_vertices[i].v2.TCoords.X) * 0.5f; // Center is around 20 percent from bottom of screen: const float tex_height = m_vertices[i].v1.TCoords.Y - m_vertices[i].v0.TCoords.Y; m_direction[i].X = m_center[i].X; m_direction[i].Y = m_vertices[i].v0.TCoords.Y + 0.7f*tex_height; setMotionBlurCenterY(i, 0.2f); cb->setDirection(i, m_direction[i].X, m_direction[i].Y); cb->setMaxHeight(i, m_vertices[i].v1.TCoords.Y); } // for i setCenter(num, m_center[num].X, m_center[num].Y); } // setMotionBlurCenterY // ---------------------------------------------------------------------------- /** Setup some PP data. */ void PostProcessing::begin() { m_any_boost = false; for (u32 i = 0; i < m_boost_time.size(); i++) m_any_boost |= m_boost_time[i] > 0.01f; } // begin // ---------------------------------------------------------------------------- /** Set the boost amount according to the speed of the camera */ void PostProcessing::giveBoost(unsigned int camera_index) { if (CVS->isGLSL()) { m_boost_time[camera_index] = 0.75f; MotionBlurProvider * const cb = (MotionBlurProvider *)Shaders::getCallback(ES_MOTIONBLUR); cb->setBoostTime(camera_index, m_boost_time[camera_index]); } } // giveBoost // ---------------------------------------------------------------------------- /** Updates the boost times for all cameras, called once per frame. * \param dt Time step size. */ void PostProcessing::update(float dt) { if (!CVS->isGLSL()) return; MotionBlurProvider* const cb = (MotionBlurProvider*) Shaders::getCallback(ES_MOTIONBLUR); if (cb == NULL) return; for (unsigned int i=0; i 0.0f) { m_boost_time[i] -= dt; if (m_boost_time[i] < 0.0f) m_boost_time[i] = 0.0f; } cb->setBoostTime(i, m_boost_time[i]); } } // update // ---------------------------------------------------------------------------- static void renderBloom(GLuint in) { BloomShader::getInstance()->render(in); } // renderBloom // ---------------------------------------------------------------------------- void PostProcessing::renderEnvMap(GLuint skybox) { glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); glBlendFunc(GL_ONE, GL_ONE); if (UserConfigParams::m_degraded_IBL) { DegradedIBLShader::getInstance()->use(); glBindVertexArray(SharedGPUObjects::getFullScreenQuadVAO()); DegradedIBLShader::getInstance() ->setTextureUnits(irr_driver ->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH)); DegradedIBLShader::getInstance()->setUniforms(); } else { IBLShader::getInstance()->use(); glBindVertexArray(SharedGPUObjects::getFullScreenQuadVAO()); IBLShader::getInstance()->setTextureUnits( irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture(), skybox); IBLShader::getInstance()->setUniforms(); } glDrawArrays(GL_TRIANGLES, 0, 3); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); } // renderEnvMap // ---------------------------------------------------------------------------- void PostProcessing::renderRHDebug(unsigned SHR, unsigned SHG, unsigned SHB, const core::matrix4 &rh_matrix, const core::vector3df &rh_extend) { glEnable(GL_PROGRAM_POINT_SIZE); RHDebug::getInstance()->use(); glActiveTexture(GL_TEXTURE0 + RHDebug::getInstance()->m_tu_shr); glBindTexture(GL_TEXTURE_3D, SHR); glActiveTexture(GL_TEXTURE0 + RHDebug::getInstance()->m_tu_shg); glBindTexture(GL_TEXTURE_3D, SHG); glActiveTexture(GL_TEXTURE0 + RHDebug::getInstance()->m_tu_shb); glBindTexture(GL_TEXTURE_3D, SHB); RHDebug::getInstance()->setUniforms(rh_matrix, rh_extend); glDrawArrays(GL_POINTS, 0, 32 * 16 * 32); glDisable(GL_PROGRAM_POINT_SIZE); } // renderRHDebug // ---------------------------------------------------------------------------- void PostProcessing::renderGI(const core::matrix4 &rh_matrix, const core::vector3df &rh_extend, const FrameBuffer &fb) { GlobalIlluminationReconstructionShader::getInstance()->render(rh_matrix, rh_extend, fb); } // renderGI // ---------------------------------------------------------------------------- void PostProcessing::renderSunlight(const core::vector3df &direction, const video::SColorf &col) { SunLightShader::getInstance()->render(direction, col); } // renderSunlight // ---------------------------------------------------------------------------- static std::vector getGaussianWeight(float sigma, size_t count) { float g0, g1, g2, total; std::vector weights; g0 = 1.f / (sqrtf(2.f * 3.14f) * sigma); g1 = exp(-.5f / (sigma * sigma)); g2 = g1 * g1; total = g0; for (unsigned i = 0; i < count; i++) { weights.push_back(g0); g0 *= g1; g1 *= g2; total += 2 * g0; } for (float &weight : weights) weight /= total; return weights; } // getGaussianWeight // ---------------------------------------------------------------------------- void PostProcessing::renderGaussian3Blur(const FrameBuffer &in_fbo, const FrameBuffer &auxiliary) { assert(in_fbo.getWidth() == auxiliary.getWidth() && in_fbo.getHeight() == auxiliary.getHeight()); float inv_width = 1.0f / in_fbo.getWidth(); float inv_height = 1.0f / in_fbo.getHeight(); { auxiliary.bind(); Gaussian3VBlurShader::getInstance()->render(in_fbo, inv_width, inv_height); } { in_fbo.bind(); Gaussian3HBlurShader::getInstance()->render(auxiliary, inv_width, inv_height); } } // renderGaussian3Blur // ---------------------------------------------------------------------------- void PostProcessing::renderGaussian6BlurLayer(FrameBuffer &in_fbo, size_t layer, float sigma_h, float sigma_v) { GLuint layer_tex; glGenTextures(1, &layer_tex); glTextureView(layer_tex, GL_TEXTURE_2D, in_fbo.getRTT()[0], GL_R32F, 0, 1, layer, 1); if (!CVS->supportsComputeShadersFiltering()) { // Used as temp irr_driver->getFBO(FBO_SCALAR_1024).bind(); Gaussian6VBlurShader::getInstance() ->render(layer_tex, UserConfigParams::m_shadows_resolution, UserConfigParams::m_shadows_resolution, sigma_v); in_fbo.bindLayer(layer); Gaussian6HBlurShader::getInstance() ->render(irr_driver->getFBO(FBO_SCALAR_1024), UserConfigParams::m_shadows_resolution, UserConfigParams::m_shadows_resolution, sigma_h); } else { const std::vector &weightsV = getGaussianWeight(sigma_v, 7); glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT); ComputeShadowBlurVShader::getInstance()->use(); ComputeShadowBlurVShader::getInstance()->setTextureUnits(layer_tex); glBindSampler(ComputeShadowBlurVShader::getInstance()->m_dest_tu, 0); glBindImageTexture(ComputeShadowBlurVShader::getInstance()->m_dest_tu, irr_driver->getFBO(FBO_SCALAR_1024).getRTT()[0], 0, false, 0, GL_WRITE_ONLY, GL_R32F); ComputeShadowBlurVShader::getInstance()->setUniforms (core::vector2df(1.f / UserConfigParams::m_shadows_resolution, 1.f / UserConfigParams::m_shadows_resolution), weightsV); glDispatchCompute((int)UserConfigParams::m_shadows_resolution / 8 + 1, (int)UserConfigParams::m_shadows_resolution / 8 + 1, 1); const std::vector &weightsH = getGaussianWeight(sigma_h, 7); glMemoryBarrier( GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); ComputeShadowBlurHShader::getInstance()->use(); ComputeShadowBlurHShader::getInstance() ->setTextureUnits(irr_driver->getFBO(FBO_SCALAR_1024).getRTT()[0]); glBindSampler(ComputeShadowBlurHShader::getInstance()->m_dest_tu, 0); glBindImageTexture(ComputeShadowBlurHShader::getInstance()->m_dest_tu, layer_tex, 0, false, 0, GL_WRITE_ONLY, GL_R32F); ComputeShadowBlurHShader::getInstance()->setUniforms (core::vector2df(1.f / UserConfigParams::m_shadows_resolution, 1.f / UserConfigParams::m_shadows_resolution), weightsH); glDispatchCompute((int)UserConfigParams::m_shadows_resolution / 8 + 1, (int)UserConfigParams::m_shadows_resolution / 8 + 1, 1); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); } glDeleteTextures(1, &layer_tex); } // renderGaussian6BlurLayer // ---------------------------------------------------------------------------- void PostProcessing::renderGaussian6Blur(const FrameBuffer &in_fbo, const FrameBuffer &auxiliary, float sigma_v, float sigma_h) { assert(in_fbo.getWidth() == auxiliary.getWidth() && in_fbo.getHeight() == auxiliary.getHeight()); float inv_width = 1.0f / in_fbo.getWidth(); float inv_height = 1.0f / in_fbo.getHeight(); if (!CVS->supportsComputeShadersFiltering()) { auxiliary.bind(); Gaussian6VBlurShader::getInstance() ->render(in_fbo.getRTT()[0], in_fbo.getWidth(), in_fbo.getWidth(), sigma_v); in_fbo.bind(); Gaussian6HBlurShader::getInstance()->setTextureUnits(auxiliary.getRTT()[0]); Gaussian6HBlurShader::getInstance()->render(auxiliary, in_fbo.getWidth(), in_fbo.getHeight(), sigma_h); } else { const std::vector &weightsV = getGaussianWeight(sigma_v, 7); glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT); ComputeGaussian6VBlurShader::getInstance()->use(); ComputeGaussian6VBlurShader::getInstance() ->setTextureUnits(in_fbo.getRTT()[0]); glBindSampler(ComputeGaussian6VBlurShader::getInstance()->m_dest_tu, 0); glBindImageTexture(ComputeGaussian6VBlurShader::getInstance()->m_dest_tu, auxiliary.getRTT()[0], 0, false, 0, GL_WRITE_ONLY, GL_RGBA16F); ComputeGaussian6VBlurShader::getInstance() ->setUniforms(core::vector2df(inv_width, inv_height), weightsV); glDispatchCompute((int)in_fbo.getWidth() / 8 + 1, (int)in_fbo.getHeight() / 8 + 1, 1); const std::vector &weightsH = getGaussianWeight(sigma_h, 7); glMemoryBarrier( GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); ComputeGaussian6HBlurShader::getInstance()->use(); ComputeGaussian6HBlurShader::getInstance() ->setTextureUnits(auxiliary.getRTT()[0]); glBindSampler(ComputeGaussian6HBlurShader::getInstance()->m_dest_tu, 0); glBindImageTexture(ComputeGaussian6HBlurShader::getInstance()->m_dest_tu, in_fbo.getRTT()[0], 0, false, 0, GL_WRITE_ONLY, GL_RGBA16F); ComputeGaussian6HBlurShader::getInstance() ->setUniforms(core::vector2df(inv_width, inv_height), weightsH); glDispatchCompute((int)in_fbo.getWidth() / 8 + 1, (int)in_fbo.getHeight() / 8 + 1, 1); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); } } // renderGaussian6Blur // ---------------------------------------------------------------------------- void PostProcessing::renderHorizontalBlur(const FrameBuffer &in_fbo, const FrameBuffer &auxiliary) { assert(in_fbo.getWidth() == auxiliary.getWidth() && in_fbo.getHeight() == auxiliary.getHeight()); auxiliary.bind(); Gaussian6HBlurShader::getInstance()->render(in_fbo, in_fbo.getWidth(), in_fbo.getHeight(), 2.0f ); in_fbo.bind(); Gaussian6HBlurShader::getInstance()->render(auxiliary, in_fbo.getWidth(), in_fbo.getHeight(), 2.0f); } // renderHorizontalBlur // ---------------------------------------------------------------------------- void PostProcessing::renderGaussian17TapBlur(const FrameBuffer &in_fbo, const FrameBuffer &auxiliary) { assert(in_fbo.getWidth() == auxiliary.getWidth() && in_fbo.getHeight() == auxiliary.getHeight()); if (CVS->supportsComputeShadersFiltering()) glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT); { if (!CVS->supportsComputeShadersFiltering()) { auxiliary.bind(); Gaussian17TapHShader::getInstance()->render(in_fbo, in_fbo.getWidth(), in_fbo.getHeight()); } else { ComputeGaussian17TapHShader::getInstance()->render(in_fbo, auxiliary, in_fbo.getWidth(), in_fbo.getHeight()); } } if (CVS->supportsComputeShadersFiltering()) glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); { if (!CVS->supportsComputeShadersFiltering()) { in_fbo.bind(); Gaussian17TapVShader::getInstance()->render(auxiliary, in_fbo.getWidth(), in_fbo.getHeight()); } else { ComputeGaussian17TapVShader::getInstance()->render(auxiliary, in_fbo, in_fbo.getWidth(), in_fbo.getHeight()); } } if (CVS->supportsComputeShadersFiltering()) glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT); } // renderGaussian17TapBlur // ---------------------------------------------------------------------------- void PostProcessing::renderPassThrough(GLuint tex, unsigned width, unsigned height) { PassThroughShader::getInstance()->render(tex, width, height); } // renderPassThrough // ---------------------------------------------------------------------------- void PostProcessing::renderTextureLayer(unsigned tex, unsigned layer) { LayerPassThroughShader::getInstance()->use(); LayerPassThroughShader::getInstance()->bindVertexArray(); LayerPassThroughShader::getInstance()->activateTexture(); glBindTexture(GL_TEXTURE_2D_ARRAY, tex); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); LayerPassThroughShader::getInstance()->setUniforms(layer); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } // renderTextureLayer // ---------------------------------------------------------------------------- void PostProcessing::renderGlow(unsigned tex) { GlowShader::getInstance()->render(tex); } // renderGlow // ---------------------------------------------------------------------------- void PostProcessing::renderSSAO() { glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); // Generate linear depth buffer irr_driver->getFBO(FBO_LINEAR_DEPTH).bind(); LinearizeDepthShader::getInstance()->render(); irr_driver->getFBO(FBO_SSAO).bind(); SSAOShader::getInstance()->render(); } // renderSSAO // ---------------------------------------------------------------------------- void PostProcessing::renderMotionBlur(unsigned , const FrameBuffer &in_fbo, FrameBuffer &out_fbo) { MotionBlurProvider * const cb = (MotionBlurProvider *)Shaders::getCallback(ES_MOTIONBLUR); Camera *cam = Camera::getActiveCamera(); unsigned camID = cam->getIndex(); scene::ICameraSceneNode * const camnode = cam->getCameraSceneNode(); // Calculate the kart's Y position on screen if (cam->getKart()) { const core::vector3df pos = cam->getKart()->getNode()->getPosition(); float ndc[4]; core::matrix4 trans = camnode->getProjectionMatrix(); trans *= camnode->getViewMatrix(); trans.transformVect(ndc, pos); const float karty = (ndc[1] / ndc[3]) * 0.5f + 0.5f; setMotionBlurCenterY(camID, karty); } else setMotionBlurCenterY(camID, 0.5f); out_fbo.bind(); glClear(GL_COLOR_BUFFER_BIT); float boost_time = cb->getBoostTime(cam->getIndex()) * 10; MotionBlurShader::getInstance()->render(in_fbo, boost_time); } // renderMotionBlur // ---------------------------------------------------------------------------- static void renderDoF(const FrameBuffer &fbo, GLuint rtt) { DepthOfFieldShader::getInstance()->render(fbo, rtt); } // renderDoF // ---------------------------------------------------------------------------- void PostProcessing::applyMLAA() { const core::vector2df &PIXEL_SIZE = core::vector2df(1.0f / UserConfigParams::m_width, 1.0f / UserConfigParams::m_height); irr_driver->getFBO(FBO_MLAA_TMP).bind(); glEnable(GL_STENCIL_TEST); glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glStencilFunc(GL_ALWAYS, 1, ~0); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // Pass 1: color edge detection MLAAColorEdgeDetectionSHader::getInstance()->render(PIXEL_SIZE); glStencilFunc(GL_EQUAL, 1, ~0); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // Pass 2: blend weights irr_driver->getFBO(FBO_MLAA_BLEND).bind(); glClear(GL_COLOR_BUFFER_BIT); MLAABlendWeightSHader::getInstance()->render(m_areamap, PIXEL_SIZE); // Blit in to tmp1 FrameBuffer::Blit(irr_driver->getFBO(FBO_MLAA_COLORS), irr_driver->getFBO(FBO_MLAA_TMP)); // Pass 3: gather irr_driver->getFBO(FBO_MLAA_COLORS).bind(); MLAAGatherSHader::getInstance()->render(PIXEL_SIZE); // Done. glDisable(GL_STENCIL_TEST); } // applyMLAA // ---------------------------------------------------------------------------- void PostProcessing::renderLightning(core::vector3df intensity) { glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); LightningShader::getInstance()->render(intensity); glDisable(GL_BLEND); } // ---------------------------------------------------------------------------- /** Render the post-processed scene */ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, bool isRace) { FrameBuffer *in_fbo = &irr_driver->getFBO(FBO_COLORS); FrameBuffer *out_fbo = &irr_driver->getFBO(FBO_TMP1_WITH_DS); // Each effect uses these as named, and sets them up for the next effect. // This allows chaining effects where some may be disabled. // As the original color shouldn't be touched, the first effect // can't be disabled. glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); World *world = World::getWorld(); Physics *physics = world ? world->getPhysics() : NULL; if (isRace && UserConfigParams::m_dof && (physics == NULL || !physics->isDebug())) { PROFILER_PUSH_CPU_MARKER("- DoF", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_DOF)); renderDoF(*out_fbo, in_fbo->getRTT()[0]); std::swap(in_fbo, out_fbo); PROFILER_POP_CPU_MARKER(); } { PROFILER_PUSH_CPU_MARKER("- Godrays", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_GODRAYS)); bool hasgodrays = false; if (World::getWorld() != NULL) hasgodrays = World::getWorld()->getTrack()->hasGodRays(); if (isRace && UserConfigParams::m_light_shaft && hasgodrays) { Track* track = World::getWorld()->getTrack(); glEnable(GL_DEPTH_TEST); // Grab the sky out_fbo->bind(); glClear(GL_COLOR_BUFFER_BIT); // irr_driver->renderSkybox(camnode); // Set the sun's color const SColor col = track->getGodRaysColor(); // The sun interposer STKMeshSceneNode *sun = irr_driver->getSunInterposer(); sun->setGlowColors(col); sun->setPosition(track->getGodRaysPosition()); sun->updateAbsolutePosition(); irr_driver->setPhase(GLOW_PASS); sun->render(); glDisable(GL_DEPTH_TEST); // Fade to quarter irr_driver->getFBO(FBO_QUARTER1).bind(); glViewport(0, 0, irr_driver->getActualScreenSize().Width / 4, irr_driver->getActualScreenSize().Height / 4); GodFadeShader::getInstance()->render(out_fbo->getRTT()[0], col); // Blur renderGaussian3Blur(irr_driver->getFBO(FBO_QUARTER1), irr_driver->getFBO(FBO_QUARTER2)); // Calculate the sun's position in texcoords const core::vector3df pos = track->getGodRaysPosition(); float ndc[4]; core::matrix4 trans = camnode->getProjectionMatrix(); trans *= camnode->getViewMatrix(); trans.transformVect(ndc, pos); const float texh = m_vertices[0].v1.TCoords.Y - m_vertices[0].v0.TCoords.Y; const float texw = m_vertices[0].v3.TCoords.X - m_vertices[0].v0.TCoords.X; const float sunx = ((ndc[0] / ndc[3]) * 0.5f + 0.5f) * texw; const float suny = ((ndc[1] / ndc[3]) * 0.5f + 0.5f) * texh; // Rays please irr_driver->getFBO(FBO_QUARTER2).bind(); GodRayShader::getInstance() ->render(irr_driver->getRenderTargetTexture(RTT_QUARTER1), core::vector2df(sunx, suny) ); // Blur renderGaussian3Blur(irr_driver->getFBO(FBO_QUARTER2), irr_driver->getFBO(FBO_QUARTER1)); // Blend glEnable(GL_BLEND); glBlendColor(0., 0., 0., track->getGodRaysOpacity()); glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE); glBlendEquation(GL_FUNC_ADD); in_fbo->bind(); renderPassThrough(irr_driver->getRenderTargetTexture(RTT_QUARTER2), in_fbo->getWidth(), in_fbo->getHeight()); glDisable(GL_BLEND); } PROFILER_POP_CPU_MARKER(); } // Simulate camera defects from there { PROFILER_PUSH_CPU_MARKER("- Bloom", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_BLOOM)); if (isRace && UserConfigParams::m_bloom && (physics == NULL || !physics->isDebug())) { glClear(GL_STENCIL_BUFFER_BIT); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); FrameBuffer::Blit(*in_fbo, irr_driver->getFBO(FBO_BLOOM_1024), GL_COLOR_BUFFER_BIT, GL_LINEAR); irr_driver->getFBO(FBO_BLOOM_512).bind(); renderBloom(irr_driver->getRenderTargetTexture(RTT_BLOOM_1024)); // Downsample FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_BLOOM_256), GL_COLOR_BUFFER_BIT, GL_LINEAR); FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_BLOOM_128), GL_COLOR_BUFFER_BIT, GL_LINEAR); // Copy for lens flare FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_LENS_512), GL_COLOR_BUFFER_BIT, GL_LINEAR); FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_LENS_256), GL_COLOR_BUFFER_BIT, GL_LINEAR); FrameBuffer::Blit(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_LENS_128), GL_COLOR_BUFFER_BIT, GL_LINEAR); // Blur renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_512), irr_driver->getFBO(FBO_TMP_512), 1., 1.); renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_256), irr_driver->getFBO(FBO_TMP_256), 1., 1.); renderGaussian6Blur(irr_driver->getFBO(FBO_BLOOM_128), irr_driver->getFBO(FBO_TMP_128), 1., 1.); renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_512), irr_driver->getFBO(FBO_TMP_512)); renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_256), irr_driver->getFBO(FBO_TMP_256)); renderHorizontalBlur(irr_driver->getFBO(FBO_LENS_128), irr_driver->getFBO(FBO_TMP_128)); // Additively blend on top of tmp1 in_fbo->bind(); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); glBlendEquation(GL_FUNC_ADD); BloomBlendShader::getInstance()->render(); LensBlendShader::getInstance()->render(); glDisable(GL_BLEND); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } // end if bloom PROFILER_POP_CPU_MARKER(); } //computeLogLuminance(in_rtt); { PROFILER_PUSH_CPU_MARKER("- Tonemap", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_TONEMAP)); // only enable vignette during race ToneMapShader::getInstance()->render(*out_fbo, in_fbo->getRTT()[0], isRace ? 1.0f : 0.0f); std::swap(in_fbo, out_fbo); PROFILER_POP_CPU_MARKER(); } { PROFILER_PUSH_CPU_MARKER("- Motion blur", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_MOTIONBLUR)); MotionBlurProvider * const cb = (MotionBlurProvider *)Shaders::getCallback(ES_MOTIONBLUR); if (isRace && UserConfigParams::m_motionblur && World::getWorld() && cb->getBoostTime(Camera::getActiveCamera()->getIndex()) > 0.) // motion blur { renderMotionBlur(0, *in_fbo, *out_fbo); std::swap(in_fbo, out_fbo); } PROFILER_POP_CPU_MARKER(); } // Handle lightning rendering { PROFILER_PUSH_CPU_MARKER("- Lightning", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_LIGHTNING)); if (World::getWorld() != NULL) { Weather* m_weather = World::getWorld()->getWeather(); if (m_weather != NULL && m_weather->shouldLightning()) { renderLightning(m_weather->getIntensity()); } } PROFILER_POP_CPU_MARKER(); } // Workaround a bug with srgb fbo on sandy bridge windows if (!CVS->isARBUniformBufferObjectUsable()) return in_fbo; glEnable(GL_FRAMEBUFFER_SRGB); irr_driver->getFBO(FBO_MLAA_COLORS).bind(); renderPassThrough(in_fbo->getRTT()[0], irr_driver->getFBO(FBO_MLAA_COLORS).getWidth(), irr_driver->getFBO(FBO_MLAA_COLORS).getHeight()); out_fbo = &irr_driver->getFBO(FBO_MLAA_COLORS); if (UserConfigParams::m_mlaa) // MLAA. Must be the last pp filter. { PROFILER_PUSH_CPU_MARKER("- MLAA", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_MLAA)); applyMLAA(); PROFILER_POP_CPU_MARKER(); } glDisable(GL_FRAMEBUFFER_SRGB); return out_fbo; } // render