diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d3d339e0f..5317c277a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1110,6 +1110,7 @@ set( FASTMATH_SOURCES gl/shaders/gl_bloomshader.cpp gl/shaders/gl_blurshader.cpp gl/shaders/gl_tonemapshader.cpp + gl/shaders/gl_lensshader.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 index a8e5867c9..155321de4 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -68,6 +68,7 @@ #include "gl/shaders/gl_bloomshader.h" #include "gl/shaders/gl_blurshader.h" #include "gl/shaders/gl_tonemapshader.h" +#include "gl/shaders/gl_lensshader.h" #include "gl/shaders/gl_presentshader.h" //========================================================================== @@ -95,6 +96,12 @@ CUSTOM_CVAR(Int, gl_bloom_kernel_size, 7, 0) self = 7; } +CVAR(Bool, gl_lens, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +CVAR(Float, gl_lens_k, -0.12f, 0) +CVAR(Float, gl_lens_kcube, 0.1f, 0) +CVAR(Float, gl_lens_chromatic, 1.12f, 0) + EXTERN_CVAR(Float, vid_brightness) EXTERN_CVAR(Float, vid_contrast) @@ -143,7 +150,7 @@ void FGLRenderer::BloomScene() // Extract blooming pixels from scene texture: glBindFramebuffer(GL_FRAMEBUFFER, level0.VFramebuffer); glViewport(0, 0, level0.Width, level0.Height); - mBuffers->BindSceneTexture(0); + mBuffers->BindCurrentTexture(0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); mBloomExtractShader->Bind(); @@ -188,7 +195,7 @@ void FGLRenderer::BloomScene() mBlurShader->BlurVertical(mVBO, blurAmount, sampleCount, level0.HTexture, level0.VFramebuffer, level0.Width, level0.Height); // Add bloom back to scene texture: - mBuffers->BindSceneTextureFB(); + mBuffers->BindCurrentFB(); glViewport(mOutputViewport.left, mOutputViewport.top, mOutputViewport.width, mOutputViewport.height); glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); @@ -242,13 +249,91 @@ void FGLRenderer::TonemapScene() glDisable(GL_BLEND); glDisable(GL_SCISSOR_TEST); - mBuffers->BindHudFB(); - mBuffers->BindSceneTexture(0); + mBuffers->BindNextFB(); + mBuffers->BindCurrentTexture(0); mTonemapShader->Bind(); mTonemapShader->SceneTexture.Set(0); mTonemapShader->Exposure.Set(mCameraExposure); mVBO->BindVBO(); mVBO->RenderScreenQuad(); + mBuffers->NextTexture(); + + if (blendEnabled) + glEnable(GL_BLEND); + if (scissorEnabled) + glEnable(GL_SCISSOR_TEST); + glBindTexture(GL_TEXTURE_2D, textureBinding); + if (gl.flags & RFL_SAMPLER_OBJECTS) + glBindSampler(0, samplerBinding); + glActiveTexture(activeTex); +} + +//----------------------------------------------------------------------------- +// +// Apply lens distortion and place the result in the HUD/2D texture +// +//----------------------------------------------------------------------------- + +void FGLRenderer::LensDistortScene() +{ + if (gl_lens == 0) + return; + + GLint activeTex, textureBinding, samplerBinding; + glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex); + glActiveTexture(GL_TEXTURE0); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding); + if (gl.flags & RFL_SAMPLER_OBJECTS) + { + glGetIntegerv(GL_SAMPLER_BINDING, &samplerBinding); + glBindSampler(0, 0); + } + + GLboolean blendEnabled, scissorEnabled; + glGetBooleanv(GL_BLEND, &blendEnabled); + glGetBooleanv(GL_SCISSOR_TEST, &scissorEnabled); + + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); + + float k[4] = + { + gl_lens_k, + gl_lens_k * gl_lens_chromatic, + gl_lens_k * gl_lens_chromatic * gl_lens_chromatic, + 0.0f + }; + float kcube[4] = + { + gl_lens_kcube, + gl_lens_kcube * gl_lens_chromatic, + gl_lens_kcube * gl_lens_chromatic * gl_lens_chromatic, + 0.0f + }; + + float aspect = mOutputViewport.width / mOutputViewport.height; + + // Scale factor to keep sampling within the input texture + float r2 = aspect * aspect * 0.25 + 0.25f; + float sqrt_r2 = sqrt(r2); + float f0 = 1.0f + MAX(r2 * (k[0] + kcube[0] * sqrt_r2), 0.0f); + float f2 = 1.0f + MAX(r2 * (k[2] + kcube[2] * sqrt_r2), 0.0f); + float f = MAX(f0, f2); + float scale = 1.0f / f; + + mBuffers->BindNextFB(); + mBuffers->BindCurrentTexture(0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + mLensShader->Bind(); + mLensShader->InputTexture.Set(0); + mLensShader->AspectRatio.Set(aspect); + mLensShader->Scale.Set(scale); + mLensShader->LensDistortionCoefficient.Set(k); + mLensShader->CubicDistortionValue.Set(kcube); + mVBO->BindVBO(); + mVBO->RenderScreenQuad(); + mBuffers->NextTexture(); if (blendEnabled) glEnable(GL_BLEND); @@ -356,7 +441,9 @@ void FGLRenderer::CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma) mPresentShader->Contrast.Set(clamp(vid_contrast, 0.1f, 3.f)); mPresentShader->Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f)); } - mBuffers->BindHudTexture(0); + mBuffers->BindCurrentTexture(0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); mVBO->BindVBO(); mVBO->RenderScreenQuad(mScreenViewport.width / (float)mBuffers->GetWidth(), mScreenViewport.height / (float)mBuffers->GetHeight()); diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index a65626007..490d73ed0 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -76,25 +76,26 @@ FGLRenderBuffers::FGLRenderBuffers() FGLRenderBuffers::~FGLRenderBuffers() { ClearScene(); - ClearHud(); + ClearPipeline(); ClearBloom(); } void FGLRenderBuffers::ClearScene() { DeleteFrameBuffer(mSceneFB); - DeleteFrameBuffer(mSceneTextureFB); DeleteRenderBuffer(mSceneMultisample); DeleteRenderBuffer(mSceneDepthStencil); DeleteRenderBuffer(mSceneDepth); DeleteRenderBuffer(mSceneStencil); - DeleteTexture(mSceneTexture); } -void FGLRenderBuffers::ClearHud() +void FGLRenderBuffers::ClearPipeline() { - DeleteFrameBuffer(mHudFB); - DeleteTexture(mHudTexture); + for (int i = 0; i < NumPipelineTextures; i++) + { + DeleteFrameBuffer(mPipelineFB[i]); + DeleteTexture(mPipelineTexture[i]); + } } void FGLRenderBuffers::ClearBloom() @@ -149,8 +150,8 @@ void FGLRenderBuffers::Setup(int width, int height) } else if (width > mWidth || height > mHeight) { + CreatePipeline(width, height); CreateScene(width, height, samples); - CreateHud(width, height); CreateBloom(width, height); mWidth = width; mHeight = height; @@ -173,9 +174,6 @@ void FGLRenderBuffers::CreateScene(int width, int height, int samples) { ClearScene(); - mSceneTexture = Create2DTexture(GetHdrFormat(), width, height); - mSceneTextureFB = CreateFrameBuffer(mSceneTexture); - if (samples > 1) mSceneMultisample = CreateRenderBuffer(GetHdrFormat(), samples, width, height); @@ -183,26 +181,30 @@ void FGLRenderBuffers::CreateScene(int width, int height, int samples) { mSceneDepth = CreateRenderBuffer(GL_DEPTH_COMPONENT24, samples, width, height); mSceneStencil = CreateRenderBuffer(GL_STENCIL_INDEX8, samples, width, height); - mSceneFB = CreateFrameBuffer(samples > 1 ? mSceneMultisample : mSceneTexture, mSceneDepth, mSceneStencil, samples > 1); + mSceneFB = CreateFrameBuffer(samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepth, mSceneStencil, samples > 1); } else { mSceneDepthStencil = CreateRenderBuffer(GL_DEPTH24_STENCIL8, samples, width, height); - mSceneFB = CreateFrameBuffer(samples > 1 ? mSceneMultisample : mSceneTexture, mSceneDepthStencil, samples > 1); + mSceneFB = CreateFrameBuffer(samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepthStencil, samples > 1); } } //========================================================================== // -// Creates the post-tonemapping-step buffers +// Creates the buffers needed for post processing steps // //========================================================================== -void FGLRenderBuffers::CreateHud(int width, int height) +void FGLRenderBuffers::CreatePipeline(int width, int height) { - ClearHud(); - mHudTexture = Create2DTexture(GetHdrFormat(), width, height); - mHudFB = CreateFrameBuffer(mHudTexture); + ClearPipeline(); + + for (int i = 0; i < NumPipelineTextures; i++) + { + mPipelineTexture[i] = Create2DTexture(GetHdrFormat(), width, height); + mPipelineFB[i] = CreateFrameBuffer(mPipelineTexture[i]); + } } //========================================================================== @@ -392,11 +394,13 @@ void FGLRenderBuffers::CheckFrameBufferCompleteness() void FGLRenderBuffers::BlitSceneToTexture() { + mCurrentPipelineTexture = 0; + if (mSamples <= 1) return; glBindFramebuffer(GL_READ_FRAMEBUFFER, mSceneFB); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mSceneTextureFB); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture]); glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); @@ -415,27 +419,48 @@ void FGLRenderBuffers::BindSceneFB() //========================================================================== // -// Makes the scene texture frame buffer active (final 2D texture only) +// Binds the current scene/effect/hud texture to the specified texture unit // //========================================================================== -void FGLRenderBuffers::BindSceneTextureFB() +void FGLRenderBuffers::BindCurrentTexture(int index) { - glBindFramebuffer(GL_FRAMEBUFFER, mSceneTextureFB); + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, mPipelineFB[mCurrentPipelineTexture]); } //========================================================================== // -// Makes the 2D/HUD frame buffer active +// Makes the frame buffer for the current texture active // //========================================================================== -void FGLRenderBuffers::BindHudFB() +void FGLRenderBuffers::BindCurrentFB() { - if (gl_tonemap != 0) - glBindFramebuffer(GL_FRAMEBUFFER, mHudFB); - else - glBindFramebuffer(GL_FRAMEBUFFER, mSceneTextureFB); + glBindFramebuffer(GL_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture]); +} + +//========================================================================== +// +// Makes the frame buffer for the next texture active +// +//========================================================================== + +void FGLRenderBuffers::BindNextFB() +{ + int out = (mCurrentPipelineTexture + 1) % NumPipelineTextures; + glBindFramebuffer(GL_FRAMEBUFFER, mPipelineFB[out]); +} + +//========================================================================== +// +// Next pipeline texture now contains the output +// +//========================================================================== + +void FGLRenderBuffers::NextTexture() +{ + mCurrentPipelineTexture = (mCurrentPipelineTexture + 1) % NumPipelineTextures; } //========================================================================== @@ -449,33 +474,6 @@ void FGLRenderBuffers::BindOutputFB() glBindFramebuffer(GL_FRAMEBUFFER, mOutputFB); } -//========================================================================== -// -// Binds the scene frame buffer texture to the specified texture unit -// -//========================================================================== - -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); - 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 diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index 37bbdc2e5..8b3001c96 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -21,13 +21,16 @@ public: ~FGLRenderBuffers(); void Setup(int width, int height); - void BlitSceneToTexture(); + void BindSceneFB(); - void BindSceneTextureFB(); - void BindHudFB(); + void BlitSceneToTexture(); + + void BindCurrentTexture(int index); + void BindCurrentFB(); + void BindNextFB(); + void NextTexture(); + void BindOutputFB(); - void BindSceneTexture(int index); - void BindHudTexture(int index); enum { NumBloomLevels = 4 }; FGLBloomTextureLevel BloomLevels[NumBloomLevels]; @@ -39,10 +42,10 @@ public: private: void ClearScene(); - void ClearHud(); + void ClearPipeline(); void ClearBloom(); void CreateScene(int width, int height, int samples); - void CreateHud(int width, int height); + void CreatePipeline(int width, int height); void CreateBloom(int width, int height); GLuint Create2DTexture(GLuint format, int width, int height); GLuint CreateRenderBuffer(GLuint format, int width, int height); @@ -62,15 +65,21 @@ private: int mHeight = 0; int mSamples = 0; - GLuint mSceneTexture = 0; + static const int NumPipelineTextures = 2; + int mCurrentPipelineTexture = 0; + + // Buffers for the scene GLuint mSceneMultisample = 0; GLuint mSceneDepthStencil = 0; GLuint mSceneDepth = 0; GLuint mSceneStencil = 0; GLuint mSceneFB = 0; - GLuint mSceneTextureFB = 0; - GLuint mHudTexture = 0; - GLuint mHudFB = 0; + + // Effect/HUD buffers + GLuint mPipelineTexture[NumPipelineTextures]; + GLuint mPipelineFB[NumPipelineTextures]; + + // Back buffer frame buffer GLuint mOutputFB = 0; }; diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index d2a0cb959..9105c08c7 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -65,6 +65,7 @@ #include "gl/shaders/gl_bloomshader.h" #include "gl/shaders/gl_blurshader.h" #include "gl/shaders/gl_tonemapshader.h" +#include "gl/shaders/gl_lensshader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/textures/gl_texture.h" #include "gl/textures/gl_translate.h" @@ -117,6 +118,7 @@ void FGLRenderer::Initialize() mBloomCombineShader = new FBloomCombineShader(); mBlurShader = new FBlurShader(); mTonemapShader = new FTonemapShader(); + mLensShader = new FLensShader(); mPresentShader = new FPresentShader(); // Only needed for the core profile, because someone decided it was a good idea to remove the default VAO. @@ -171,6 +173,7 @@ FGLRenderer::~FGLRenderer() if (mBloomCombineShader) delete mBloomCombineShader; if (mBlurShader) delete mBlurShader; if (mTonemapShader) delete mTonemapShader; + if (mLensShader) delete mLensShader; } //========================================================================== @@ -248,7 +251,7 @@ void FGLRenderer::Begin2D() if (mDrawingScene2D) mBuffers->BindSceneFB(); else - mBuffers->BindHudFB(); + mBuffers->BindCurrentFB(); } glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 78eac4cbf..9be66bbfb 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -23,6 +23,7 @@ class FBloomExtractShader; class FBloomCombineShader; class FBlurShader; class FTonemapShader; +class FLensShader; class FPresentShader; inline float DEG2RAD(float deg) @@ -90,6 +91,7 @@ public: FBloomCombineShader *mBloomCombineShader; FBlurShader *mBlurShader; FTonemapShader *mTonemapShader; + FLensShader *mLensShader; FPresentShader *mPresentShader; FTexture *gllight; @@ -160,6 +162,7 @@ public: void EndDrawScene(sector_t * viewsector); void BloomScene(); void TonemapScene(); + void LensDistortScene(); void CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma); void Flush() { CopyToBackbuffer(nullptr, true); } diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 87197d09d..16bb6cd34 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -864,6 +864,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo if (FGLRenderBuffers::IsEnabled()) mBuffers->BlitSceneToTexture(); BloomScene(); TonemapScene(); + LensDistortScene(); } mDrawingScene2D = false; eye->TearDown(); diff --git a/src/gl/shaders/gl_lensshader.cpp b/src/gl/shaders/gl_lensshader.cpp new file mode 100644 index 000000000..39f5042e7 --- /dev/null +++ b/src/gl/shaders/gl_lensshader.cpp @@ -0,0 +1,68 @@ +/* +** gl_lensshader.cpp +** Lens distortion with chromatic aberration 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_lensshader.h" + +void FLensShader::Bind() +{ + if (!mShader) + { + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/lensdistortion.vp", "", 330); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/lensdistortion.fp", "", 330); + mShader.SetFragDataLocation(0, "FragColor"); + mShader.Link("shaders/glsl/lensdistortion"); + mShader.SetAttribLocation(0, "PositionInProjection"); + InputTexture.Init(mShader, "InputTexture"); + AspectRatio.Init(mShader, "Aspect"); + Scale.Init(mShader, "Scale"); + LensDistortionCoefficient.Init(mShader, "k"); + CubicDistortionValue.Init(mShader, "kcube"); + } + mShader.Bind(); +} diff --git a/src/gl/shaders/gl_lensshader.h b/src/gl/shaders/gl_lensshader.h new file mode 100644 index 000000000..ef0810e4e --- /dev/null +++ b/src/gl/shaders/gl_lensshader.h @@ -0,0 +1,21 @@ +#ifndef __GL_LENSSHADER_H +#define __GL_LENSSHADER_H + +#include "gl_shaderprogram.h" + +class FLensShader +{ +public: + void Bind(); + + FBufferedUniform1i InputTexture; + FBufferedUniform1f AspectRatio; + FBufferedUniform1f Scale; + FBufferedUniform4f LensDistortionCoefficient; + FBufferedUniform4f CubicDistortionValue; + +private: + FShaderProgram mShader; +}; + +#endif \ No newline at end of file diff --git a/src/gl/system/gl_cvars.h b/src/gl/system/gl_cvars.h index 30f4cc4f3..4b28bb667 100644 --- a/src/gl/system/gl_cvars.h +++ b/src/gl/system/gl_cvars.h @@ -49,5 +49,9 @@ EXTERN_CVAR(Float, gl_bloom_amount) EXTERN_CVAR(Int, gl_bloom_kernel_size) EXTERN_CVAR(Int, gl_tonemap) EXTERN_CVAR(Float, gl_exposure) +EXTERN_CVAR(Bool, gl_lens) +EXTERN_CVAR(Float, gl_lens_k) +EXTERN_CVAR(Float, gl_lens_kcube) +EXTERN_CVAR(Float, gl_lens_chromatic) #endif // _GL_INTERN_H diff --git a/src/gl/system/gl_wipe.cpp b/src/gl/system/gl_wipe.cpp index 781194ca4..0d1973131 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::IsEnabled()) { - GLRenderer->mBuffers->BindHudFB(); + GLRenderer->mBuffers->BindCurrentFB(); 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::IsEnabled()) - GLRenderer->mBuffers->BindHudFB(); + GLRenderer->mBuffers->BindCurrentFB(); 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::IsEnabled()) { - GLRenderer->mBuffers->BindHudFB(); + GLRenderer->mBuffers->BindCurrentFB(); const auto &bounds = GLRenderer->mScreenViewport; glViewport(bounds.left, bounds.top, bounds.width, bounds.height); } diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 21aab53a6..8050079e8 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2621,6 +2621,7 @@ GLPREFMNU_VRQUADSTEREO = "Enable Quad Stereo"; GLPREFMNU_MULTISAMPLE = "Multisample"; GLPREFMNU_TONEMAP = "Tonemap Mode"; GLPREFMNU_BLOOM = "Bloom effect"; +GLPREFMNU_LENS = "Lens distortion effect"; // Option Values OPTVAL_SMART = "Smart"; diff --git a/wadsrc/static/menudef.z b/wadsrc/static/menudef.z index bcee4ca01..bd458f376 100644 --- a/wadsrc/static/menudef.z +++ b/wadsrc/static/menudef.z @@ -220,4 +220,5 @@ OptionMenu "GLPrefOptions" Option "$GLPREFMNU_MULTISAMPLE", gl_multisample, "Multisample" Option "$GLPREFMNU_TONEMAP", gl_tonemap, "TonemapModes" Option "$GLPREFMNU_BLOOM", gl_bloom, "OnOff" + Option "$GLPREFMNU_LENS", gl_lens, "OnOff" } diff --git a/wadsrc/static/shaders/glsl/lensdistortion.fp b/wadsrc/static/shaders/glsl/lensdistortion.fp new file mode 100644 index 000000000..7facf5a80 --- /dev/null +++ b/wadsrc/static/shaders/glsl/lensdistortion.fp @@ -0,0 +1,58 @@ +/* + Original Lens Distortion Algorithm from SSontech + http://www.ssontech.com/content/lensalg.htm + + If (u,v) are the coordinates of a feature in the undistorted perfect + image plane, then (u', v') are the coordinates of the feature on the + distorted image plate, ie the scanned or captured image from the + camera. The distortion occurs radially away from the image center, + with correction for the image aspect ratio (image_aspect = physical + image width/height), as follows: + + r2 = image_aspect*image_aspect*u*u + v*v + f = 1 + r2*(k + kcube*sqrt(r2)) + u' = f*u + v' = f*v + + The constant k is the distortion coefficient that appears on the lens + panel and through Sizzle. It is generally a small positive or negative + number under 1%. The constant kcube is the cubic distortion value found + on the image preprocessor's lens panel: it can be used to undistort or + redistort images, but it does not affect or get computed by the solver. + When no cubic distortion is needed, neither is the square root, saving + time. + + Chromatic Aberration example, + using red distord channel with green and blue undistord channel: + + k = vec3(-0.15, 0.0, 0.0); + kcube = vec3(0.15, 0.0, 0.0); +*/ + +in vec2 TexCoord; +out vec4 FragColor; + +uniform sampler2D InputTexture; +uniform float Aspect; // image width/height +uniform float Scale; // 1/max(f) +uniform vec4 k; // lens distortion coefficient +uniform vec4 kcube; // cubic distortion value + +void main() +{ + vec2 position = (TexCoord - vec2(0.5)); + + vec2 p = vec2(position.x * Aspect, position.y); + float r2 = dot(p, p); + vec3 f = vec3(1.0) + r2 * (k.rgb + kcube.rgb * sqrt(r2)); + + vec3 x = f * position.x * Scale + 0.5; + vec3 y = f * position.y * Scale + 0.5; + + vec3 c; + c.r = texture(InputTexture, vec2(x.r, y.r)).r; + c.g = texture(InputTexture, vec2(x.g, y.g)).g; + c.b = texture(InputTexture, vec2(x.b, y.b)).b; + + FragColor = vec4(c, 1.0); +} diff --git a/wadsrc/static/shaders/glsl/lensdistortion.vp b/wadsrc/static/shaders/glsl/lensdistortion.vp new file mode 100644 index 000000000..5990669a5 --- /dev/null +++ b/wadsrc/static/shaders/glsl/lensdistortion.vp @@ -0,0 +1,9 @@ + +in vec4 PositionInProjection; +out vec2 TexCoord; + +void main() +{ + gl_Position = PositionInProjection; + TexCoord = PositionInProjection.xy * 0.5 + 0.5; +}