From 69f52cc89800a38406155d9b040642c0fb436b69 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 27 Jul 2016 21:50:30 +0200 Subject: [PATCH] Added bloom shaders --- src/CMakeLists.txt | 2 + src/gl/renderer/gl_renderbuffers.cpp | 74 +++++- src/gl/renderer/gl_renderbuffers.h | 16 ++ src/gl/renderer/gl_renderer.cpp | 8 + src/gl/renderer/gl_renderer.h | 7 + src/gl/scene/gl_scene.cpp | 128 ++++++++++ src/gl/shaders/gl_bloomshader.cpp | 79 ++++++ src/gl/shaders/gl_bloomshader.h | 29 +++ src/gl/shaders/gl_blurshader.cpp | 265 +++++++++++++++++++++ src/gl/shaders/gl_blurshader.h | 39 +++ src/gl/shaders/gl_shaderprogram.cpp | 14 +- src/gl/shaders/gl_shaderprogram.h | 1 + wadsrc/static/shaders/glsl/bloomcombine.fp | 12 + wadsrc/static/shaders/glsl/bloomcombine.vp | 11 + wadsrc/static/shaders/glsl/bloomextract.fp | 14 ++ wadsrc/static/shaders/glsl/bloomextract.vp | 11 + 16 files changed, 696 insertions(+), 14 deletions(-) create mode 100644 src/gl/shaders/gl_bloomshader.cpp create mode 100644 src/gl/shaders/gl_bloomshader.h create mode 100644 src/gl/shaders/gl_blurshader.cpp create mode 100644 src/gl/shaders/gl_blurshader.h create mode 100644 wadsrc/static/shaders/glsl/bloomcombine.fp create mode 100644 wadsrc/static/shaders/glsl/bloomcombine.vp create mode 100644 wadsrc/static/shaders/glsl/bloomextract.fp create mode 100644 wadsrc/static/shaders/glsl/bloomextract.vp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cad329962..4e7ae8cbb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1106,6 +1106,8 @@ set( FASTMATH_SOURCES gl/shaders/gl_texshader.cpp gl/shaders/gl_shaderprogram.cpp gl/shaders/gl_presentshader.cpp + gl/shaders/gl_bloomshader.cpp + gl/shaders/gl_blurshader.cpp gl/system/gl_interface.cpp gl/system/gl_framebuffer.cpp gl/system/gl_menu.cpp diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index 26377553f..52182c9da 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -72,12 +72,37 @@ FGLRenderBuffers::FGLRenderBuffers() FGLRenderBuffers::~FGLRenderBuffers() { + Clear(); +} + +void FGLRenderBuffers::Clear() +{ + for (int i = 0; i < NumBloomLevels; i++) + { + auto &level = BloomLevels[i]; + if (level.HFramebuffer != 0) + glDeleteFramebuffers(1, &level.HFramebuffer); + if (level.VFramebuffer != 0) + glDeleteFramebuffers(1, &level.VFramebuffer); + if (level.HTexture != 0) + glDeleteTextures(1, &level.HTexture); + if (level.VTexture != 0) + glDeleteTextures(1, &level.VTexture); + level = FGLBloomTextureLevel(); + } + if (mSceneFB != 0) glDeleteFramebuffers(1, &mSceneFB); if (mSceneTexture != 0) glDeleteTextures(1, &mSceneTexture); if (mSceneDepthStencil != 0) glDeleteRenderbuffers(1, &mSceneDepthStencil); + + mSceneFB = 0; + mSceneTexture = 0; + mSceneDepthStencil = 0; + mWidth = 0; + mHeight = 0; } //========================================================================== @@ -92,12 +117,9 @@ void FGLRenderBuffers::Setup(int width, int height) if (width <= mWidth && height <= mHeight) return; - if (mSceneFB != 0) - glDeleteFramebuffers(1, &mSceneFB); - if (mSceneTexture != 0) - glDeleteTextures(1, &mSceneTexture); - if (mSceneDepthStencil != 0) - glDeleteRenderbuffers(1, &mSceneDepthStencil); + Clear(); + + glActiveTexture(GL_TEXTURE0); glGenFramebuffers(1, &mSceneFB); glGenTextures(1, &mSceneTexture); @@ -109,15 +131,49 @@ void FGLRenderBuffers::Setup(int width, int height) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindTexture(GL_TEXTURE_2D, 0); glBindRenderbuffer(GL_RENDERBUFFER, mSceneDepthStencil); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); - glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, mSceneFB); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mSceneTexture, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mSceneDepthStencil); + + int bloomWidth = MAX(width / 2, 1); + int bloomHeight = MAX(height / 2, 1); + for (int i = 0; i < NumBloomLevels; i++) + { + auto &level = BloomLevels[i]; + level.Width = MAX(bloomWidth / 2, 1); + level.Height = MAX(bloomHeight / 2, 1); + + glGenTextures(1, &level.VTexture); + glGenTextures(1, &level.HTexture); + glGenFramebuffers(1, &level.VFramebuffer); + glGenFramebuffers(1, &level.HFramebuffer); + + glBindTexture(GL_TEXTURE_2D, level.VTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, level.Width, level.Height, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, level.HTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, level.Width, level.Height, 0, GL_RGBA, GL_FLOAT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindFramebuffer(GL_FRAMEBUFFER, level.VFramebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, level.VTexture, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, level.HFramebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, level.HTexture, 0); + + bloomWidth = level.Width; + bloomHeight = level.Height; + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, mOutputFB); mWidth = width; @@ -154,6 +210,6 @@ void FGLRenderBuffers::BindOutputFB() void FGLRenderBuffers::BindSceneTexture(int index) { - glActiveTexture(index); + glActiveTexture(GL_TEXTURE0 + index); glBindTexture(GL_TEXTURE_2D, mSceneTexture); } diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index bbb38b533..611d196d2 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -3,6 +3,17 @@ #include "gl/shaders/gl_shader.h" +class FGLBloomTextureLevel +{ +public: + GLuint VTexture = 0; + GLuint VFramebuffer = 0; + GLuint HTexture = 0; + GLuint HFramebuffer = 0; + GLuint Width = 0; + GLuint Height = 0; +}; + class FGLRenderBuffers { public: @@ -16,7 +27,12 @@ public: static bool IsSupported() { return gl.version >= 3.3f; } + enum { NumBloomLevels = 4 }; + FGLBloomTextureLevel BloomLevels[NumBloomLevels]; + private: + void Clear(); + int mWidth = 0; int mHeight = 0; diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 6c1677b7b..2eed18bb9 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -62,6 +62,8 @@ #include "gl/data/gl_vertexbuffer.h" #include "gl/scene/gl_drawinfo.h" #include "gl/shaders/gl_shader.h" +#include "gl/shaders/gl_bloomshader.h" +#include "gl/shaders/gl_blurshader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/textures/gl_texture.h" #include "gl/textures/gl_translate.h" @@ -110,6 +112,9 @@ void gl_FlushModels(); void FGLRenderer::Initialize() { mBuffers = new FGLRenderBuffers(); + mBloomExtractShader = new FBloomExtractShader(); + mBloomCombineShader = new FBloomCombineShader(); + mBlurShader = new FBlurShader(); mPresentShader = new FPresentShader(); // Only needed for the core profile, because someone decided it was a good idea to remove the default VAO. @@ -160,6 +165,9 @@ FGLRenderer::~FGLRenderer() } if (mBuffers) delete mBuffers; if (mPresentShader) delete mPresentShader; + if (mBloomExtractShader) delete mBloomExtractShader; + if (mBloomCombineShader) delete mBloomCombineShader; + if (mBlurShader) delete mBlurShader; } //========================================================================== diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 992076528..6635c2353 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -19,6 +19,9 @@ class FLightBuffer; class FSamplerManager; class DPSprite; class FGLRenderBuffers; +class FBloomExtractShader; +class FBloomCombineShader; +class FBlurShader; class FPresentShader; inline float DEG2RAD(float deg) @@ -82,6 +85,9 @@ public: int mOldFBID; FGLRenderBuffers *mBuffers; + FBloomExtractShader *mBloomExtractShader; + FBloomCombineShader *mBloomCombineShader; + FBlurShader *mBlurShader; FPresentShader *mPresentShader; FTexture *gllight; @@ -147,6 +153,7 @@ public: void SetFixedColormap (player_t *player); void WriteSavePic (player_t *player, FILE *file, int width, int height); void EndDrawScene(sector_t * viewsector); + void BloomScene(); void Flush(); void SetProjection(float fov, float ratio, float fovratio); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 3f52ce2b1..0aa9f0444 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -72,6 +72,8 @@ #include "gl/scene/gl_drawinfo.h" #include "gl/scene/gl_portal.h" #include "gl/shaders/gl_shader.h" +#include "gl/shaders/gl_bloomshader.h" +#include "gl/shaders/gl_blurshader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/stereo3d/gl_stereo3d.h" #include "gl/stereo3d/scoped_view_shifter.h" @@ -204,6 +206,130 @@ void FGLRenderer::Set3DViewport() glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); } +//----------------------------------------------------------------------------- +// +// Adds bloom contribution to scene texture +// +//----------------------------------------------------------------------------- + +void FGLRenderer::BloomScene() +{ + if (!FGLRenderBuffers::IsSupported()) + return; + + const float blurAmount = 4.0f; + int sampleCount = 5; // Note: must be uneven number 3 to 15 + float exposure = 2.0f; + + auto vbo = GLRenderer->mVBO; + + // TODO: Need a better way to share state with other parts of the pipeline + GLboolean blendEnabled, scissorEnabled; + GLint currentProgram, blendEquationRgb, blendEquationAlpha, blendSrcRgb, blendSrcAlpha, blendDestRgb, blendDestAlpha; + glGetBooleanv(GL_BLEND, &blendEnabled); + glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled); + glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram); + glGetIntegerv(GL_BLEND_EQUATION_RGB, &blendEquationRgb); + glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blendEquationAlpha); + glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcRgb); + glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrcAlpha); + glGetIntegerv(GL_BLEND_DST_RGB, &blendDestRgb); + glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDestAlpha); + + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); + + const auto &level0 = mBuffers->BloomLevels[0]; + + // Extract blooming pixels from scene texture: + glBindFramebuffer(GL_FRAMEBUFFER, level0.VFramebuffer); + glViewport(0, 0, level0.Width, level0.Height); + mBuffers->BindSceneTexture(0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + mBloomExtractShader->Bind(); + mBloomExtractShader->SceneTexture.Set(0); + mBloomExtractShader->Exposure.Set(exposure); + { + FFlatVertex *ptr = vbo->GetBuffer(); + ptr->Set(-1.0f, -1.0f, 0, 0.0f, 0.0f); ptr++; + ptr->Set(-1.0f, 1.0f, 0, 0.0f, 1.0f); ptr++; + ptr->Set(1.0f, -1.0f, 0, 1.0f, 0.0f); ptr++; + ptr->Set(1.0f, 1.0f, 0, 1.0f, 1.0f); ptr++; + vbo->RenderCurrent(ptr, GL_TRIANGLE_STRIP); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // Blur and downscale: + for (int i = 0; i < FGLRenderBuffers::NumBloomLevels - 1; i++) + { + const auto &level = mBuffers->BloomLevels[i]; + const auto &next = mBuffers->BloomLevels[i + 1]; + mBlurShader->BlurHorizontal(vbo, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height); + mBlurShader->BlurVertical(vbo, blurAmount, sampleCount, level.HTexture, next.VFramebuffer, next.Width, next.Height); + } + + // Blur and upscale: + for (int i = FGLRenderBuffers::NumBloomLevels - 1; i > 0; i--) + { + const auto &level = mBuffers->BloomLevels[i]; + const auto &next = mBuffers->BloomLevels[i - 1]; + + mBlurShader->BlurHorizontal(vbo, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height); + mBlurShader->BlurVertical(vbo, blurAmount, sampleCount, level.HTexture, level.VFramebuffer, level.Width, level.Height); + + // Linear upscale: + glBindFramebuffer(GL_FRAMEBUFFER, next.VFramebuffer); + glViewport(0, 0, next.Width, next.Height); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, level.VTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + mBloomCombineShader->Bind(); + mBloomCombineShader->BloomTexture.Set(0); + { + FFlatVertex *ptr = vbo->GetBuffer(); + ptr->Set(-1.0f, -1.0f, 0, 0.0f, 0.0f); ptr++; + ptr->Set(-1.0f, 1.0f, 0, 0.0f, 1.0f); ptr++; + ptr->Set(1.0f, -1.0f, 0, 1.0f, 0.0f); ptr++; + ptr->Set(1.0f, 1.0f, 0, 1.0f, 1.0f); ptr++; + vbo->RenderCurrent(ptr, GL_TRIANGLE_STRIP); + } + } + + mBlurShader->BlurHorizontal(vbo, blurAmount, sampleCount, level0.VTexture, level0.HFramebuffer, level0.Width, level0.Height); + mBlurShader->BlurVertical(vbo, blurAmount, sampleCount, level0.HTexture, level0.VFramebuffer, level0.Width, level0.Height); + + // Add bloom back to scene texture: + mBuffers->BindSceneFB(); + glViewport(0, 0, mOutputViewport.width, mOutputViewport.height); + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_ONE, GL_ONE); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, level0.VTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + mBloomCombineShader->Bind(); + mBloomCombineShader->BloomTexture.Set(0); + { + FFlatVertex *ptr = vbo->GetBuffer(); + ptr->Set(-1.0f, -1.0f, 0, 0.0f, 0.0f); ptr++; + ptr->Set(-1.0f, 1.0f, 0, 0.0f, 1.0f); ptr++; + ptr->Set(1.0f, -1.0f, 0, 1.0f, 0.0f); ptr++; + ptr->Set(1.0f, 1.0f, 0, 1.0f, 1.0f); ptr++; + vbo->RenderCurrent(ptr, GL_TRIANGLE_STRIP); + } + + if (blendEnabled) + glEnable(GL_BLEND); + if (scissorEnabled) + glEnable(GL_SCISSOR_TEST); + glBlendEquationSeparate(blendEquationRgb, blendEquationAlpha); + glBlendFuncSeparate(blendSrcRgb, blendDestRgb, blendSrcAlpha, blendDestAlpha); +} + //----------------------------------------------------------------------------- // // Run post processing steps and copy to frame buffer @@ -810,6 +936,8 @@ void FGLRenderer::EndDrawScene(sector_t * viewsector) gl_RenderState.ResetColor(); gl_RenderState.EnableTexture(true); glDisable(GL_SCISSOR_TEST); + + BloomScene(); } diff --git a/src/gl/shaders/gl_bloomshader.cpp b/src/gl/shaders/gl_bloomshader.cpp new file mode 100644 index 000000000..23cda96b7 --- /dev/null +++ b/src/gl/shaders/gl_bloomshader.cpp @@ -0,0 +1,79 @@ +/* +** gl_bloomshader.cpp +** Shaders used to do bloom +** +**--------------------------------------------------------------------------- +** Copyright 2016 Magnus Norddahl +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. Full disclosure of the entire project's source code, except for third +** party libraries is mandatory. (NOTE: This clause is non-negotiable!) +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "gl/gl_functions.h" +#include "vectors.h" +#include "gl/system/gl_interface.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_cvars.h" +#include "gl/shaders/gl_bloomshader.h" + +void FBloomExtractShader::Bind() +{ + if (!mShader) + { + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/bloomextract.vp"); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/bloomextract.fp"); + mShader.SetFragDataLocation(0, "FragColor"); + mShader.Link("shaders/glsl/bloomextract"); + mShader.SetAttribLocation(0, "PositionInProjection"); + SceneTexture.Init(mShader, "SceneTexture"); + Exposure.Init(mShader, "ExposureAdjustment"); + } + mShader.Bind(); +} + +void FBloomCombineShader::Bind() +{ + if (!mShader) + { + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/bloomcombine.vp"); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/bloomcombine.fp"); + mShader.SetFragDataLocation(0, "FragColor"); + mShader.Link("shaders/glsl/bloomcombine"); + mShader.SetAttribLocation(0, "PositionInProjection"); + BloomTexture.Init(mShader, "Bloom"); + } + mShader.Bind(); +} diff --git a/src/gl/shaders/gl_bloomshader.h b/src/gl/shaders/gl_bloomshader.h new file mode 100644 index 000000000..a8e93df83 --- /dev/null +++ b/src/gl/shaders/gl_bloomshader.h @@ -0,0 +1,29 @@ +#ifndef __GL_BLOOMSHADER_H +#define __GL_BLOOMSHADER_H + +#include "gl_shaderprogram.h" + +class FBloomExtractShader +{ +public: + void Bind(); + + FBufferedUniform1i SceneTexture; + FBufferedUniform1f Exposure; + +private: + FShaderProgram mShader; +}; + +class FBloomCombineShader +{ +public: + void Bind(); + + FBufferedUniform1i BloomTexture; + +private: + FShaderProgram mShader; +}; + +#endif \ No newline at end of file diff --git a/src/gl/shaders/gl_blurshader.cpp b/src/gl/shaders/gl_blurshader.cpp new file mode 100644 index 000000000..6ed7573ee --- /dev/null +++ b/src/gl/shaders/gl_blurshader.cpp @@ -0,0 +1,265 @@ +/* +** gl_blurshader.cpp +** Gaussian blur shader +** +**--------------------------------------------------------------------------- +** Copyright 2016 Magnus Norddahl +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. Full disclosure of the entire project's source code, except for third +** party libraries is mandatory. (NOTE: This clause is non-negotiable!) +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "gl/gl_functions.h" +#include "vectors.h" +#include "gl/system/gl_interface.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_cvars.h" +#include "gl/shaders/gl_blurshader.h" +#include "gl/data/gl_vertexbuffer.h" + +//========================================================================== +// +// Performs a vertical gaussian blur pass +// +//========================================================================== + +void FBlurShader::BlurVertical(FFlatVertexBuffer *vbo, float blurAmount, int sampleCount, GLuint inputTexture, GLuint outputFrameBuffer, int width, int height) +{ + Blur(vbo, blurAmount, sampleCount, inputTexture, outputFrameBuffer, width, height, true); +} + +//========================================================================== +// +// Performs a horizontal gaussian blur pass +// +//========================================================================== + +void FBlurShader::BlurHorizontal(FFlatVertexBuffer *vbo, float blurAmount, int sampleCount, GLuint inputTexture, GLuint outputFrameBuffer, int width, int height) +{ + Blur(vbo, blurAmount, sampleCount, inputTexture, outputFrameBuffer, width, height, false); +} + +//========================================================================== +// +// Helper for BlurVertical and BlurHorizontal. Executes the actual pass +// +//========================================================================== + +void FBlurShader::Blur(FFlatVertexBuffer *vbo, float blurAmount, int sampleCount, GLuint inputTexture, GLuint outputFrameBuffer, int width, int height, bool vertical) +{ + int error = glGetError(); + BlurSetup *setup = GetSetup(blurAmount, sampleCount); + error = glGetError(); + if (vertical) + setup->VerticalShader->Bind(); + else + setup->HorizontalShader->Bind(); + error = glGetError(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, inputTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + error = glGetError(); + glBindFramebuffer(GL_FRAMEBUFFER, outputFrameBuffer); + glViewport(0, 0, width, height); + glDisable(GL_BLEND); + error = glGetError(); + FFlatVertex *ptr = vbo->GetBuffer(); + ptr->Set(-1.0f, -1.0f, 0, 0.0f, 0.0f); ptr++; + ptr->Set(-1.0f, 1.0f, 0, 0.0f, 1.0f); ptr++; + ptr->Set(1.0f, -1.0f, 0, 1.0f, 0.0f); ptr++; + ptr->Set(1.0f, 1.0f, 0, 1.0f, 1.0f); ptr++; + vbo->RenderCurrent(ptr, GL_TRIANGLE_STRIP); + error = glGetError(); +} + +//========================================================================== +// +// Compiles the blur shaders needed for the specified blur amount and +// kernel size +// +//========================================================================== + +FBlurShader::BlurSetup *FBlurShader::GetSetup(float blurAmount, int sampleCount) +{ + for (size_t mBlurSetupIndex = 0; mBlurSetupIndex < mBlurSetups.Size(); mBlurSetupIndex++) + { + if (mBlurSetups[mBlurSetupIndex].blurAmount == blurAmount && mBlurSetups[mBlurSetupIndex].sampleCount == sampleCount) + { + return &mBlurSetups[mBlurSetupIndex]; + } + } + + BlurSetup blurSetup(blurAmount, sampleCount); + + FString vertexCode = VertexShaderCode(); + FString horizontalCode = FragmentShaderCode(blurAmount, sampleCount, false); + FString verticalCode = FragmentShaderCode(blurAmount, sampleCount, true); + + blurSetup.VerticalShader = std::make_shared(); + blurSetup.VerticalShader->Compile(FShaderProgram::Vertex, "vertical blur vertex shader", vertexCode); + blurSetup.VerticalShader->Compile(FShaderProgram::Fragment, "vertical blur fragment shader", verticalCode); + blurSetup.VerticalShader->SetFragDataLocation(0, "FragColor"); + blurSetup.VerticalShader->SetAttribLocation(0, "PositionInProjection"); + blurSetup.VerticalShader->Link("vertical blur"); + blurSetup.VerticalShader->Bind(); + int error = glGetError(); + glUniform1i(glGetUniformLocation(*blurSetup.VerticalShader.get(), "SourceTexture"), 0); + error = glGetError(); + + blurSetup.HorizontalShader = std::make_shared(); + blurSetup.HorizontalShader->Compile(FShaderProgram::Vertex, "horizontal blur vertex shader", vertexCode); + blurSetup.HorizontalShader->Compile(FShaderProgram::Fragment, "horizontal blur fragment shader", horizontalCode); + blurSetup.HorizontalShader->SetFragDataLocation(0, "FragColor"); + blurSetup.HorizontalShader->SetAttribLocation(0, "PositionInProjection"); + blurSetup.HorizontalShader->Link("horizontal blur"); + blurSetup.HorizontalShader->Bind(); + glUniform1i(glGetUniformLocation(*blurSetup.HorizontalShader.get(), "SourceTexture"), 0); + + mBlurSetups.Push(blurSetup); + + return &mBlurSetups[mBlurSetups.Size() - 1]; +} + +//========================================================================== +// +// The vertex shader GLSL code +// +//========================================================================== + +FString FBlurShader::VertexShaderCode() +{ + return R"( + #version 330 + + in vec4 PositionInProjection; + out vec2 TexCoord; + + void main() + { + gl_Position = PositionInProjection; + TexCoord = (gl_Position.xy + 1.0) * 0.5; + } + )"; +} + +//========================================================================== +// +// Generates the fragment shader GLSL code for a specific blur setup +// +//========================================================================== + +FString FBlurShader::FragmentShaderCode(float blurAmount, int sampleCount, bool vertical) +{ + TArray sampleWeights; + TArray sampleOffsets; + ComputeBlurSamples(sampleCount, blurAmount, sampleWeights, sampleOffsets); + + const char *fragmentShader = + R"( + #version 330 + in vec2 TexCoord; + uniform sampler2D SourceTexture; + out vec4 FragColor; + void main() + { + FragColor = %s; + } + )"; + + FString loopCode; + for (int i = 0; i < sampleCount; i++) + { + if (i > 0) + loopCode += " + "; + + if (vertical) + loopCode.AppendFormat("\r\n\t\t\ttextureOffset(SourceTexture, TexCoord, ivec2(0, %d)) * %f", sampleOffsets[i], (double)sampleWeights[i]); + else + loopCode.AppendFormat("\r\n\t\t\ttextureOffset(SourceTexture, TexCoord, ivec2(%d, 0)) * %f", sampleOffsets[i], (double)sampleWeights[i]); + } + + FString code; + code.Format(fragmentShader, loopCode.GetChars()); + return code; +} + +//========================================================================== +// +// Calculates the sample weight for a specific offset in the kernel +// +//========================================================================== + +float FBlurShader::ComputeGaussian(float n, float theta) // theta = Blur Amount +{ + return (float)((1.0f / sqrtf(2 * (float)M_PI * theta)) * expf(-(n * n) / (2.0f * theta * theta))); +} + +//========================================================================== +// +// Calculates the sample weights and offsets +// +//========================================================================== + +void FBlurShader::ComputeBlurSamples(int sampleCount, float blurAmount, TArray &sampleWeights, TArray &sampleOffsets) +{ + sampleWeights.Resize(sampleCount); + sampleOffsets.Resize(sampleCount); + + sampleWeights[0] = ComputeGaussian(0, blurAmount); + sampleOffsets[0] = 0; + + float totalWeights = sampleWeights[0]; + + for (int i = 0; i < sampleCount / 2; i++) + { + float weight = ComputeGaussian(i + 1.0f, blurAmount); + + sampleWeights[i * 2 + 1] = weight; + sampleWeights[i * 2 + 2] = weight; + sampleOffsets[i * 2 + 1] = i + 1; + sampleOffsets[i * 2 + 2] = -i - 1; + + totalWeights += weight * 2; + } + + for (int i = 0; i < sampleCount; i++) + { + sampleWeights[i] /= totalWeights; + } +} diff --git a/src/gl/shaders/gl_blurshader.h b/src/gl/shaders/gl_blurshader.h new file mode 100644 index 000000000..9bf4a9d5d --- /dev/null +++ b/src/gl/shaders/gl_blurshader.h @@ -0,0 +1,39 @@ +#ifndef __GL_BLURSHADER_H +#define __GL_BLURSHADER_H + +#include "gl_shaderprogram.h" +#include + +class FFlatVertexBuffer; + +class FBlurShader +{ +public: + void BlurVertical(FFlatVertexBuffer *vbo, float blurAmount, int sampleCount, GLuint inputTexture, GLuint outputFrameBuffer, int width, int height); + void BlurHorizontal(FFlatVertexBuffer *vbo, float blurAmount, int sampleCount, GLuint inputTexture, GLuint outputFrameBuffer, int width, int height); + +private: + void Blur(FFlatVertexBuffer *vbo, float blurAmount, int sampleCount, GLuint inputTexture, GLuint outputFrameBuffer, int width, int height, bool vertical); + + struct BlurSetup + { + BlurSetup(float blurAmount, int sampleCount) : blurAmount(blurAmount), sampleCount(sampleCount) { } + + float blurAmount; + int sampleCount; + std::shared_ptr VerticalShader; + std::shared_ptr HorizontalShader; + }; + + BlurSetup *GetSetup(float blurAmount, int sampleCount); + + FString VertexShaderCode(); + FString FragmentShaderCode(float blurAmount, int sampleCount, bool vertical); + + float ComputeGaussian(float n, float theta); + void ComputeBlurSamples(int sampleCount, float blurAmount, TArray &sample_weights, TArray &sample_offsets); + + TArray mBlurSetups; +}; + +#endif \ No newline at end of file diff --git a/src/gl/shaders/gl_shaderprogram.cpp b/src/gl/shaders/gl_shaderprogram.cpp index 35fd468ae..4448bdf59 100644 --- a/src/gl/shaders/gl_shaderprogram.cpp +++ b/src/gl/shaders/gl_shaderprogram.cpp @@ -95,13 +95,17 @@ void FShaderProgram::CreateShader(ShaderType type) void FShaderProgram::Compile(ShaderType type, const char *lumpName) { - CreateShader(type); - - const auto &handle = mShaders[type]; - int lump = Wads.CheckNumForFullName(lumpName); if (lump == -1) I_Error("Unable to load '%s'", lumpName); FString code = Wads.ReadLump(lump).GetString().GetChars(); + Compile(type, lumpName, code); +} + +void FShaderProgram::Compile(ShaderType type, const char *name, const FString &code) +{ + CreateShader(type); + + const auto &handle = mShaders[type]; int lengths[1] = { (int)code.Len() }; const char *sources[1] = { code.GetChars() }; @@ -113,7 +117,7 @@ void FShaderProgram::Compile(ShaderType type, const char *lumpName) glGetShaderiv(handle, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { - I_Error("Compile Shader '%s':\n%s\n", lumpName, GetShaderInfoLog(handle).GetChars()); + I_Error("Compile Shader '%s':\n%s\n", name, GetShaderInfoLog(handle).GetChars()); } else { diff --git a/src/gl/shaders/gl_shaderprogram.h b/src/gl/shaders/gl_shaderprogram.h index 9cc331525..8aebf1623 100644 --- a/src/gl/shaders/gl_shaderprogram.h +++ b/src/gl/shaders/gl_shaderprogram.h @@ -16,6 +16,7 @@ public: }; void Compile(ShaderType type, const char *lumpName); + void Compile(ShaderType type, const char *name, const FString &code); void SetFragDataLocation(int index, const char *name); void Link(const char *name); void SetAttribLocation(int index, const char *name); diff --git a/wadsrc/static/shaders/glsl/bloomcombine.fp b/wadsrc/static/shaders/glsl/bloomcombine.fp new file mode 100644 index 000000000..0db4802f0 --- /dev/null +++ b/wadsrc/static/shaders/glsl/bloomcombine.fp @@ -0,0 +1,12 @@ + +#version 330 + +in vec2 TexCoord; +out vec4 FragColor; + +uniform sampler2D Bloom; + +void main() +{ + FragColor = vec4(texture(Bloom, TexCoord).rgb, 0.0); +} diff --git a/wadsrc/static/shaders/glsl/bloomcombine.vp b/wadsrc/static/shaders/glsl/bloomcombine.vp new file mode 100644 index 000000000..b73fe7fc8 --- /dev/null +++ b/wadsrc/static/shaders/glsl/bloomcombine.vp @@ -0,0 +1,11 @@ + +#version 330 + +in vec4 PositionInProjection; +out vec2 TexCoord; + +void main() +{ + gl_Position = PositionInProjection; + TexCoord = PositionInProjection.xy * 0.5 + 0.5; +} diff --git a/wadsrc/static/shaders/glsl/bloomextract.fp b/wadsrc/static/shaders/glsl/bloomextract.fp new file mode 100644 index 000000000..b6d663db6 --- /dev/null +++ b/wadsrc/static/shaders/glsl/bloomextract.fp @@ -0,0 +1,14 @@ + +#version 330 + +in vec2 TexCoord; +out vec4 FragColor; + +uniform sampler2D SceneTexture; +uniform float ExposureAdjustment; + +void main() +{ + vec4 color = texture(SceneTexture, TexCoord); + FragColor = max(vec4(color.rgb * ExposureAdjustment - 1, 1), vec4(0)); +} diff --git a/wadsrc/static/shaders/glsl/bloomextract.vp b/wadsrc/static/shaders/glsl/bloomextract.vp new file mode 100644 index 000000000..b73fe7fc8 --- /dev/null +++ b/wadsrc/static/shaders/glsl/bloomextract.vp @@ -0,0 +1,11 @@ + +#version 330 + +in vec4 PositionInProjection; +out vec2 TexCoord; + +void main() +{ + gl_Position = PositionInProjection; + TexCoord = PositionInProjection.xy * 0.5 + 0.5; +}