From 69f52cc89800a38406155d9b040642c0fb436b69 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 27 Jul 2016 21:50:30 +0200 Subject: [PATCH 1/4] 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 cad3299628..4e7ae8cbb6 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 26377553f0..52182c9dae 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 bbb38b533a..611d196d2b 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 6c1677b7b3..2eed18bb9a 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 9920765287..6635c2353a 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 3f52ce2b13..0aa9f04446 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 0000000000..23cda96b73 --- /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 0000000000..a8e93df83c --- /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 0000000000..6ed7573ee0 --- /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 0000000000..9bf4a9d5d4 --- /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 35fd468aee..4448bdf594 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 9cc3315259..8aebf16236 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 0000000000..0db4802f0a --- /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 0000000000..b73fe7fc81 --- /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 0000000000..b6d663db66 --- /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 0000000000..b73fe7fc81 --- /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; +} From 0efee85bd835fb0fffe73cf9a519ace5ff595abb Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Fri, 29 Jul 2016 00:36:43 +0200 Subject: [PATCH 2/4] Added tonemapping and sector based exposure control --- src/CMakeLists.txt | 1 + src/gl/renderer/gl_renderbuffers.cpp | 41 ++++++++++++++ src/gl/renderer/gl_renderbuffers.h | 4 ++ src/gl/renderer/gl_renderer.cpp | 8 ++- src/gl/renderer/gl_renderer.h | 5 ++ src/gl/scene/gl_scene.cpp | 79 +++++++++++++++++++++------ src/gl/shaders/gl_blurshader.cpp | 11 +--- src/gl/shaders/gl_tonemapshader.cpp | 65 ++++++++++++++++++++++ src/gl/shaders/gl_tonemapshader.h | 18 ++++++ src/gl/system/gl_wipe.cpp | 6 +- wadsrc/static/shaders/glsl/tonemap.fp | 65 ++++++++++++++++++++++ wadsrc/static/shaders/glsl/tonemap.vp | 11 ++++ 12 files changed, 284 insertions(+), 30 deletions(-) create mode 100644 src/gl/shaders/gl_tonemapshader.cpp create mode 100644 src/gl/shaders/gl_tonemapshader.h create mode 100644 wadsrc/static/shaders/glsl/tonemap.fp create mode 100644 wadsrc/static/shaders/glsl/tonemap.vp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4e7ae8cbb6..6009a2c375 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1108,6 +1108,7 @@ set( FASTMATH_SOURCES gl/shaders/gl_presentshader.cpp gl/shaders/gl_bloomshader.cpp gl/shaders/gl_blurshader.cpp + gl/shaders/gl_tonemapshader.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 52182c9dae..590ea39355 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -97,10 +97,16 @@ void FGLRenderBuffers::Clear() glDeleteTextures(1, &mSceneTexture); if (mSceneDepthStencil != 0) glDeleteRenderbuffers(1, &mSceneDepthStencil); + if (mHudFB != 0) + glDeleteFramebuffers(1, &mHudFB); + if (mHudTexture != 0) + glDeleteTextures(1, &mHudTexture); mSceneFB = 0; mSceneTexture = 0; mSceneDepthStencil = 0; + mHudFB = 0; + mHudTexture = 0; mWidth = 0; mHeight = 0; } @@ -122,7 +128,9 @@ void FGLRenderBuffers::Setup(int width, int height) glActiveTexture(GL_TEXTURE0); glGenFramebuffers(1, &mSceneFB); + glGenFramebuffers(1, &mHudFB); glGenTextures(1, &mSceneTexture); + glGenTextures(1, &mHudTexture); glGenRenderbuffers(1, &mSceneDepthStencil); glBindTexture(GL_TEXTURE_2D, mSceneTexture); @@ -139,6 +147,16 @@ void FGLRenderBuffers::Setup(int width, int height) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mSceneTexture, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mSceneDepthStencil); + glBindTexture(GL_TEXTURE_2D, mHudTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + 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); + + glBindFramebuffer(GL_FRAMEBUFFER, mHudFB); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mHudTexture, 0); + int bloomWidth = MAX(width / 2, 1); int bloomHeight = MAX(height / 2, 1); for (int i = 0; i < NumBloomLevels; i++) @@ -191,6 +209,17 @@ void FGLRenderBuffers::BindSceneFB() glBindFramebuffer(GL_FRAMEBUFFER, mSceneFB); } +//========================================================================== +// +// Makes the 2D/HUD frame buffer active +// +//========================================================================== + +void FGLRenderBuffers::BindHudFB() +{ + glBindFramebuffer(GL_FRAMEBUFFER, mHudFB); +} + //========================================================================== // // Makes the screen frame buffer active @@ -213,3 +242,15 @@ void FGLRenderBuffers::BindSceneTexture(int index) glActiveTexture(GL_TEXTURE0 + index); glBindTexture(GL_TEXTURE_2D, mSceneTexture); } + +//========================================================================== +// +// Binds the 2D/HUD frame buffer texture to the specified texture unit +// +//========================================================================== + +void FGLRenderBuffers::BindHudTexture(int index) +{ + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, mHudTexture); +} diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index 611d196d2b..24d301d52b 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -22,8 +22,10 @@ public: void Setup(int width, int height); void BindSceneFB(); + void BindHudFB(); void BindOutputFB(); void BindSceneTexture(int index); + void BindHudTexture(int index); static bool IsSupported() { return gl.version >= 3.3f; } @@ -39,6 +41,8 @@ private: GLuint mSceneTexture = 0; GLuint mSceneDepthStencil = 0; GLuint mSceneFB = 0; + GLuint mHudTexture = 0; + GLuint mHudFB = 0; GLuint mOutputFB = 0; }; diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 2eed18bb9a..264ff4c7f5 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -64,6 +64,7 @@ #include "gl/shaders/gl_shader.h" #include "gl/shaders/gl_bloomshader.h" #include "gl/shaders/gl_blurshader.h" +#include "gl/shaders/gl_tonemapshader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/textures/gl_texture.h" #include "gl/textures/gl_translate.h" @@ -115,6 +116,7 @@ void FGLRenderer::Initialize() mBloomExtractShader = new FBloomExtractShader(); mBloomCombineShader = new FBloomCombineShader(); mBlurShader = new FBlurShader(); + mTonemapShader = new FTonemapShader(); mPresentShader = new FPresentShader(); // Only needed for the core profile, because someone decided it was a good idea to remove the default VAO. @@ -168,6 +170,7 @@ FGLRenderer::~FGLRenderer() if (mBloomExtractShader) delete mBloomExtractShader; if (mBloomCombineShader) delete mBloomCombineShader; if (mBlurShader) delete mBlurShader; + if (mTonemapShader) delete mTonemapShader; } //========================================================================== @@ -235,7 +238,10 @@ void FGLRenderer::Begin2D() if (FGLRenderBuffers::IsSupported()) { mBuffers->Setup(framebuffer->GetWidth(), framebuffer->GetHeight()); - mBuffers->BindSceneFB(); + if (mDrawingScene2D) + mBuffers->BindSceneFB(); + else + mBuffers->BindHudFB(); glViewport(0, 0, mOutputViewport.width, mOutputViewport.height); } else diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 6635c2353a..39eddcbf0b 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -22,6 +22,7 @@ class FGLRenderBuffers; class FBloomExtractShader; class FBloomCombineShader; class FBlurShader; +class FTonemapShader; class FPresentShader; inline float DEG2RAD(float deg) @@ -88,6 +89,7 @@ public: FBloomExtractShader *mBloomExtractShader; FBloomCombineShader *mBloomCombineShader; FBlurShader *mBlurShader; + FTonemapShader *mTonemapShader; FPresentShader *mPresentShader; FTexture *gllight; @@ -106,6 +108,8 @@ public: GL_IRECT mOutputViewportLB; GL_IRECT mOutputViewport; + bool mDrawingScene2D = false; + float mCameraExposure = 1.0f; FGLRenderer(OpenGLFrameBuffer *fb); ~FGLRenderer() ; @@ -154,6 +158,7 @@ public: void WriteSavePic (player_t *player, FILE *file, int width, int height); void EndDrawScene(sector_t * viewsector); void BloomScene(); + void TonemapScene(); 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 0aa9f04446..2136e21a28 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -74,6 +74,7 @@ #include "gl/shaders/gl_shader.h" #include "gl/shaders/gl_bloomshader.h" #include "gl/shaders/gl_blurshader.h" +#include "gl/shaders/gl_tonemapshader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/stereo3d/gl_stereo3d.h" #include "gl/stereo3d/scoped_view_shifter.h" @@ -219,11 +220,9 @@ void FGLRenderer::BloomScene() const float blurAmount = 4.0f; int sampleCount = 5; // Note: must be uneven number 3 to 15 - float exposure = 2.0f; + float exposure = mCameraExposure; - auto vbo = GLRenderer->mVBO; - - // TODO: Need a better way to share state with other parts of the pipeline + // TBD: Maybe 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); @@ -251,12 +250,12 @@ void FGLRenderer::BloomScene() mBloomExtractShader->SceneTexture.Set(0); mBloomExtractShader->Exposure.Set(exposure); { - FFlatVertex *ptr = vbo->GetBuffer(); + FFlatVertex *ptr = mVBO->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); + mVBO->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); @@ -266,8 +265,8 @@ void FGLRenderer::BloomScene() { 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); + mBlurShader->BlurHorizontal(mVBO, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height); + mBlurShader->BlurVertical(mVBO, blurAmount, sampleCount, level.HTexture, next.VFramebuffer, next.Width, next.Height); } // Blur and upscale: @@ -276,8 +275,8 @@ void FGLRenderer::BloomScene() 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); + mBlurShader->BlurHorizontal(mVBO, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height); + mBlurShader->BlurVertical(mVBO, blurAmount, sampleCount, level.HTexture, level.VFramebuffer, level.Width, level.Height); // Linear upscale: glBindFramebuffer(GL_FRAMEBUFFER, next.VFramebuffer); @@ -289,17 +288,17 @@ void FGLRenderer::BloomScene() mBloomCombineShader->Bind(); mBloomCombineShader->BloomTexture.Set(0); { - FFlatVertex *ptr = vbo->GetBuffer(); + FFlatVertex *ptr = mVBO->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); + mVBO->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); + mBlurShader->BlurHorizontal(mVBO, blurAmount, sampleCount, level0.VTexture, level0.HFramebuffer, level0.Width, level0.Height); + mBlurShader->BlurVertical(mVBO, blurAmount, sampleCount, level0.HTexture, level0.VFramebuffer, level0.Width, level0.Height); // Add bloom back to scene texture: mBuffers->BindSceneFB(); @@ -314,12 +313,12 @@ void FGLRenderer::BloomScene() mBloomCombineShader->Bind(); mBloomCombineShader->BloomTexture.Set(0); { - FFlatVertex *ptr = vbo->GetBuffer(); + FFlatVertex *ptr = mVBO->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); + mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP); } if (blendEnabled) @@ -332,7 +331,41 @@ void FGLRenderer::BloomScene() //----------------------------------------------------------------------------- // -// Run post processing steps and copy to frame buffer +// Tonemap scene texture and place the result in the HUD/2D texture +// +//----------------------------------------------------------------------------- + +void FGLRenderer::TonemapScene() +{ + GLboolean blendEnabled, scissorEnabled; + glGetBooleanv(GL_BLEND, &blendEnabled); + glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled); + + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); + + mBuffers->BindHudFB(); + mBuffers->BindSceneTexture(0); + mTonemapShader->Bind(); + mTonemapShader->SceneTexture.Set(0); + mTonemapShader->Exposure.Set(mCameraExposure); + + FFlatVertex *ptr = mVBO->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++; + mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP); + + if (blendEnabled) + glEnable(GL_BLEND); + if (scissorEnabled) + glEnable(GL_SCISSOR_TEST); +} + +//----------------------------------------------------------------------------- +// +// Gamma correct while copying to frame buffer // //----------------------------------------------------------------------------- @@ -406,7 +439,7 @@ void FGLRenderer::Flush() mPresentShader->Contrast.Set(clamp(vid_contrast, 0.1f, 3.f)); mPresentShader->Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f)); } - mBuffers->BindSceneTexture(0); + mBuffers->BindHudTexture(0); FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer(); ptr->Set(-1.0f, -1.0f, 0, 0.0f, 0.0f); ptr++; @@ -938,6 +971,7 @@ void FGLRenderer::EndDrawScene(sector_t * viewsector) glDisable(GL_SCISSOR_TEST); BloomScene(); + TonemapScene(); } @@ -1042,6 +1076,13 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo mViewActor=camera; } + if (toscreen) + { + float light = viewsector->lightlevel / 255.0f; + float exposure = MAX(1.0f + (1.0f - light * light) * 1.5f, 0.5f); + mCameraExposure = mCameraExposure * 0.98f + exposure * 0.02f; + } + // 'viewsector' will not survive the rendering so it cannot be used anymore below. retval = viewsector; @@ -1056,6 +1097,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo // TODO: stereo specific viewport - needed when implementing side-by-side modes etc. SetOutputViewport(bounds); Set3DViewport(); + mDrawingScene2D = true; mCurrentFoV = fov; // Stereo mode specific perspective projection SetProjection( eye->GetProjection(fov, ratio, fovratio) ); @@ -1073,6 +1115,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo ProcessScene(toscreen); if (mainview) EndDrawScene(retval); // do not call this for camera textures. + mDrawingScene2D = false; eye->TearDown(); } stereo3dMode.TearDown(); diff --git a/src/gl/shaders/gl_blurshader.cpp b/src/gl/shaders/gl_blurshader.cpp index 6ed7573ee0..aee8696d99 100644 --- a/src/gl/shaders/gl_blurshader.cpp +++ b/src/gl/shaders/gl_blurshader.cpp @@ -80,32 +80,29 @@ void FBlurShader::BlurHorizontal(FFlatVertexBuffer *vbo, float blurAmount, int s 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(); } //========================================================================== @@ -138,9 +135,7 @@ FBlurShader::BlurSetup *FBlurShader::GetSetup(float blurAmount, int sampleCount) 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); diff --git a/src/gl/shaders/gl_tonemapshader.cpp b/src/gl/shaders/gl_tonemapshader.cpp new file mode 100644 index 0000000000..44ea400128 --- /dev/null +++ b/src/gl/shaders/gl_tonemapshader.cpp @@ -0,0 +1,65 @@ +/* +** gl_tonemapshader.cpp +** Converts a HDR texture to 0-1 range by applying a tonemap operator +** +**--------------------------------------------------------------------------- +** 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_tonemapshader.h" + +void FTonemapShader::Bind() +{ + if (!mShader) + { + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/tonemap.vp"); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/tonemap.fp"); + mShader.SetFragDataLocation(0, "FragColor"); + mShader.Link("shaders/glsl/tonemap"); + mShader.SetAttribLocation(0, "PositionInProjection"); + SceneTexture.Init(mShader, "InputTexture"); + Exposure.Init(mShader, "ExposureAdjustment"); + } + mShader.Bind(); +} diff --git a/src/gl/shaders/gl_tonemapshader.h b/src/gl/shaders/gl_tonemapshader.h new file mode 100644 index 0000000000..0380e31b8b --- /dev/null +++ b/src/gl/shaders/gl_tonemapshader.h @@ -0,0 +1,18 @@ +#ifndef __GL_TONEMAPSHADER_H +#define __GL_TONEMAPSHADER_H + +#include "gl_shaderprogram.h" + +class FTonemapShader +{ +public: + void Bind(); + + FBufferedUniform1i SceneTexture; + FBufferedUniform1f Exposure; + +private: + FShaderProgram mShader; +}; + +#endif \ No newline at end of file diff --git a/src/gl/system/gl_wipe.cpp b/src/gl/system/gl_wipe.cpp index 6b15e0b6d6..1f1dedba39 100644 --- a/src/gl/system/gl_wipe.cpp +++ b/src/gl/system/gl_wipe.cpp @@ -158,7 +158,7 @@ bool OpenGLFrameBuffer::WipeStartScreen(int type) if (FGLRenderBuffers::IsSupported()) { - GLRenderer->mBuffers->BindSceneFB(); + GLRenderer->mBuffers->BindHudFB(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height); } else @@ -193,7 +193,7 @@ void OpenGLFrameBuffer::WipeEndScreen() wipeendscreen->Bind(0, false, false); if (FGLRenderBuffers::IsSupported()) - GLRenderer->mBuffers->BindSceneFB(); + GLRenderer->mBuffers->BindHudFB(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -232,7 +232,7 @@ bool OpenGLFrameBuffer::WipeDo(int ticks) if (FGLRenderBuffers::IsSupported()) { - GLRenderer->mBuffers->BindSceneFB(); + GLRenderer->mBuffers->BindHudFB(); glViewport(0, 0, GLRenderer->mOutputViewport.width, GLRenderer->mOutputViewport.height); } diff --git a/wadsrc/static/shaders/glsl/tonemap.fp b/wadsrc/static/shaders/glsl/tonemap.fp new file mode 100644 index 0000000000..e1e3d3497c --- /dev/null +++ b/wadsrc/static/shaders/glsl/tonemap.fp @@ -0,0 +1,65 @@ + +#version 330 + +in vec2 TexCoord; +out vec4 FragColor; + +uniform sampler2D InputTexture; +uniform float ExposureAdjustment; + +vec3 Linear(vec3 c) +{ + //return pow(c, 2.2); + return c * c; // cheaper, but assuming gamma of 2.0 instead of 2.2 +} + +vec3 sRGB(vec3 c) +{ + //return pow(c, vec3(1.0 / 2.2)); + return sqrt(c); // cheaper, but assuming gamma of 2.0 instead of 2.2 +} + +vec3 TonemapLinear(vec3 color) +{ + return sRGB(color); +} + +vec3 TonemapReinhard(vec3 color) +{ + color = color / (1 + color); + return sRGB(color); +} + +vec3 TonemapHejlDawson(vec3 color) +{ + vec3 x = max(vec3(0), color - 0.004); + return (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06); // no sRGB needed +} + +vec3 Uncharted2Tonemap(vec3 x) +{ + float A = 0.15; + float B = 0.50; + float C = 0.10; + float D = 0.20; + float E = 0.02; + float F = 0.30; + return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; +} + +vec3 TonemapUncharted2(vec3 color) +{ + float W = 11.2; + float ExposureBias = 2.0; + vec3 curr = Uncharted2Tonemap(ExposureBias * color); + vec3 whiteScale = vec3(1) / Uncharted2Tonemap(vec3(W)); + return sRGB(curr * whiteScale); +} + +void main() +{ + vec3 color = texture(InputTexture, TexCoord).rgb; + color = color * ExposureAdjustment; + color = Linear(color); // needed because gzdoom's scene texture is not linear at the moment + FragColor = vec4(TonemapUncharted2(color), 1.0); +} diff --git a/wadsrc/static/shaders/glsl/tonemap.vp b/wadsrc/static/shaders/glsl/tonemap.vp new file mode 100644 index 0000000000..b73fe7fc81 --- /dev/null +++ b/wadsrc/static/shaders/glsl/tonemap.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; +} From 5849c830289fb74022bee01df6e924f0102a147f Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Fri, 29 Jul 2016 21:31:20 +0200 Subject: [PATCH 3/4] Added bloom and tonemap to menus Added gl_renderbuffers CVAR that disables render buffers Added patch shader support to FShaderProgram Added OpenGL 2 fallback support to render buffers --- src/gl/renderer/gl_renderbuffers.cpp | 220 ++++++++++++++------- src/gl/renderer/gl_renderbuffers.h | 14 +- src/gl/renderer/gl_renderer.cpp | 2 +- src/gl/renderer/gl_renderer.h | 2 +- src/gl/scene/gl_scene.cpp | 61 ++++-- src/gl/shaders/gl_bloomshader.cpp | 8 +- src/gl/shaders/gl_blurshader.cpp | 11 +- src/gl/shaders/gl_presentshader.cpp | 4 +- src/gl/shaders/gl_shader.cpp | 44 +---- src/gl/shaders/gl_shaderprogram.cpp | 100 +++++++++- src/gl/shaders/gl_shaderprogram.h | 11 +- src/gl/shaders/gl_tonemapshader.cpp | 31 ++- src/gl/shaders/gl_tonemapshader.h | 14 +- src/gl/system/gl_cvars.h | 8 + src/gl/system/gl_interface.cpp | 3 + src/gl/system/gl_interface.h | 3 + src/gl/system/gl_wipe.cpp | 6 +- wadsrc/static/language.enu | 5 + wadsrc/static/menudef.z | 11 ++ wadsrc/static/shaders/glsl/bloomcombine.fp | 2 - wadsrc/static/shaders/glsl/bloomcombine.vp | 2 - wadsrc/static/shaders/glsl/bloomextract.fp | 2 - wadsrc/static/shaders/glsl/bloomextract.vp | 2 - wadsrc/static/shaders/glsl/present.fp | 2 - wadsrc/static/shaders/glsl/present.vp | 2 - wadsrc/static/shaders/glsl/tonemap.fp | 22 ++- wadsrc/static/shaders/glsl/tonemap.vp | 2 - 27 files changed, 401 insertions(+), 193 deletions(-) diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index 590ea39355..8be9e6b075 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -53,6 +53,8 @@ #include "i_system.h" #include "doomerrors.h" +CVAR(Bool, gl_renderbuffers, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); + //========================================================================== // // Initialize render buffers and textures used in rendering passes @@ -80,37 +82,45 @@ 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); + DeleteFrameBuffer(level.HFramebuffer); + DeleteFrameBuffer(level.VFramebuffer); + DeleteTexture(level.HTexture); + DeleteTexture(level.VTexture); level = FGLBloomTextureLevel(); } - if (mSceneFB != 0) - glDeleteFramebuffers(1, &mSceneFB); - if (mSceneTexture != 0) - glDeleteTextures(1, &mSceneTexture); - if (mSceneDepthStencil != 0) - glDeleteRenderbuffers(1, &mSceneDepthStencil); - if (mHudFB != 0) - glDeleteFramebuffers(1, &mHudFB); - if (mHudTexture != 0) - glDeleteTextures(1, &mHudTexture); - - mSceneFB = 0; - mSceneTexture = 0; - mSceneDepthStencil = 0; - mHudFB = 0; - mHudTexture = 0; + DeleteFrameBuffer(mSceneFB); + DeleteTexture(mSceneTexture); + DeleteRenderBuffer(mSceneDepthStencil); + DeleteRenderBuffer(mSceneDepth); + DeleteRenderBuffer(mSceneStencil); + DeleteFrameBuffer(mHudFB); + DeleteTexture(mHudTexture); mWidth = 0; mHeight = 0; } +void FGLRenderBuffers::DeleteTexture(GLuint &handle) +{ + if (handle != 0) + glDeleteTextures(1, &handle); + handle = 0; +} + +void FGLRenderBuffers::DeleteRenderBuffer(GLuint &handle) +{ + if (handle != 0) + glDeleteRenderbuffers(1, &handle); + handle = 0; +} + +void FGLRenderBuffers::DeleteFrameBuffer(GLuint &handle) +{ + if (handle != 0) + glDeleteFramebuffers(1, &handle); + handle = 0; +} + //========================================================================== // // Makes sure all render buffers have sizes suitable for rending at the @@ -125,37 +135,23 @@ void FGLRenderBuffers::Setup(int width, int height) Clear(); - glActiveTexture(GL_TEXTURE0); + GLuint hdrFormat = ((gl.flags & RFL_NO_RGBA16F) != 0) ? GL_RGBA8 : GL_RGBA16F; - glGenFramebuffers(1, &mSceneFB); - glGenFramebuffers(1, &mHudFB); - glGenTextures(1, &mSceneTexture); - glGenTextures(1, &mHudTexture); - glGenRenderbuffers(1, &mSceneDepthStencil); + mSceneTexture = Create2DTexture(hdrFormat, width, height); + if ((gl.flags & RFL_NO_DEPTHSTENCIL) != 0) + { + mSceneDepth = CreateRenderBuffer(GL_DEPTH_COMPONENT24, width, height); + mSceneStencil = CreateRenderBuffer(GL_STENCIL_INDEX8, width, height); + mSceneFB = CreateFrameBuffer(mSceneTexture, mSceneDepth, mSceneStencil); + } + else + { + mSceneDepthStencil = CreateRenderBuffer(GL_DEPTH24_STENCIL8, width, height); + mSceneFB = CreateFrameBuffer(mSceneTexture, mSceneDepthStencil); + } - glBindTexture(GL_TEXTURE_2D, mSceneTexture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - 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); - - glBindRenderbuffer(GL_RENDERBUFFER, mSceneDepthStencil); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); - - 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); - - glBindTexture(GL_TEXTURE_2D, mHudTexture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - 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); - - glBindFramebuffer(GL_FRAMEBUFFER, mHudFB); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mHudTexture, 0); + mHudTexture = Create2DTexture(hdrFormat, width, height); + mHudFB = CreateFrameBuffer(mHudTexture); int bloomWidth = MAX(width / 2, 1); int bloomHeight = MAX(height / 2, 1); @@ -165,31 +161,16 @@ void FGLRenderBuffers::Setup(int width, int height) 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); + level.VTexture = Create2DTexture(hdrFormat, level.Width, level.Height); + level.HTexture = Create2DTexture(hdrFormat, level.Width, level.Height); + level.VFramebuffer = CreateFrameBuffer(level.VTexture); + level.HFramebuffer = CreateFrameBuffer(level.HTexture); bloomWidth = level.Width; bloomHeight = level.Height; } + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, mOutputFB); @@ -198,6 +179,78 @@ void FGLRenderBuffers::Setup(int width, int height) mHeight = height; } +//========================================================================== +// +// Creates a 2D texture defaulting to linear filtering and clamp to edge +// +//========================================================================== + +GLuint FGLRenderBuffers::Create2DTexture(GLuint format, int width, int height) +{ + GLuint type = (format == GL_RGBA16F) ? GL_FLOAT : GL_UNSIGNED_BYTE; + GLuint handle = 0; + glGenTextures(1, &handle); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, handle); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, type, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + 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); + return handle; +} + +//========================================================================== +// +// Creates a render buffer +// +//========================================================================== + +GLuint FGLRenderBuffers::CreateRenderBuffer(GLuint format, int width, int height) +{ + GLuint handle = 0; + glGenRenderbuffers(1, &handle); + glBindRenderbuffer(GL_RENDERBUFFER, handle); + glRenderbufferStorage(GL_RENDERBUFFER, format, width, height); + return handle; +} + +//========================================================================== +// +// Creates a frame buffer +// +//========================================================================== + +GLuint FGLRenderBuffers::CreateFrameBuffer(GLuint colorbuffer) +{ + GLuint handle = 0; + glGenFramebuffers(1, &handle); + glBindFramebuffer(GL_FRAMEBUFFER, handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0); + return handle; +} + +GLuint FGLRenderBuffers::CreateFrameBuffer(GLuint colorbuffer, GLuint depthstencil) +{ + GLuint handle = 0; + glGenFramebuffers(1, &handle); + glBindFramebuffer(GL_FRAMEBUFFER, handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthstencil); + return handle; +} + +GLuint FGLRenderBuffers::CreateFrameBuffer(GLuint colorbuffer, GLuint depth, GLuint stencil) +{ + GLuint handle = 0; + glGenFramebuffers(1, &handle); + glBindFramebuffer(GL_FRAMEBUFFER, handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencil); + return handle; +} + //========================================================================== // // Makes the scene frame buffer active @@ -217,7 +270,10 @@ void FGLRenderBuffers::BindSceneFB() void FGLRenderBuffers::BindHudFB() { - glBindFramebuffer(GL_FRAMEBUFFER, mHudFB); + if (gl_tonemap != 0) + glBindFramebuffer(GL_FRAMEBUFFER, mHudFB); + else + glBindFramebuffer(GL_FRAMEBUFFER, mSceneFB); } //========================================================================== @@ -252,5 +308,19 @@ void FGLRenderBuffers::BindSceneTexture(int index) void FGLRenderBuffers::BindHudTexture(int index) { glActiveTexture(GL_TEXTURE0 + index); - glBindTexture(GL_TEXTURE_2D, mHudTexture); + if (gl_tonemap != 0) + glBindTexture(GL_TEXTURE_2D, mHudTexture); + else + glBindTexture(GL_TEXTURE_2D, mSceneTexture); +} + +//========================================================================== +// +// Returns true if render buffers are supported and should be used +// +//========================================================================== + +bool FGLRenderBuffers::IsEnabled() +{ + return gl_renderbuffers && gl.glslversion != 0; } diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index 24d301d52b..ba5e94ab33 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -27,19 +27,29 @@ public: void BindSceneTexture(int index); void BindHudTexture(int index); - static bool IsSupported() { return gl.version >= 3.3f; } - enum { NumBloomLevels = 4 }; FGLBloomTextureLevel BloomLevels[NumBloomLevels]; + static bool IsEnabled(); + private: void Clear(); + GLuint Create2DTexture(GLuint format, int width, int height); + GLuint CreateRenderBuffer(GLuint format, int width, int height); + GLuint CreateFrameBuffer(GLuint colorbuffer); + GLuint CreateFrameBuffer(GLuint colorbuffer, GLuint depthstencil); + GLuint CreateFrameBuffer(GLuint colorbuffer, GLuint depth, GLuint stencil); + void DeleteTexture(GLuint &handle); + void DeleteRenderBuffer(GLuint &handle); + void DeleteFrameBuffer(GLuint &handle); int mWidth = 0; int mHeight = 0; GLuint mSceneTexture = 0; GLuint mSceneDepthStencil = 0; + GLuint mSceneDepth = 0; + GLuint mSceneStencil = 0; GLuint mSceneFB = 0; GLuint mHudTexture = 0; GLuint mHudFB = 0; diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 264ff4c7f5..a1d8bb2a73 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -235,7 +235,7 @@ void FGLRenderer::SetupLevel() void FGLRenderer::Begin2D() { - if (FGLRenderBuffers::IsSupported()) + if (FGLRenderBuffers::IsEnabled()) { mBuffers->Setup(framebuffer->GetWidth(), framebuffer->GetHeight()); if (mDrawingScene2D) diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 39eddcbf0b..ad2046cad4 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -117,7 +117,7 @@ public: angle_t FrustumAngle(); void SetViewArea(); void SetOutputViewport(GL_IRECT *bounds); - void Set3DViewport(); + void Set3DViewport(bool toscreen); void Reset3DViewport(); sector_t *RenderViewpoint (AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); void RenderView(player_t *player); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 2136e21a28..c23992f1b6 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -96,6 +96,22 @@ CVAR(Float, gl_mask_threshold, 0.5f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Float, gl_mask_sprite_threshold, 0.5f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, gl_sort_textures, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Bool, gl_bloom, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); +CVAR(Float, gl_bloom_amount, 1.4f, 0) +CVAR(Float, gl_exposure, 0.0f, 0) + +CUSTOM_CVAR(Int, gl_tonemap, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (self < 0 || self > 4) + self = 0; +} + +CUSTOM_CVAR(Int, gl_bloom_kernel_size, 7, 0) +{ + if (self < 3 || self > 15 || self % 2 == 0) + self = 7; +} + EXTERN_CVAR (Bool, cl_capfps) EXTERN_CVAR (Bool, r_deathcamera) EXTERN_CVAR(Float, vid_brightness) @@ -163,7 +179,7 @@ void FGLRenderer::SetViewArea() void FGLRenderer::Reset3DViewport() { - if (FGLRenderBuffers::IsSupported()) + if (FGLRenderBuffers::IsEnabled()) glViewport(0, 0, mOutputViewport.width, mOutputViewport.height); else glViewport(mOutputViewport.left, mOutputViewport.top, mOutputViewport.width, mOutputViewport.height); @@ -175,10 +191,10 @@ void FGLRenderer::Reset3DViewport() // //----------------------------------------------------------------------------- -void FGLRenderer::Set3DViewport() +void FGLRenderer::Set3DViewport(bool toscreen) { const auto &bounds = mOutputViewportLB; - if (FGLRenderBuffers::IsSupported()) + if (toscreen && FGLRenderBuffers::IsEnabled()) { mBuffers->Setup(mOutputViewport.width, mOutputViewport.height); mBuffers->BindSceneFB(); @@ -215,12 +231,12 @@ void FGLRenderer::Set3DViewport() void FGLRenderer::BloomScene() { - if (!FGLRenderBuffers::IsSupported()) + // Only bloom things if enabled and no special fixed light mode is active + if (!gl_bloom || !FGLRenderBuffers::IsEnabled() || gl_fixedcolormap != CM_DEFAULT) return; - const float blurAmount = 4.0f; - int sampleCount = 5; // Note: must be uneven number 3 to 15 - float exposure = mCameraExposure; + const float blurAmount = gl_bloom_amount; + int sampleCount = gl_bloom_kernel_size; // TBD: Maybe need a better way to share state with other parts of the pipeline GLboolean blendEnabled, scissorEnabled; @@ -248,7 +264,7 @@ void FGLRenderer::BloomScene() glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); mBloomExtractShader->Bind(); mBloomExtractShader->SceneTexture.Set(0); - mBloomExtractShader->Exposure.Set(exposure); + mBloomExtractShader->Exposure.Set(mCameraExposure); { FFlatVertex *ptr = mVBO->GetBuffer(); ptr->Set(-1.0f, -1.0f, 0, 0.0f, 0.0f); ptr++; @@ -327,6 +343,7 @@ void FGLRenderer::BloomScene() glEnable(GL_SCISSOR_TEST); glBlendEquationSeparate(blendEquationRgb, blendEquationAlpha); glBlendFuncSeparate(blendSrcRgb, blendDestRgb, blendSrcAlpha, blendDestAlpha); + glUseProgram(currentProgram); } //----------------------------------------------------------------------------- @@ -337,6 +354,9 @@ void FGLRenderer::BloomScene() void FGLRenderer::TonemapScene() { + if (gl_tonemap == 0) + return; + GLboolean blendEnabled, scissorEnabled; glGetBooleanv(GL_BLEND, &blendEnabled); glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled); @@ -371,7 +391,7 @@ void FGLRenderer::TonemapScene() void FGLRenderer::Flush() { - if (FGLRenderBuffers::IsSupported()) + if (FGLRenderBuffers::IsEnabled()) { glDisable(GL_MULTISAMPLE); glDisable(GL_DEPTH_TEST); @@ -969,9 +989,6 @@ void FGLRenderer::EndDrawScene(sector_t * viewsector) gl_RenderState.ResetColor(); gl_RenderState.EnableTexture(true); glDisable(GL_SCISSOR_TEST); - - BloomScene(); - TonemapScene(); } @@ -1078,9 +1095,16 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo if (toscreen) { - float light = viewsector->lightlevel / 255.0f; - float exposure = MAX(1.0f + (1.0f - light * light) * 1.5f, 0.5f); - mCameraExposure = mCameraExposure * 0.98f + exposure * 0.02f; + if (gl_exposure == 0.0f) + { + float light = viewsector->lightlevel / 255.0f; + float exposure = MAX(1.0f + (1.0f - light * light) * 0.9f, 0.5f); + mCameraExposure = mCameraExposure * 0.995f + exposure * 0.005f; + } + else + { + mCameraExposure = gl_exposure; + } } // 'viewsector' will not survive the rendering so it cannot be used anymore below. @@ -1096,7 +1120,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo eye->SetUp(); // TODO: stereo specific viewport - needed when implementing side-by-side modes etc. SetOutputViewport(bounds); - Set3DViewport(); + Set3DViewport(toscreen); mDrawingScene2D = true; mCurrentFoV = fov; // Stereo mode specific perspective projection @@ -1115,6 +1139,11 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo ProcessScene(toscreen); if (mainview) EndDrawScene(retval); // do not call this for camera textures. + if (toscreen) + { + BloomScene(); + TonemapScene(); + } mDrawingScene2D = false; eye->TearDown(); } diff --git a/src/gl/shaders/gl_bloomshader.cpp b/src/gl/shaders/gl_bloomshader.cpp index 23cda96b73..c7191884fb 100644 --- a/src/gl/shaders/gl_bloomshader.cpp +++ b/src/gl/shaders/gl_bloomshader.cpp @@ -53,8 +53,8 @@ void FBloomExtractShader::Bind() { if (!mShader) { - mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/bloomextract.vp"); - mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/bloomextract.fp"); + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/bloomextract.vp", "", 330); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/bloomextract.fp", "", 330); mShader.SetFragDataLocation(0, "FragColor"); mShader.Link("shaders/glsl/bloomextract"); mShader.SetAttribLocation(0, "PositionInProjection"); @@ -68,8 +68,8 @@ void FBloomCombineShader::Bind() { if (!mShader) { - mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/bloomcombine.vp"); - mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/bloomcombine.fp"); + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/bloomcombine.vp", "", 330); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/bloomcombine.fp", "", 330); mShader.SetFragDataLocation(0, "FragColor"); mShader.Link("shaders/glsl/bloomcombine"); mShader.SetAttribLocation(0, "PositionInProjection"); diff --git a/src/gl/shaders/gl_blurshader.cpp b/src/gl/shaders/gl_blurshader.cpp index aee8696d99..14bcc28c51 100644 --- a/src/gl/shaders/gl_blurshader.cpp +++ b/src/gl/shaders/gl_blurshader.cpp @@ -129,8 +129,8 @@ FBlurShader::BlurSetup *FBlurShader::GetSetup(float blurAmount, int sampleCount) 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->Compile(FShaderProgram::Vertex, "vertical blur vertex shader", vertexCode, "", 330); + blurSetup.VerticalShader->Compile(FShaderProgram::Fragment, "vertical blur fragment shader", verticalCode, "", 330); blurSetup.VerticalShader->SetFragDataLocation(0, "FragColor"); blurSetup.VerticalShader->SetAttribLocation(0, "PositionInProjection"); blurSetup.VerticalShader->Link("vertical blur"); @@ -138,8 +138,8 @@ FBlurShader::BlurSetup *FBlurShader::GetSetup(float blurAmount, int sampleCount) glUniform1i(glGetUniformLocation(*blurSetup.VerticalShader.get(), "SourceTexture"), 0); 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->Compile(FShaderProgram::Vertex, "horizontal blur vertex shader", vertexCode, "", 330); + blurSetup.HorizontalShader->Compile(FShaderProgram::Fragment, "horizontal blur fragment shader", horizontalCode, "", 330); blurSetup.HorizontalShader->SetFragDataLocation(0, "FragColor"); blurSetup.HorizontalShader->SetAttribLocation(0, "PositionInProjection"); blurSetup.HorizontalShader->Link("horizontal blur"); @@ -160,8 +160,6 @@ FBlurShader::BlurSetup *FBlurShader::GetSetup(float blurAmount, int sampleCount) FString FBlurShader::VertexShaderCode() { return R"( - #version 330 - in vec4 PositionInProjection; out vec2 TexCoord; @@ -187,7 +185,6 @@ FString FBlurShader::FragmentShaderCode(float blurAmount, int sampleCount, bool const char *fragmentShader = R"( - #version 330 in vec2 TexCoord; uniform sampler2D SourceTexture; out vec4 FragColor; diff --git a/src/gl/shaders/gl_presentshader.cpp b/src/gl/shaders/gl_presentshader.cpp index a5d514308e..0ebfc99452 100644 --- a/src/gl/shaders/gl_presentshader.cpp +++ b/src/gl/shaders/gl_presentshader.cpp @@ -53,8 +53,8 @@ void FPresentShader::Bind() { if (!mShader) { - mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/present.vp"); - mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/present.fp"); + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/present.vp", "", 330); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/present.fp", "", 330); mShader.SetFragDataLocation(0, "FragColor"); mShader.Link("shaders/glsl/present"); mShader.SetAttribLocation(0, "PositionInProjection"); diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 551707398f..b5fd1dbbd2 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -56,48 +56,10 @@ #include "gl/renderer/gl_renderstate.h" #include "gl/system/gl_cvars.h" #include "gl/shaders/gl_shader.h" +#include "gl/shaders/gl_shaderprogram.h" #include "gl/textures/gl_material.h" #include "gl/dynlights/gl_lightbuffer.h" -//========================================================================== -// -// patch the shader source to work with -// GLSL 1.2 keywords and identifiers -// -//========================================================================== - -void PatchCommon(FString &code) -{ - code.Substitute("precision highp int;", ""); - code.Substitute("precision highp float;", ""); -} - -void PatchVertShader(FString &code) -{ - PatchCommon(code); - code.Substitute("in vec", "attribute vec"); - code.Substitute("out vec", "varying vec"); - code.Substitute("gl_ClipDistance", "//"); -} - -void PatchFragShader(FString &code) -{ - PatchCommon(code); - code.Substitute("out vec4 FragColor;", ""); - code.Substitute("FragColor", "gl_FragColor"); - code.Substitute("in vec", "varying vec"); - // this patches the switch statement to if's. - code.Substitute("break;", ""); - code.Substitute("switch (uFixedColormap)", "int i = uFixedColormap;"); - code.Substitute("case 0:", "if (i == 0)"); - code.Substitute("case 1:", "else if (i == 1)"); - code.Substitute("case 2:", "else if (i == 2)"); - code.Substitute("case 3:", "else if (i == 3)"); - code.Substitute("case 4:", "else if (i == 4)"); - code.Substitute("case 5:", "else if (i == 5)"); - code.Substitute("texture(", "texture2D("); -} - //========================================================================== // // @@ -204,8 +166,8 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * if (gl.glslversion < 1.3) { - PatchVertShader(vp_comb); - PatchFragShader(fp_comb); + FShaderProgram::PatchVertShader(vp_comb); + FShaderProgram::PatchFragShader(fp_comb); } hVertProg = glCreateShader(GL_VERTEX_SHADER); diff --git a/src/gl/shaders/gl_shaderprogram.cpp b/src/gl/shaders/gl_shaderprogram.cpp index 4448bdf594..bed4a2e295 100644 --- a/src/gl/shaders/gl_shaderprogram.cpp +++ b/src/gl/shaders/gl_shaderprogram.cpp @@ -93,22 +93,23 @@ void FShaderProgram::CreateShader(ShaderType type) // //========================================================================== -void FShaderProgram::Compile(ShaderType type, const char *lumpName) +void FShaderProgram::Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion) { int lump = Wads.CheckNumForFullName(lumpName); - if (lump == -1) I_Error("Unable to load '%s'", lumpName); + if (lump == -1) I_FatalError("Unable to load '%s'", lumpName); FString code = Wads.ReadLump(lump).GetString().GetChars(); - Compile(type, lumpName, code); + Compile(type, lumpName, code, defines, maxGlslVersion); } -void FShaderProgram::Compile(ShaderType type, const char *name, const FString &code) +void FShaderProgram::Compile(ShaderType type, const char *name, const FString &code, const char *defines, int maxGlslVersion) { CreateShader(type); const auto &handle = mShaders[type]; - int lengths[1] = { (int)code.Len() }; - const char *sources[1] = { code.GetChars() }; + FString patchedCode = PatchShader(type, code, defines, maxGlslVersion); + int lengths[1] = { (int)patchedCode.Len() }; + const char *sources[1] = { patchedCode.GetChars() }; glShaderSource(handle, 1, sources, lengths); glCompileShader(handle); @@ -117,7 +118,7 @@ void FShaderProgram::Compile(ShaderType type, const char *name, const FString &c glGetShaderiv(handle, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { - I_Error("Compile Shader '%s':\n%s\n", name, GetShaderInfoLog(handle).GetChars()); + I_FatalError("Compile Shader '%s':\n%s\n", name, GetShaderInfoLog(handle).GetChars()); } else { @@ -152,7 +153,7 @@ void FShaderProgram::Link(const char *name) glGetProgramiv(mProgram, GL_LINK_STATUS, &status); if (status == GL_FALSE) { - I_Error("Link Shader '%s':\n%s\n", name, GetProgramInfoLog(mProgram).GetChars()); + I_FatalError("Link Shader '%s':\n%s\n", name, GetProgramInfoLog(mProgram).GetChars()); } } @@ -207,3 +208,86 @@ FString FShaderProgram::GetProgramInfoLog(GLuint handle) glGetProgramInfoLog(handle, 10000, &length, buffer); return FString(buffer); } + +//========================================================================== +// +// Patches a shader to be compatible with the version of OpenGL in use +// +//========================================================================== + +FString FShaderProgram::PatchShader(ShaderType type, const FString &code, const char *defines, int maxGlslVersion) +{ + FString patchedCode; + + int shaderVersion = MIN((int)round(gl.glslversion * 10) * 10, maxGlslVersion); + patchedCode.AppendFormat("#version %d\n", shaderVersion); + + // TODO: Find some way to add extension requirements to the patching + // + // #extension GL_ARB_uniform_buffer_object : require + // #extension GL_ARB_shader_storage_buffer_object : require + + if (defines) + patchedCode << defines; + + if (gl.glslversion >= 1.3) + { + // these settings are actually pointless but there seem to be some old ATI drivers that fail to compile the shader without setting the precision here. + patchedCode << "precision highp int;\n"; + patchedCode << "precision highp float;\n"; + } + + patchedCode << "#line 1\n"; + patchedCode << code; + + if (gl.glslversion < 1.3) + { + if (type == Vertex) + PatchVertShader(patchedCode); + else if (type == Fragment) + PatchFragShader(patchedCode); + } + + return patchedCode; +} + +//========================================================================== +// +// patch the shader source to work with +// GLSL 1.2 keywords and identifiers +// +//========================================================================== + +void FShaderProgram::PatchCommon(FString &code) +{ + code.Substitute("precision highp int;", ""); + code.Substitute("precision highp float;", ""); +} + +void FShaderProgram::PatchVertShader(FString &code) +{ + PatchCommon(code); + code.Substitute("in vec", "attribute vec"); + code.Substitute("in float", "attribute float"); + code.Substitute("out vec", "varying vec"); + code.Substitute("out float", "varying float"); + code.Substitute("gl_ClipDistance", "//"); +} + +void FShaderProgram::PatchFragShader(FString &code) +{ + PatchCommon(code); + code.Substitute("out vec4 FragColor;", ""); + code.Substitute("FragColor", "gl_FragColor"); + code.Substitute("in vec", "varying vec"); + // this patches the switch statement to if's. + code.Substitute("break;", ""); + code.Substitute("switch (uFixedColormap)", "int i = uFixedColormap;"); + code.Substitute("case 0:", "if (i == 0)"); + code.Substitute("case 1:", "else if (i == 1)"); + code.Substitute("case 2:", "else if (i == 2)"); + code.Substitute("case 3:", "else if (i == 3)"); + code.Substitute("case 4:", "else if (i == 4)"); + code.Substitute("case 5:", "else if (i == 5)"); + code.Substitute("texture(", "texture2D("); +} diff --git a/src/gl/shaders/gl_shaderprogram.h b/src/gl/shaders/gl_shaderprogram.h index 8aebf16236..16684ddff5 100644 --- a/src/gl/shaders/gl_shaderprogram.h +++ b/src/gl/shaders/gl_shaderprogram.h @@ -15,8 +15,8 @@ public: NumShaderTypes }; - void Compile(ShaderType type, const char *lumpName); - void Compile(ShaderType type, const char *name, const FString &code); + void Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion); + void Compile(ShaderType type, const char *name, const FString &code, const char *defines, int maxGlslVersion); void SetFragDataLocation(int index, const char *name); void Link(const char *name); void SetAttribLocation(int index, const char *name); @@ -25,7 +25,14 @@ public: operator GLuint() const { return mProgram; } explicit operator bool() const { return mProgram != 0; } + // Needed by FShader + static void PatchVertShader(FString &code); + static void PatchFragShader(FString &code); + private: + static FString PatchShader(ShaderType type, const FString &code, const char *defines, int maxGlslVersion); + static void PatchCommon(FString &code); + void CreateShader(ShaderType type); FString GetShaderInfoLog(GLuint handle); FString GetProgramInfoLog(GLuint handle); diff --git a/src/gl/shaders/gl_tonemapshader.cpp b/src/gl/shaders/gl_tonemapshader.cpp index 44ea400128..c26e8d1af9 100644 --- a/src/gl/shaders/gl_tonemapshader.cpp +++ b/src/gl/shaders/gl_tonemapshader.cpp @@ -51,15 +51,28 @@ void FTonemapShader::Bind() { - if (!mShader) + auto &shader = mShader[gl_tonemap]; + if (!shader) { - mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/tonemap.vp"); - mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/tonemap.fp"); - mShader.SetFragDataLocation(0, "FragColor"); - mShader.Link("shaders/glsl/tonemap"); - mShader.SetAttribLocation(0, "PositionInProjection"); - SceneTexture.Init(mShader, "InputTexture"); - Exposure.Init(mShader, "ExposureAdjustment"); + shader.Compile(FShaderProgram::Vertex, "shaders/glsl/tonemap.vp", "", 330); + shader.Compile(FShaderProgram::Fragment, "shaders/glsl/tonemap.fp", GetDefines(gl_tonemap), 330); + shader.SetFragDataLocation(0, "FragColor"); + shader.Link("shaders/glsl/tonemap"); + shader.SetAttribLocation(0, "PositionInProjection"); + SceneTexture.Init(shader, "InputTexture"); + Exposure.Init(shader, "ExposureAdjustment"); + } + shader.Bind(); +} + +const char *FTonemapShader::GetDefines(int mode) +{ + switch (mode) + { + default: + case Linear: return "#define LINEAR\n"; + case Reinhard: return "#define REINHARD\n"; + case HejlDawson: return "#define HEJLDAWSON\n"; + case Uncharted2: return "#define UNCHARTED2\n"; } - mShader.Bind(); } diff --git a/src/gl/shaders/gl_tonemapshader.h b/src/gl/shaders/gl_tonemapshader.h index 0380e31b8b..846fdf6590 100644 --- a/src/gl/shaders/gl_tonemapshader.h +++ b/src/gl/shaders/gl_tonemapshader.h @@ -12,7 +12,19 @@ public: FBufferedUniform1f Exposure; private: - FShaderProgram mShader; + enum TonemapMode + { + None, + Uncharted2, + HejlDawson, + Reinhard, + Linear, + NumTonemapModes + }; + + static const char *GetDefines(int mode); + + FShaderProgram mShader[NumTonemapModes]; }; #endif \ No newline at end of file diff --git a/src/gl/system/gl_cvars.h b/src/gl/system/gl_cvars.h index cedeec06ae..27b588e115 100644 --- a/src/gl/system/gl_cvars.h +++ b/src/gl/system/gl_cvars.h @@ -41,4 +41,12 @@ EXTERN_CVAR(Bool, gl_seamless) EXTERN_CVAR(Float, gl_mask_threshold) EXTERN_CVAR(Float, gl_mask_sprite_threshold) +EXTERN_CVAR(Bool, gl_renderbuffers); + +EXTERN_CVAR(Bool, gl_bloom); +EXTERN_CVAR(Float, gl_bloom_amount) +EXTERN_CVAR(Int, gl_bloom_kernel_size) +EXTERN_CVAR(Int, gl_tonemap) +EXTERN_CVAR(Float, gl_exposure) + #endif // _GL_INTERN_H diff --git a/src/gl/system/gl_interface.cpp b/src/gl/system/gl_interface.cpp index 7d091ebb60..ced1b8b4ab 100644 --- a/src/gl/system/gl_interface.cpp +++ b/src/gl/system/gl_interface.cpp @@ -192,6 +192,9 @@ void gl_LoadExtensions() { if (CheckExtension("GL_NV_GPU_shader4") || CheckExtension("GL_EXT_GPU_shader4")) gl.glslversion = 1.21f; // for pre-3.0 drivers that support capable hardware. Needed for Apple. else gl.glslversion = 0; + + if (!CheckExtension("GL_EXT_packed_float")) gl.flags |= RFL_NO_RGBA16F; + if (!CheckExtension("GL_EXT_packed_depth_stencil")) gl.flags |= RFL_NO_DEPTHSTENCIL; } else if (gl.version < 4.f) { diff --git a/src/gl/system/gl_interface.h b/src/gl/system/gl_interface.h index 433c2327ce..1813e2c7ff 100644 --- a/src/gl/system/gl_interface.h +++ b/src/gl/system/gl_interface.h @@ -20,6 +20,9 @@ enum RenderFlags RFL_SHADER_STORAGE_BUFFER = 4, RFL_BUFFER_STORAGE = 8, RFL_SAMPLER_OBJECTS = 16, + + RFL_NO_RGBA16F = 32, + RFL_NO_DEPTHSTENCIL = 64 }; enum TexMode diff --git a/src/gl/system/gl_wipe.cpp b/src/gl/system/gl_wipe.cpp index 1f1dedba39..4c6922e4f4 100644 --- a/src/gl/system/gl_wipe.cpp +++ b/src/gl/system/gl_wipe.cpp @@ -156,7 +156,7 @@ bool OpenGLFrameBuffer::WipeStartScreen(int type) glFinish(); wipestartscreen->Bind(0, false, false); - if (FGLRenderBuffers::IsSupported()) + if (FGLRenderBuffers::IsEnabled()) { GLRenderer->mBuffers->BindHudFB(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height); @@ -192,7 +192,7 @@ void OpenGLFrameBuffer::WipeEndScreen() glFinish(); wipeendscreen->Bind(0, false, false); - if (FGLRenderBuffers::IsSupported()) + if (FGLRenderBuffers::IsEnabled()) GLRenderer->mBuffers->BindHudFB(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height); @@ -230,7 +230,7 @@ bool OpenGLFrameBuffer::WipeDo(int ticks) glDisable(GL_DEPTH_TEST); glDepthMask(false); - if (FGLRenderBuffers::IsSupported()) + if (FGLRenderBuffers::IsEnabled()) { GLRenderer->mBuffers->BindHudFB(); glViewport(0, 0, GLRenderer->mOutputViewport.width, GLRenderer->mOutputViewport.height); diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 30b0f542fd..df47661a4c 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2618,6 +2618,8 @@ GLPREFMNU_AMBLIGHT = "Ambient light level"; GLPREFMNU_RENDERQUALITY = "Rendering quality"; GLPREFMNU_VRMODE = "Stereo 3D VR"; GLPREFMNU_VRQUADSTEREO = "Enable Quad Stereo"; +GLPREFMNU_TONEMAP = "Tonemap Mode"; +GLPREFMNU_BLOOM = "Bloom effect"; // Option Values OPTVAL_SMART = "Smart"; @@ -2686,3 +2688,6 @@ OPTVAL_AMBERBLUE = "Amber/Blue"; OPTVAL_LEFTEYE = "Left Eye"; OPTVAL_RIGHTEYE = "Right Eye"; OPTVAL_QUADBUFFERED = "Quad-buffered"; +OPTVAL_UNCHARTED2 = "Uncharted 2"; +OPTVAL_HEJLDAWSON = "Hejl Dawson"; +OPTVAL_REINHARD = "Reinhard"; \ No newline at end of file diff --git a/wadsrc/static/menudef.z b/wadsrc/static/menudef.z index e96f6b55c3..d49197dad2 100644 --- a/wadsrc/static/menudef.z +++ b/wadsrc/static/menudef.z @@ -32,6 +32,15 @@ OptionValue "HWGammaModes" 2, "$OPTVAL_FULLSCREENONLY" } +OptionValue "TonemapModes" +{ + 0, "$OPTVAL_OFF" + 1, "$OPTVAL_UNCHARTED2" + 2, "$OPTVAL_HEJLDAWSON" + 3, "$OPTVAL_REINHARD" + 4, "$OPTVAL_LINEAR" +} + OptionValue "TextureFormats" { 0, "$OPTVAL_RGBA8" @@ -198,4 +207,6 @@ OptionMenu "GLPrefOptions" Option "$GLPREFMNU_RENDERQUALITY", gl_render_precise, "Precision" Option "$GLPREFMNU_VRMODE", vr_mode, "VRMode" Option "$GLPREFMNU_VRQUADSTEREO", vr_enable_quadbuffered, "OnOff" + Option "$GLPREFMNU_TONEMAP", gl_tonemap, "TonemapModes" + Option "$GLPREFMNU_BLOOM", gl_bloom, "OnOff" } diff --git a/wadsrc/static/shaders/glsl/bloomcombine.fp b/wadsrc/static/shaders/glsl/bloomcombine.fp index 0db4802f0a..57496771c1 100644 --- a/wadsrc/static/shaders/glsl/bloomcombine.fp +++ b/wadsrc/static/shaders/glsl/bloomcombine.fp @@ -1,6 +1,4 @@ -#version 330 - in vec2 TexCoord; out vec4 FragColor; diff --git a/wadsrc/static/shaders/glsl/bloomcombine.vp b/wadsrc/static/shaders/glsl/bloomcombine.vp index b73fe7fc81..5990669a51 100644 --- a/wadsrc/static/shaders/glsl/bloomcombine.vp +++ b/wadsrc/static/shaders/glsl/bloomcombine.vp @@ -1,6 +1,4 @@ -#version 330 - in vec4 PositionInProjection; out vec2 TexCoord; diff --git a/wadsrc/static/shaders/glsl/bloomextract.fp b/wadsrc/static/shaders/glsl/bloomextract.fp index b6d663db66..dc753ce498 100644 --- a/wadsrc/static/shaders/glsl/bloomextract.fp +++ b/wadsrc/static/shaders/glsl/bloomextract.fp @@ -1,6 +1,4 @@ -#version 330 - in vec2 TexCoord; out vec4 FragColor; diff --git a/wadsrc/static/shaders/glsl/bloomextract.vp b/wadsrc/static/shaders/glsl/bloomextract.vp index b73fe7fc81..5990669a51 100644 --- a/wadsrc/static/shaders/glsl/bloomextract.vp +++ b/wadsrc/static/shaders/glsl/bloomextract.vp @@ -1,6 +1,4 @@ -#version 330 - in vec4 PositionInProjection; out vec2 TexCoord; diff --git a/wadsrc/static/shaders/glsl/present.fp b/wadsrc/static/shaders/glsl/present.fp index 93534ba7f7..4a3f418404 100644 --- a/wadsrc/static/shaders/glsl/present.fp +++ b/wadsrc/static/shaders/glsl/present.fp @@ -1,6 +1,4 @@ -#version 330 - in vec2 TexCoord; out vec4 FragColor; diff --git a/wadsrc/static/shaders/glsl/present.vp b/wadsrc/static/shaders/glsl/present.vp index b73fe7fc81..5990669a51 100644 --- a/wadsrc/static/shaders/glsl/present.vp +++ b/wadsrc/static/shaders/glsl/present.vp @@ -1,6 +1,4 @@ -#version 330 - in vec4 PositionInProjection; out vec2 TexCoord; diff --git a/wadsrc/static/shaders/glsl/tonemap.fp b/wadsrc/static/shaders/glsl/tonemap.fp index e1e3d3497c..35388b7b8c 100644 --- a/wadsrc/static/shaders/glsl/tonemap.fp +++ b/wadsrc/static/shaders/glsl/tonemap.fp @@ -1,6 +1,4 @@ -#version 330 - in vec2 TexCoord; out vec4 FragColor; @@ -19,23 +17,31 @@ vec3 sRGB(vec3 c) return sqrt(c); // cheaper, but assuming gamma of 2.0 instead of 2.2 } -vec3 TonemapLinear(vec3 color) +#if defined(LINEAR) + +vec3 Tonemap(vec3 color) { return sRGB(color); } -vec3 TonemapReinhard(vec3 color) +#elif defined(REINHARD) + +vec3 Tonemap(vec3 color) { color = color / (1 + color); return sRGB(color); } -vec3 TonemapHejlDawson(vec3 color) +#elif defined(HEJLDAWSON) + +vec3 Tonemap(vec3 color) { vec3 x = max(vec3(0), color - 0.004); return (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06); // no sRGB needed } +#else + vec3 Uncharted2Tonemap(vec3 x) { float A = 0.15; @@ -47,7 +53,7 @@ vec3 Uncharted2Tonemap(vec3 x) return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; } -vec3 TonemapUncharted2(vec3 color) +vec3 Tonemap(vec3 color) { float W = 11.2; float ExposureBias = 2.0; @@ -56,10 +62,12 @@ vec3 TonemapUncharted2(vec3 color) return sRGB(curr * whiteScale); } +#endif + void main() { vec3 color = texture(InputTexture, TexCoord).rgb; color = color * ExposureAdjustment; color = Linear(color); // needed because gzdoom's scene texture is not linear at the moment - FragColor = vec4(TonemapUncharted2(color), 1.0); + FragColor = vec4(Tonemap(color), 1.0); } diff --git a/wadsrc/static/shaders/glsl/tonemap.vp b/wadsrc/static/shaders/glsl/tonemap.vp index b73fe7fc81..5990669a51 100644 --- a/wadsrc/static/shaders/glsl/tonemap.vp +++ b/wadsrc/static/shaders/glsl/tonemap.vp @@ -1,6 +1,4 @@ -#version 330 - in vec4 PositionInProjection; out vec2 TexCoord; From 9bfce5b6eafa5ddadc027d6567f43fb1dcf9ef2c Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sat, 30 Jul 2016 00:02:26 +0200 Subject: [PATCH 4/4] Moved post processing effects to its own file --- src/CMakeLists.txt | 1 + src/gl/renderer/gl_postprocess.cpp | 346 +++++++++++++++++++++++++++++ src/gl/scene/gl_scene.cpp | 273 ----------------------- 3 files changed, 347 insertions(+), 273 deletions(-) create mode 100644 src/gl/renderer/gl_postprocess.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6009a2c375..d3d339e0f2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1062,6 +1062,7 @@ set( FASTMATH_SOURCES gl/renderer/gl_renderstate.cpp gl/renderer/gl_renderbuffers.cpp gl/renderer/gl_lightdata.cpp + gl/renderer/gl_postprocess.cpp gl/hqnx/init.cpp gl/hqnx/hq2x.cpp gl/hqnx/hq3x.cpp diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp new file mode 100644 index 0000000000..c68cd6531f --- /dev/null +++ b/src/gl/renderer/gl_postprocess.cpp @@ -0,0 +1,346 @@ +/* +** gl_postprocess.cpp +** Post processing effects in the render pipeline +** +**--------------------------------------------------------------------------- +** 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 "gi.h" +#include "m_png.h" +#include "m_random.h" +#include "st_stuff.h" +#include "dobject.h" +#include "doomstat.h" +#include "g_level.h" +#include "r_data/r_interpolate.h" +#include "r_utility.h" +#include "d_player.h" +#include "p_effect.h" +#include "sbar.h" +#include "po_man.h" +#include "r_utility.h" +#include "a_hexenglobal.h" +#include "p_local.h" +#include "gl/gl_functions.h" +#include "gl/system/gl_interface.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/renderer/gl_renderbuffers.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/data/gl_data.h" +#include "gl/data/gl_vertexbuffer.h" +#include "gl/shaders/gl_bloomshader.h" +#include "gl/shaders/gl_blurshader.h" +#include "gl/shaders/gl_tonemapshader.h" +#include "gl/shaders/gl_presentshader.h" + +//========================================================================== +// +// CVARs +// +//========================================================================== +CVAR(Bool, gl_bloom, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); +CVAR(Float, gl_bloom_amount, 1.4f, 0) +CVAR(Float, gl_exposure, 0.0f, 0) + +CUSTOM_CVAR(Int, gl_tonemap, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (self < 0 || self > 4) + self = 0; +} + +CUSTOM_CVAR(Int, gl_bloom_kernel_size, 7, 0) +{ + if (self < 3 || self > 15 || self % 2 == 0) + self = 7; +} + +EXTERN_CVAR(Float, vid_brightness) +EXTERN_CVAR(Float, vid_contrast) + +//----------------------------------------------------------------------------- +// +// Adds bloom contribution to scene texture +// +//----------------------------------------------------------------------------- + +void FGLRenderer::BloomScene() +{ + // Only bloom things if enabled and no special fixed light mode is active + if (!gl_bloom || !FGLRenderBuffers::IsEnabled() || gl_fixedcolormap != CM_DEFAULT) + return; + + const float blurAmount = gl_bloom_amount; + int sampleCount = gl_bloom_kernel_size; + + // TBD: Maybe 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(mCameraExposure); + { + FFlatVertex *ptr = mVBO->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++; + mVBO->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(mVBO, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height); + mBlurShader->BlurVertical(mVBO, 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(mVBO, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height); + mBlurShader->BlurVertical(mVBO, 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 = mVBO->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++; + mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP); + } + } + + mBlurShader->BlurHorizontal(mVBO, blurAmount, sampleCount, level0.VTexture, level0.HFramebuffer, level0.Width, level0.Height); + mBlurShader->BlurVertical(mVBO, 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 = mVBO->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++; + mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP); + } + + if (blendEnabled) + glEnable(GL_BLEND); + if (scissorEnabled) + glEnable(GL_SCISSOR_TEST); + glBlendEquationSeparate(blendEquationRgb, blendEquationAlpha); + glBlendFuncSeparate(blendSrcRgb, blendDestRgb, blendSrcAlpha, blendDestAlpha); + glUseProgram(currentProgram); +} + +//----------------------------------------------------------------------------- +// +// Tonemap scene texture and place the result in the HUD/2D texture +// +//----------------------------------------------------------------------------- + +void FGLRenderer::TonemapScene() +{ + if (gl_tonemap == 0) + return; + + GLboolean blendEnabled, scissorEnabled; + glGetBooleanv(GL_BLEND, &blendEnabled); + glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled); + + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); + + mBuffers->BindHudFB(); + mBuffers->BindSceneTexture(0); + mTonemapShader->Bind(); + mTonemapShader->SceneTexture.Set(0); + mTonemapShader->Exposure.Set(mCameraExposure); + + FFlatVertex *ptr = mVBO->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++; + mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP); + + if (blendEnabled) + glEnable(GL_BLEND); + if (scissorEnabled) + glEnable(GL_SCISSOR_TEST); +} + +//----------------------------------------------------------------------------- +// +// Gamma correct while copying to frame buffer +// +//----------------------------------------------------------------------------- + +void FGLRenderer::Flush() +{ + if (FGLRenderBuffers::IsEnabled()) + { + glDisable(GL_MULTISAMPLE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + + mBuffers->BindOutputFB(); + + // Calculate letterbox + int clientWidth = framebuffer->GetClientWidth(); + int clientHeight = framebuffer->GetClientHeight(); + float scaleX = clientWidth / (float)mOutputViewport.width; + float scaleY = clientHeight / (float)mOutputViewport.height; + float scale = MIN(scaleX, scaleY); + int width = (int)round(mOutputViewport.width * scale); + int height = (int)round(mOutputViewport.height * scale); + int x = (clientWidth - width) / 2; + int y = (clientHeight - height) / 2; + + // Black bars around the box: + glViewport(0, 0, clientWidth, clientHeight); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glEnable(GL_SCISSOR_TEST); + if (y > 0) + { + glScissor(0, 0, clientWidth, y); + glClear(GL_COLOR_BUFFER_BIT); + } + if (clientHeight - y - height > 0) + { + glScissor(0, y + height, clientWidth, clientHeight - y - height); + glClear(GL_COLOR_BUFFER_BIT); + } + if (x > 0) + { + glScissor(0, y, x, height); + glClear(GL_COLOR_BUFFER_BIT); + } + if (clientWidth - x - width > 0) + { + glScissor(x + width, y, clientWidth - x - width, height); + glClear(GL_COLOR_BUFFER_BIT); + } + glDisable(GL_SCISSOR_TEST); + + // Present what was rendered: + glViewport(x, y, width, height); + + GLboolean blendEnabled; + GLint currentProgram; + glGetBooleanv(GL_BLEND, &blendEnabled); + glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram); + glDisable(GL_BLEND); + + mPresentShader->Bind(); + mPresentShader->InputTexture.Set(0); + if (framebuffer->IsHWGammaActive()) + { + mPresentShader->Gamma.Set(1.0f); + mPresentShader->Contrast.Set(1.0f); + mPresentShader->Brightness.Set(0.0f); + } + else + { + mPresentShader->Gamma.Set(clamp(Gamma, 0.1f, 4.f)); + mPresentShader->Contrast.Set(clamp(vid_contrast, 0.1f, 3.f)); + mPresentShader->Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f)); + } + mBuffers->BindHudTexture(0); + + FFlatVertex *ptr = GLRenderer->mVBO->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++; + GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP); + + if (blendEnabled) + glEnable(GL_BLEND); + glUseProgram(currentProgram); + } +} diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index c23992f1b6..aea69d9416 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -72,10 +72,6 @@ #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_tonemapshader.h" -#include "gl/shaders/gl_presentshader.h" #include "gl/stereo3d/gl_stereo3d.h" #include "gl/stereo3d/scoped_view_shifter.h" #include "gl/textures/gl_translate.h" @@ -96,26 +92,8 @@ CVAR(Float, gl_mask_threshold, 0.5f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Float, gl_mask_sprite_threshold, 0.5f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, gl_sort_textures, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR(Bool, gl_bloom, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); -CVAR(Float, gl_bloom_amount, 1.4f, 0) -CVAR(Float, gl_exposure, 0.0f, 0) - -CUSTOM_CVAR(Int, gl_tonemap, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (self < 0 || self > 4) - self = 0; -} - -CUSTOM_CVAR(Int, gl_bloom_kernel_size, 7, 0) -{ - if (self < 3 || self > 15 || self % 2 == 0) - self = 7; -} - EXTERN_CVAR (Bool, cl_capfps) EXTERN_CVAR (Bool, r_deathcamera) -EXTERN_CVAR(Float, vid_brightness) -EXTERN_CVAR(Float, vid_contrast) extern int viewpitch; @@ -223,257 +201,6 @@ void FGLRenderer::Set3DViewport(bool toscreen) glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); } -//----------------------------------------------------------------------------- -// -// Adds bloom contribution to scene texture -// -//----------------------------------------------------------------------------- - -void FGLRenderer::BloomScene() -{ - // Only bloom things if enabled and no special fixed light mode is active - if (!gl_bloom || !FGLRenderBuffers::IsEnabled() || gl_fixedcolormap != CM_DEFAULT) - return; - - const float blurAmount = gl_bloom_amount; - int sampleCount = gl_bloom_kernel_size; - - // TBD: Maybe 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(mCameraExposure); - { - FFlatVertex *ptr = mVBO->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++; - mVBO->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(mVBO, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height); - mBlurShader->BlurVertical(mVBO, 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(mVBO, blurAmount, sampleCount, level.VTexture, level.HFramebuffer, level.Width, level.Height); - mBlurShader->BlurVertical(mVBO, 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 = mVBO->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++; - mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP); - } - } - - mBlurShader->BlurHorizontal(mVBO, blurAmount, sampleCount, level0.VTexture, level0.HFramebuffer, level0.Width, level0.Height); - mBlurShader->BlurVertical(mVBO, 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 = mVBO->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++; - mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP); - } - - if (blendEnabled) - glEnable(GL_BLEND); - if (scissorEnabled) - glEnable(GL_SCISSOR_TEST); - glBlendEquationSeparate(blendEquationRgb, blendEquationAlpha); - glBlendFuncSeparate(blendSrcRgb, blendDestRgb, blendSrcAlpha, blendDestAlpha); - glUseProgram(currentProgram); -} - -//----------------------------------------------------------------------------- -// -// Tonemap scene texture and place the result in the HUD/2D texture -// -//----------------------------------------------------------------------------- - -void FGLRenderer::TonemapScene() -{ - if (gl_tonemap == 0) - return; - - GLboolean blendEnabled, scissorEnabled; - glGetBooleanv(GL_BLEND, &blendEnabled); - glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled); - - glDisable(GL_BLEND); - glDisable(GL_SCISSOR_TEST); - - mBuffers->BindHudFB(); - mBuffers->BindSceneTexture(0); - mTonemapShader->Bind(); - mTonemapShader->SceneTexture.Set(0); - mTonemapShader->Exposure.Set(mCameraExposure); - - FFlatVertex *ptr = mVBO->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++; - mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP); - - if (blendEnabled) - glEnable(GL_BLEND); - if (scissorEnabled) - glEnable(GL_SCISSOR_TEST); -} - -//----------------------------------------------------------------------------- -// -// Gamma correct while copying to frame buffer -// -//----------------------------------------------------------------------------- - -void FGLRenderer::Flush() -{ - if (FGLRenderBuffers::IsEnabled()) - { - glDisable(GL_MULTISAMPLE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_STENCIL_TEST); - - mBuffers->BindOutputFB(); - - // Calculate letterbox - int clientWidth = framebuffer->GetClientWidth(); - int clientHeight = framebuffer->GetClientHeight(); - float scaleX = clientWidth / (float)mOutputViewport.width; - float scaleY = clientHeight / (float)mOutputViewport.height; - float scale = MIN(scaleX, scaleY); - int width = (int)round(mOutputViewport.width * scale); - int height = (int)round(mOutputViewport.height * scale); - int x = (clientWidth - width) / 2; - int y = (clientHeight - height) / 2; - - // Black bars around the box: - glViewport(0, 0, clientWidth, clientHeight); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glEnable(GL_SCISSOR_TEST); - if (y > 0) - { - glScissor(0, 0, clientWidth, y); - glClear(GL_COLOR_BUFFER_BIT); - } - if (clientHeight - y - height > 0) - { - glScissor(0, y + height, clientWidth, clientHeight - y - height); - glClear(GL_COLOR_BUFFER_BIT); - } - if (x > 0) - { - glScissor(0, y, x, height); - glClear(GL_COLOR_BUFFER_BIT); - } - if (clientWidth - x - width > 0) - { - glScissor(x + width, y, clientWidth - x - width, height); - glClear(GL_COLOR_BUFFER_BIT); - } - glDisable(GL_SCISSOR_TEST); - - // Present what was rendered: - glViewport(x, y, width, height); - - GLboolean blendEnabled; - GLint currentProgram; - glGetBooleanv(GL_BLEND, &blendEnabled); - glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram); - glDisable(GL_BLEND); - - mPresentShader->Bind(); - mPresentShader->InputTexture.Set(0); - if (framebuffer->IsHWGammaActive()) - { - mPresentShader->Gamma.Set(1.0f); - mPresentShader->Contrast.Set(1.0f); - mPresentShader->Brightness.Set(0.0f); - } - else - { - mPresentShader->Gamma.Set(clamp(Gamma, 0.1f, 4.f)); - mPresentShader->Contrast.Set(clamp(vid_contrast, 0.1f, 3.f)); - mPresentShader->Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f)); - } - mBuffers->BindHudTexture(0); - - FFlatVertex *ptr = GLRenderer->mVBO->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++; - GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP); - - if (blendEnabled) - glEnable(GL_BLEND); - glUseProgram(currentProgram); - } -} - //----------------------------------------------------------------------------- // // Setup the camera position