From 6b9529d70f52879ffb885fcad05789eb83cce162 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 2 Aug 2016 17:32:21 +0200 Subject: [PATCH] Added lens distortion shader --- src/CMakeLists.txt | 1 + src/gl/renderer/gl_postprocess.cpp | 73 ++++++++++++++++++++ src/gl/renderer/gl_renderbuffers.cpp | 4 +- src/gl/renderer/gl_renderer.cpp | 3 + src/gl/renderer/gl_renderer.h | 3 + src/gl/scene/gl_scene.cpp | 1 + src/gl/shaders/gl_lensshader.cpp | 66 ++++++++++++++++++ src/gl/shaders/gl_lensshader.h | 19 +++++ src/gl/system/gl_cvars.h | 4 ++ wadsrc/static/shaders/glsl/lensdistortion.fp | 55 +++++++++++++++ wadsrc/static/shaders/glsl/lensdistortion.vp | 9 +++ 11 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 src/gl/shaders/gl_lensshader.cpp create mode 100644 src/gl/shaders/gl_lensshader.h create mode 100644 wadsrc/static/shaders/glsl/lensdistortion.fp create mode 100644 wadsrc/static/shaders/glsl/lensdistortion.vp 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..373f7c635 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.15f, 0) +CVAR(Float, gl_lens_kcube, 0.15f, 0) +CVAR(Float, gl_lens_chromatic, 1.2f, 0) + EXTERN_CVAR(Float, vid_brightness) EXTERN_CVAR(Float, vid_contrast) @@ -260,6 +267,70 @@ void FGLRenderer::TonemapScene() 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 + }; + + mBuffers->BindHudFB(); + mBuffers->BindSceneTexture(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->LensDistortionCoefficient.Set(k); + mLensShader->CubicDistortionValue.Set(kcube); + mVBO->BindVBO(); + mVBO->RenderScreenQuad(); + + 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); +} + //----------------------------------------------------------------------------- // // Gamma correct while copying to frame buffer @@ -357,6 +428,8 @@ void FGLRenderer::CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma) mPresentShader->Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f)); } mBuffers->BindHudTexture(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..d9a8823cd 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -432,7 +432,7 @@ void FGLRenderBuffers::BindSceneTextureFB() void FGLRenderBuffers::BindHudFB() { - if (gl_tonemap != 0) + if (gl_tonemap != 0 || gl_lens) glBindFramebuffer(GL_FRAMEBUFFER, mHudFB); else glBindFramebuffer(GL_FRAMEBUFFER, mSceneTextureFB); @@ -470,7 +470,7 @@ void FGLRenderBuffers::BindSceneTexture(int index) void FGLRenderBuffers::BindHudTexture(int index) { glActiveTexture(GL_TEXTURE0 + index); - if (gl_tonemap != 0) + if (gl_tonemap != 0 || gl_lens) glBindTexture(GL_TEXTURE_2D, mHudTexture); else glBindTexture(GL_TEXTURE_2D, mSceneTexture); diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index d2a0cb959..5fa52ec6b 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; } //========================================================================== 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..9181d260d --- /dev/null +++ b/src/gl/shaders/gl_lensshader.cpp @@ -0,0 +1,66 @@ +/* +** 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"); + 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..a83532161 --- /dev/null +++ b/src/gl/shaders/gl_lensshader.h @@ -0,0 +1,19 @@ +#ifndef __GL_LENSSHADER_H +#define __GL_LENSSHADER_H + +#include "gl_shaderprogram.h" + +class FLensShader +{ +public: + void Bind(); + + FBufferedUniform1i InputTexture; + 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/wadsrc/static/shaders/glsl/lensdistortion.fp b/wadsrc/static/shaders/glsl/lensdistortion.fp new file mode 100644 index 000000000..9d6fd1a26 --- /dev/null +++ b/wadsrc/static/shaders/glsl/lensdistortion.fp @@ -0,0 +1,55 @@ +/* + 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 vec4 k; // lens distortion coefficient +uniform vec4 kcube; // cubic distortion value + +void main() +{ + vec2 position = TexCoord - vec2(0.5); + + float r2 = dot(position, position); + vec3 f = vec3(1.0) + r2 * (k.rgb + kcube.rgb * sqrt(r2)); + + vec3 x = f * position.x + 0.5; + vec3 y = f * position.y + 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; +}