diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cad3299628..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 @@ -1106,6 +1107,9 @@ 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/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_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/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index 26377553f0..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 @@ -72,12 +74,51 @@ FGLRenderBuffers::FGLRenderBuffers() FGLRenderBuffers::~FGLRenderBuffers() { - if (mSceneFB != 0) - glDeleteFramebuffers(1, &mSceneFB); - if (mSceneTexture != 0) - glDeleteTextures(1, &mSceneTexture); - if (mSceneDepthStencil != 0) - glDeleteRenderbuffers(1, &mSceneDepthStencil); + Clear(); +} + +void FGLRenderBuffers::Clear() +{ + for (int i = 0; i < NumBloomLevels; i++) + { + auto &level = BloomLevels[i]; + DeleteFrameBuffer(level.HFramebuffer); + DeleteFrameBuffer(level.VFramebuffer); + DeleteTexture(level.HTexture); + DeleteTexture(level.VTexture); + level = FGLBloomTextureLevel(); + } + + 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; } //========================================================================== @@ -92,38 +133,124 @@ 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(); - glGenFramebuffers(1, &mSceneFB); - glGenTextures(1, &mSceneTexture); - glGenRenderbuffers(1, &mSceneDepthStencil); + GLuint hdrFormat = ((gl.flags & RFL_NO_RGBA16F) != 0) ? GL_RGBA8 : GL_RGBA16F; - 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); + 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); + } + + mHudTexture = Create2DTexture(hdrFormat, width, height); + mHudFB = CreateFrameBuffer(mHudTexture); + + 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); + + 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, 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); glBindFramebuffer(GL_FRAMEBUFFER, mOutputFB); mWidth = width; 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 @@ -135,6 +262,20 @@ void FGLRenderBuffers::BindSceneFB() glBindFramebuffer(GL_FRAMEBUFFER, mSceneFB); } +//========================================================================== +// +// Makes the 2D/HUD frame buffer active +// +//========================================================================== + +void FGLRenderBuffers::BindHudFB() +{ + if (gl_tonemap != 0) + glBindFramebuffer(GL_FRAMEBUFFER, mHudFB); + else + glBindFramebuffer(GL_FRAMEBUFFER, mSceneFB); +} + //========================================================================== // // Makes the screen frame buffer active @@ -154,6 +295,32 @@ void FGLRenderBuffers::BindOutputFB() void FGLRenderBuffers::BindSceneTexture(int index) { - glActiveTexture(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); + 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 bbb38b533a..ba5e94ab33 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: @@ -11,18 +22,37 @@ 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; } + 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; GLuint mOutputFB = 0; }; diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 6c1677b7b3..a1d8bb2a73 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -62,6 +62,9 @@ #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_tonemapshader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/textures/gl_texture.h" #include "gl/textures/gl_translate.h" @@ -110,6 +113,10 @@ void gl_FlushModels(); void FGLRenderer::Initialize() { mBuffers = new FGLRenderBuffers(); + 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. @@ -160,6 +167,10 @@ FGLRenderer::~FGLRenderer() } if (mBuffers) delete mBuffers; if (mPresentShader) delete mPresentShader; + if (mBloomExtractShader) delete mBloomExtractShader; + if (mBloomCombineShader) delete mBloomCombineShader; + if (mBlurShader) delete mBlurShader; + if (mTonemapShader) delete mTonemapShader; } //========================================================================== @@ -224,10 +235,13 @@ void FGLRenderer::SetupLevel() void FGLRenderer::Begin2D() { - if (FGLRenderBuffers::IsSupported()) + if (FGLRenderBuffers::IsEnabled()) { 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 9920765287..ad2046cad4 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -19,6 +19,10 @@ class FLightBuffer; class FSamplerManager; class DPSprite; class FGLRenderBuffers; +class FBloomExtractShader; +class FBloomCombineShader; +class FBlurShader; +class FTonemapShader; class FPresentShader; inline float DEG2RAD(float deg) @@ -82,6 +86,10 @@ public: int mOldFBID; FGLRenderBuffers *mBuffers; + FBloomExtractShader *mBloomExtractShader; + FBloomCombineShader *mBloomCombineShader; + FBlurShader *mBlurShader; + FTonemapShader *mTonemapShader; FPresentShader *mPresentShader; FTexture *gllight; @@ -100,6 +108,8 @@ public: GL_IRECT mOutputViewportLB; GL_IRECT mOutputViewport; + bool mDrawingScene2D = false; + float mCameraExposure = 1.0f; FGLRenderer(OpenGLFrameBuffer *fb); ~FGLRenderer() ; @@ -107,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); @@ -147,6 +157,8 @@ 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 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 3f52ce2b13..aea69d9416 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -72,7 +72,6 @@ #include "gl/scene/gl_drawinfo.h" #include "gl/scene/gl_portal.h" #include "gl/shaders/gl_shader.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" @@ -95,8 +94,6 @@ CVAR(Bool, gl_sort_textures, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) EXTERN_CVAR (Bool, cl_capfps) EXTERN_CVAR (Bool, r_deathcamera) -EXTERN_CVAR(Float, vid_brightness) -EXTERN_CVAR(Float, vid_contrast) extern int viewpitch; @@ -160,7 +157,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); @@ -172,10 +169,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(); @@ -204,97 +201,6 @@ void FGLRenderer::Set3DViewport() glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); } -//----------------------------------------------------------------------------- -// -// Run post processing steps and copy to frame buffer -// -//----------------------------------------------------------------------------- - -void FGLRenderer::Flush() -{ - if (FGLRenderBuffers::IsSupported()) - { - 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->BindSceneTexture(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 @@ -914,6 +820,20 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo mViewActor=camera; } + if (toscreen) + { + 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. retval = viewsector; @@ -927,7 +847,8 @@ 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 SetProjection( eye->GetProjection(fov, ratio, fovratio) ); @@ -945,6 +866,12 @@ 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(); } stereo3dMode.TearDown(); diff --git a/src/gl/shaders/gl_bloomshader.cpp b/src/gl/shaders/gl_bloomshader.cpp new file mode 100644 index 0000000000..c7191884fb --- /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", "", 330); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/bloomextract.fp", "", 330); + 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", "", 330); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/bloomcombine.fp", "", 330); + 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..14bcc28c51 --- /dev/null +++ b/src/gl/shaders/gl_blurshader.cpp @@ -0,0 +1,257 @@ +/* +** 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) +{ + BlurSetup *setup = GetSetup(blurAmount, sampleCount); + if (vertical) + setup->VerticalShader->Bind(); + else + setup->HorizontalShader->Bind(); + + 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); + + glBindFramebuffer(GL_FRAMEBUFFER, outputFrameBuffer); + glViewport(0, 0, width, height); + glDisable(GL_BLEND); + + 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); +} + +//========================================================================== +// +// 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, "", 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"); + blurSetup.VerticalShader->Bind(); + glUniform1i(glGetUniformLocation(*blurSetup.VerticalShader.get(), "SourceTexture"), 0); + + blurSetup.HorizontalShader = std::make_shared(); + 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"); + 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"( + 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"( + 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_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 35fd468aee..bed4a2e295 100644 --- a/src/gl/shaders/gl_shaderprogram.cpp +++ b/src/gl/shaders/gl_shaderprogram.cpp @@ -93,18 +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_FatalError("Unable to load '%s'", lumpName); + FString code = Wads.ReadLump(lump).GetString().GetChars(); + Compile(type, lumpName, code, defines, maxGlslVersion); +} + +void FShaderProgram::Compile(ShaderType type, const char *name, const FString &code, const char *defines, int maxGlslVersion) { 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(); - - 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); @@ -113,7 +118,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_FatalError("Compile Shader '%s':\n%s\n", name, GetShaderInfoLog(handle).GetChars()); } else { @@ -148,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()); } } @@ -203,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 9cc3315259..16684ddff5 100644 --- a/src/gl/shaders/gl_shaderprogram.h +++ b/src/gl/shaders/gl_shaderprogram.h @@ -15,7 +15,8 @@ public: NumShaderTypes }; - void Compile(ShaderType type, const char *lumpName); + 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); @@ -24,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 new file mode 100644 index 0000000000..c26e8d1af9 --- /dev/null +++ b/src/gl/shaders/gl_tonemapshader.cpp @@ -0,0 +1,78 @@ +/* +** 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() +{ + auto &shader = mShader[gl_tonemap]; + if (!shader) + { + 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"; + } +} diff --git a/src/gl/shaders/gl_tonemapshader.h b/src/gl/shaders/gl_tonemapshader.h new file mode 100644 index 0000000000..846fdf6590 --- /dev/null +++ b/src/gl/shaders/gl_tonemapshader.h @@ -0,0 +1,30 @@ +#ifndef __GL_TONEMAPSHADER_H +#define __GL_TONEMAPSHADER_H + +#include "gl_shaderprogram.h" + +class FTonemapShader +{ +public: + void Bind(); + + FBufferedUniform1i SceneTexture; + FBufferedUniform1f Exposure; + +private: + 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 6b15e0b6d6..4c6922e4f4 100644 --- a/src/gl/system/gl_wipe.cpp +++ b/src/gl/system/gl_wipe.cpp @@ -156,9 +156,9 @@ bool OpenGLFrameBuffer::WipeStartScreen(int type) glFinish(); wipestartscreen->Bind(0, false, false); - if (FGLRenderBuffers::IsSupported()) + if (FGLRenderBuffers::IsEnabled()) { - GLRenderer->mBuffers->BindSceneFB(); + GLRenderer->mBuffers->BindHudFB(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height); } else @@ -192,8 +192,8 @@ void OpenGLFrameBuffer::WipeEndScreen() glFinish(); wipeendscreen->Bind(0, false, false); - if (FGLRenderBuffers::IsSupported()) - GLRenderer->mBuffers->BindSceneFB(); + if (FGLRenderBuffers::IsEnabled()) + GLRenderer->mBuffers->BindHudFB(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -230,9 +230,9 @@ bool OpenGLFrameBuffer::WipeDo(int ticks) glDisable(GL_DEPTH_TEST); glDepthMask(false); - if (FGLRenderBuffers::IsSupported()) + if (FGLRenderBuffers::IsEnabled()) { - GLRenderer->mBuffers->BindSceneFB(); + 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 new file mode 100644 index 0000000000..57496771c1 --- /dev/null +++ b/wadsrc/static/shaders/glsl/bloomcombine.fp @@ -0,0 +1,10 @@ + +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..5990669a51 --- /dev/null +++ b/wadsrc/static/shaders/glsl/bloomcombine.vp @@ -0,0 +1,9 @@ + +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..dc753ce498 --- /dev/null +++ b/wadsrc/static/shaders/glsl/bloomextract.fp @@ -0,0 +1,12 @@ + +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..5990669a51 --- /dev/null +++ b/wadsrc/static/shaders/glsl/bloomextract.vp @@ -0,0 +1,9 @@ + +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/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 new file mode 100644 index 0000000000..35388b7b8c --- /dev/null +++ b/wadsrc/static/shaders/glsl/tonemap.fp @@ -0,0 +1,73 @@ + +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 +} + +#if defined(LINEAR) + +vec3 Tonemap(vec3 color) +{ + return sRGB(color); +} + +#elif defined(REINHARD) + +vec3 Tonemap(vec3 color) +{ + color = color / (1 + color); + return sRGB(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; + 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 Tonemap(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); +} + +#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(Tonemap(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..5990669a51 --- /dev/null +++ b/wadsrc/static/shaders/glsl/tonemap.vp @@ -0,0 +1,9 @@ + +in vec4 PositionInProjection; +out vec2 TexCoord; + +void main() +{ + gl_Position = PositionInProjection; + TexCoord = PositionInProjection.xy * 0.5 + 0.5; +}